Files
familysync/.planning/milestones/v1.1-phases/12-initial-setup-wizard/12-06-SUMMARY.md
T
2026-06-18 22:21:38 -04:00

4.9 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics
phase plan subsystem tags requires provides affects tech-stack key-files decisions metrics
12-initial-setup-wizard 06 setup-wizard-backend
setup
vapid
security
uat-gap-closure
app_config.vapid_public_key (written by POST /api/setup/config)
process.env.VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY (Docker env)
process.env.DB_NAME (Docker env)
POST /api/setup/validate/vapid rejects a submitted public key that does not match the env VAPID_PUBLIC_KEY
GET /api/setup/status returns { setupComplete, dbName } with the non-secret DB name
SetupStatusResponse.dbName typed field for the PWA (Plan 05) read-only referent
apps/pwa setup wizard (Plan 05 consumes dbName + the now-strict VAPID row)
added patterns
validate/vapid equality check uses the same app_config select idiom as validate/oidc
non-secret env surfacing: only DB_NAME exposed, never DB_HOST/DB_USER/DB_PASSWORD
created modified
apps/api/src/routes/setup.ts
apps/api/tests/routes/setup.test.ts
apps/pwa/src/api/client.ts
D-12-06-VAPID-EQ: validate/vapid compares the operator-submitted PUBLIC key (app_config.vapid_public_key) to process.env.VAPID_PUBLIC_KEY; the private key is never compared or echoed (T-12-06 preserved).
D-12-06-DBNAME: only process.env.DB_NAME (?? null) is surfaced in GET /status; DB_HOST/DB_USER/DB_PASSWORD are never added to any response (grep-verified).
duration_minutes completed
8 2026-06-16

Phase 12 Plan 06: Setup-Route Gap Closure (VAPID equality + DB name) Summary

Closed UAT gaps 2 and 3 on the backend setup-route surface: POST /api/setup/validate/vapid now rejects a wrong/typoed wizard-entered VAPID public key by asserting it equals the env VAPID_PUBLIC_KEY, and GET /api/setup/status now returns the non-secret dbName so the DB-connection row has an on-screen referent.

What Was Built

Task 1 — validate/vapid asserts submitted key matches env public key (gap 2, TDD)

Before the structural webpush.setVapidDetails() check, the handler now reads app_config.vapid_public_key (the operator-submitted key) and returns 400 unless it exactly equals process.env.VAPID_PUBLIC_KEY. Previously a clearly-invalid key like BH123 still went green because only the env pair was validated — push would silently break in production (SETUP-02). The equality compares PUBLIC keys only; VAPID_PRIVATE_KEY remains read solely from process.env and is never compared or returned (T-12-06).

  • RED commit e9d07b3: mismatch → 400 (no private-key leak), absent row → 400, happy path seeds matching row.
  • GREEN commit e46e80a: equality assertion implemented.

Task 2 — Expose non-secret DB name via GET /api/setup/status (gap 3 backend)

GET /api/setup/status now returns { setupComplete, dbName } where dbName = process.env.DB_NAME ?? null (the var read by apps/api/src/db/client.ts). Only the database NAME is surfaced — never DB_HOST/DB_USER/DB_PASSWORD. SetupStatusResponse in the PWA client gained dbName?: string | null so Plan 05 can render a typed read-only field.

  • Commit fbd3b77.

Verification

  • cd apps/api && set -a; source ../../.env; set +a; DB_HOST=127.0.0.1 pnpm test -- setup407 passed (29 files).
  • grep -nE "VAPID_PRIVATE_KEY" apps/api/src/routes/setup.ts → only the env-only structural-check lines + doc comments; never compared against app_config or returned.
  • grep -nE "DB_PASSWORD|DB_HOST|DB_USER" apps/api/src/routes/setup.ts | grep -i "status\|c.json"no matches (no secret/topology in status response).
  • cd apps/pwa && pnpm typecheck → clean (tsc + e2e tsconfig).

Deviations from Plan

None — plan executed exactly as written. The pre-existing "invalid/truncated VAPID key" test (env keys invalid, no app_config row) still asserts 400/ok:false and stays GREEN; with the new equality check it now 400s on the absent-row branch rather than the structural branch, which is the intended stricter behavior.

TDD Gate Compliance

Task 1 followed RED→GREEN: failing test commit e9d07b3 (test(12-06): ...) precedes implementation commit e46e80a (feat(12-06): ...). No REFACTOR step needed. Task 2 is a non-behavioral env-surfacing change with an accompanying assertion added in the same commit.

Threat Surface

Threat ID Disposition Outcome
T-12-06 (VAPID_PRIVATE_KEY disclosure) mitigate Preserved — private key env-only; equality uses PUBLIC keys; test asserts no private key in mismatch body.
T-12-3DB (DB secret/topology disclosure) mitigate Only DB_NAME surfaced; grep confirms no DB_HOST/DB_USER/DB_PASSWORD in status response.
T-12-04 (setup-route replay) mitigate isSetupLocked() remains the first await in every handler (unchanged).

No new security-relevant surface introduced beyond the planned threat_model.

Known Stubs

None.

Self-Check: PASSED