Files
homepage/.agents/PLAN.md
T
ducoterra a2f331e526
Build and Push Container / build-and-push (push) Successful in 11s
Add phased-execution planning structure
Introduce .agents/ (PLAN.md with locked architectural anchors,
phase roadmap under phases/todo/) and AGENTS.md rules for
agents working in the repo. Queues the pending phases: fix
history XSS, simulate fetch commands, and nginx security
headers.
2026-09-18 17:23:41 -04:00

6.1 KiB

PLAN — Developer Homepage

Status: live static site · Last reviewed: 2026-09-18 (security & code-quality audit)

1. Project Identity

A single-page personal portfolio (Reese Wells — self-hosting & infrastructure) with an interactive fake terminal (easter-egg commands, vim simulator, achievement system). No backend, no user accounts, no PII beyond public contact details and GPG public keys.

2. Assumptions & Design Principles

  1. Static first. The site is plain HTML/CSS/JS. No runtime dependencies, no CDNs, no build-time JS frameworks — the supply chain is the repo itself.
  2. Boring deployment. build.sh → hashed dist/ → nginx:alpine on :8080 (plain HTTP) → TLS terminated by the external Caddy reverse proxy.
  3. Test the artifact. E2E tests run against the built dist/, not src/, so what is tested is exactly what is deployed.
  4. Security headers are a container property. All hardening headers are emitted by nginx (the only thing that serves the site in production).
  5. Fun is safe. The fake terminal's easter eggs (page-wipe, login screen, vim) are DOM-only pranks — no real network or system effects. The only real network behavior (curl/wget fetch) is being removed — see Phase 02.

3. Architectural Anchors (LOCKED DECISIONS)

COMPONENT DECISION RATIONALE STATUS
Frontend runtime Vanilla HTML/CSS/JS, flat src/*.js files, defer load order No toolchain, auditable, works with build.sh hashing LOCKED (pre-existing)
Terminal modules terminal-commands.js (canned data) · terminal-achievements.js · terminal-vim.js · terminal.js (core) Flat modules keep build.sh's root-level hashing working; single source of truth for command data LOCKED (2026-09-18 refactor)
Build build.sh: md5 content-hash cache-busting + sed reference rewrites → dist/ md5 is a cache key, not a security hash — acceptable LOCKED (pre-existing)
Serving nginx:alpine, listen 8080, plain HTTP TLS is added by the Caddy edge in front of the container LOCKED (pre-existing)
Edge Caddy reverse proxy (Route53 DNS-validated TLS) in front of the container HSTS/TLS are edge responsibilities LOCKED (assumed — verify per Phase 03)
E2E tests @playwright/test pinned 1.62.0 (matches cached chromium-1234), serves dist/ via python3 -m http.server 8123, config playwright.config.js Tests the deployed artifact; pinned to avoid browser re-downloads LOCKED (2026-09-18, user-approved)
Deployment Docker image → gitea.reeseapps.com/services/homepage via Gitea Actions (secrets-based registry login) Self-hosted CI LOCKED (pre-existing)
Persistence localStorage only (achievements, key reese-terminal-achievements) No backend by design LOCKED (pre-existing)

4. High-Level Architecture

src/ (index.html, style.css, script.js, terminal*.js, assets)
  │  build.sh (hash + rewrite)
  ▼
dist/ (index.html + hashed assets)          ◄── playwright webServer (port 8123)
  │  Dockerfile (COPY → nginx html root)    ◄── tests/e2e/*.spec.js (44 tests)
  ▼
nginx:alpine :8080 (HTTP, security headers, cache policy)
  ▲
Caddy edge (TLS, HSTS) ← reeseapps.com
  • src/terminal.js — terminal core: prompt loop, history, tab completion, dispatch, expand/collapse (expandTerminal/collapseTerminal, shared with script.js hero click).
  • src/terminal-commands.js — TERMINAL_COMMANDS (canned outputs; values may be functions evaluated at dispatch), TERMINAL_COMMAND_LIST (tab completion), getCommandOutput().
  • src/terminal-vim.js — launchVimSimulator({content, terminal, mobileInput, filename, setVimMode}).
  • src/terminal-achievements.js — ACHIEVEMENTS, localStorage persistence, toasts, hidden-section reveal, initTerminalAchievements().
  • src/script.js — nav menu, IntersectionObserver fades, server-rack background generation, hero-click terminal toggle.

5. Validation / Verification Workflow

  1. ./build.sh — rebuild dist/.
  2. npm test (Playwright) — 44 E2E tests against dist/ on port 8123. Every phase's final pass must leave the suite green.
  3. .agents/validate.sh (installed by the phased-execution skill) — must run 1 + 2 and, for Phase 03, the header checks below.
  4. Container header check (Phase 03): scripts/check-headers.sh — builds the image, runs it on a scratch port, asserts the four security headers on /, /index.html, and a hashed asset.

6. Data Model

No server-side data. Client-side state:

Key Where Shape
reese-terminal-achievements localStorage JSON array of achievement ids (e.g. ["time_flies","nice_try"])

Session-only state (no persistence): terminal history, isRoot, vim state, menu open/closed, terminal grown/collapsed.

7. Roadmap

Phase Title Source
01_fix_history_xss Fix terminal history-recall XSS (M-1) audit finding M-1
02_simulate_fetch_commands Remove unvalidated client fetch from curl/wget (M-2) audit finding M-2
03_nginx_security_headers Security headers on every location (M-4) + server_tokens off (L-2) audit findings M-4, L-2

Dismissed: audit finding M-3 (README 0.0.0.0:8080 binding) — false positive per project owner (2026-09-18); the host firewall already restricts access and the site is fronted by the Caddy edge.

Completed in-session (not phased): code-quality pass 2026-09-18 — module split (Q-1), dead-code removal (Q-2/3/4, scrollOffset), expand/collapse helpers (Q-5), rack-decoration CSS classes (Q-7), hidden-attribute reveal (Q-8), a11y label on terminal input (Q-9), dynamic date (Q-10), vim dd line-deletion fix, Playwright regression suite (44 tests). See .agents/remediation_plan.md.

Remaining low-priority audit items (not yet phased): L-1 (verify HSTS at the Caddy edge — folded into Phase 03 verification), L-3 (drop style-src 'unsafe-inline' after moving inline style="" attrs to classes), L-5 (label the displayed SSH key as fictitious or show fingerprint only), L-7 (pin CI action SHAs / image signing), L-8 (build.sh verification test in CI).