Files
ThothII/.superpowers/sdd/container-task-4-report.md
T

4.1 KiB

Container Packaging Task 4 Report

Status

Implemented and verified runtime-configured frontend packaging.

Changes

  • Added the browser runtime contract window.__THOTHII_CONFIG__.backendBaseUrl.
  • Loaded /config.js before the Vite module entrypoint.
  • Made runtime configuration take precedence while preserving VITE_BACKEND_URL and the existing http://localhost:8787 client default for development and tests.
  • Added a multi-stage frontend image that builds with Node and serves static assets as unprivileged UID/GID 101:101 with nginx on port 8080.
  • Added startup-time BACKEND_BASE_URL substitution (default /api).
  • Added /api/ reverse proxying to core:8787, SPA fallback, no-cache runtime config, and SSE-safe proxy settings (proxy_buffering off, proxy_cache off, one-hour read timeout).

TDD evidence

  • RED: npx vitest run src/api/runtime-config.test.ts failed because ./runtime-config did not exist.
  • GREEN: targeted runtime config suite passed (3 tests after preserving the legacy client default).

Verification

  • cd frontend && npx vitest run --reporter=dot && npx tsc -b && npm run build — exit 0 (40 test files, 185 tests; TypeScript and Vite production build passed).
  • docker build -f docker/frontend.Dockerfile -t thothii-frontend:test . — success.
  • Image metadata reports USER 101:101.
  • Two-container isolated-network smoke:
    • /config.js returned window.__THOTHII_CONFIG__ = { backendBaseUrl: "/api" };
    • /api/health proxied to the core image and returned {"status":"ok"}.
    • an unknown nested route returned the SPA index.html.
    • active nginx config contained proxy_buffering off, proxy_cache off, and proxy_read_timeout 1h.
    • /config.js returned Cache-Control: no-store.
  • sh -n docker/frontend-entrypoint.sh and git diff --check — exit 0.

Secret-leakage inspection

  • .dockerignore excludes .env* (except examples), credentials/key formats, dependency trees, build outputs, backend data, and deployment data.
  • The runtime web root contained no .env*, .pem, .key, .p12, or .pfx files.
  • Image history contained build/package instructions only; no secret build arguments or credential values were introduced by this task.

Self-review / concerns

  • nginx resolves the core hostname at startup, matching the planned Compose service name; standalone runs therefore need a reachable network alias named core.
  • Existing frontend test warnings (React refs/act, MSW unmatched incidental requests, Vite chunk-size warnings) remain; they did not fail the requested gates and are unrelated to this task.
  • .superpowers/sdd/progress.md was already modified by the orchestrator and was intentionally excluded from this task's commit.

P1 review fixes

Follow-up commit work addressed both review findings:

  • Runtime configuration is now produced with jq -cn --arg, so BACKEND_BASE_URL is encoded by a real JSON serializer rather than interpolated into JavaScript by sed.
  • The image includes frontend-config-smoke, which strips only the fixed assignment wrapper, parses the remaining JSON with jq, requires exactly the backendBaseUrl key, and compares the decoded value to the environment input.
  • The hostile smoke passed with quotes, backslashes, a literal newline, ampersand, pipe, and "; globalThis.PWNED=true; // in the value. A breakout would leave non-JSON trailing input and fail parsing.
  • Added joinBackendPath, shared by API fetch and EventSource creation. It removes duplicate boundary slashes for relative and absolute bases while keeping empty and / bases rooted.

Follow-up verification:

  • RED: six join cases failed with joinBackendPath is not a function before implementation.
  • Targeted: runtime config, API client, and EventSource suites — 14 tests passed.
  • Full frontend gate — exit 0 (40 test files, 191 tests, TypeScript, Vite build).
  • Rebuilt thothii-frontend:test successfully.
  • Hostile config image smoke — frontend runtime config smoke: ok.
  • Rebuilt two-container smoke — default /api config, proxied /api/health, SPA fallback, and SSE-safe nginx directives all passed.