Files
familysync/.planning/phases/12-initial-setup-wizard/12-01-SUMMARY.md
T

8.6 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
12-initial-setup-wizard 01 database, api, testing
drizzle
mariadb
migration
web-push
vapid
vitest
hono
phase provides
10-admin-role-settings app_config table, users.is_admin, member_credentials table — consumed by Phase 12 schema changes
users.claimed column (boolean, default false NOT NULL) — distinguishes unclaimed wizard rows from OIDC-bound rows
users.oidc_iss / users.oidc_sub now nullable — wizard creates local rows before OIDC identity is known
0002_lethal_millenium_guard.sql migration — applied to dev DB with backfill UPDATE
scripts/generate-secrets.mjs — generates SESSION_SECRET, APP_PASSWORD_ENCRYPTION_KEY, VAPID keypair to stdout
apps/api/src/lib/setupGuard.ts — isSetupLocked() stub (real impl in Plan 02)
apps/api/src/routes/setup.ts — setupRouter stub Hono router (handlers in Plan 02)
apps/api/tests/routes/setup.test.ts — Wave-0 RED scaffolds for SETUP-01..04 + 423 guard
apps/api/tests/auth/user.test.ts — D-08 first-login-claims RED scaffold
12-02-setup-routes (consumes setupGuard + setupRouter stubs, schema claimed column)
12-03-pwa-setup-page (consumes /api/setup/* routes)
12-04-integration (consumes full setup flow)
added patterns
drizzle-kit generate+migrate workflow for schema changes (NEVER drizzle-kit push — D-Task5-DDL)
CommonJS default-import pattern for ESM scripts consuming CJS packages (web-push)
it.todo() Wave-0 scaffold pattern — RED tests exist before happy path is built
isSetupLocked() per-call freshness contract (D-10 — never module-cache)
created modified
apps/api/src/db/migrations/0002_lethal_millenium_guard.sql
apps/api/src/db/migrations/meta/0002_snapshot.json
scripts/generate-secrets.mjs
apps/api/src/lib/setupGuard.ts
apps/api/src/routes/setup.ts
apps/api/tests/routes/setup.test.ts
apps/api/src/db/schema.ts
apps/api/src/db/migrations/meta/_journal.json
apps/api/tests/auth/user.test.ts
package.json
D-07-CJS-IMPORT: web-push is CJS — ESM scripts must use default import then destructure (import webpush from '...'; const { generateVAPIDKeys } = webpush)
D-07-BACKFILL: 0002 migration appends UPDATE users SET claimed=true WHERE oidc_iss IS NOT NULL to prevent first-login-claims (D-08) matching pre-existing OIDC users
D-07-NULL-UNIQUE: MariaDB treats multiple NULL+NULL pairs as DISTINCT in unique indexes — uniq_oidc_identity constraint kept unchanged; multiple unclaimed rows correctly allowed
Wave-0 scaffold: create it.todo() tests BEFORE implementing routes — ensures RED gate exists for SETUP-04 423 guard (Pitfall 8)
generate-secrets: stdout-only secret generation — SC-3 compliance checked via grep acceptance gate
SETUP-03
8min 2026-06-15

Phase 12 Plan 01: Foundation Summary

Schema migration making OIDC identity nullable + claimed marker applied to dev DB; stdout-only secret generator for VAPID keypair; Wave-0 stub router + RED test scaffolds for all four SETUP requirements

Performance

  • Duration: 8 min
  • Started: 2026-06-15T17:37:13Z
  • Completed: 2026-06-15T17:44:48Z
  • Tasks: 4
  • Files modified: 10

Accomplishments

  • Applied Drizzle migration 0002 to dev DB: oidcIss/oidcSub now nullable, claimed column added, existing OIDC users backfilled claimed=true
  • Created scripts/generate-secrets.mjs satisfying SETUP-03: prints SESSION_SECRET (64 hex), APP_PASSWORD_ENCRYPTION_KEY (64 hex), VAPID_PUBLIC_KEY (~87 b64url), VAPID_PRIVATE_KEY (~43 b64url) to stdout only — never to disk or DB
  • Created Wave-0 import targets: setupGuard.ts (isSetupLocked stub) and setup.ts (empty setupRouter) so Plan 02 imports compile from day one
  • Created 20 RED it.todo() scaffolds in setup.test.ts (SETUP-01..04 + 423 guard + D-10 effective-config) and user.test.ts (D-08 first-login-claims) — suite collects at 375 passed | 20 todo

Task Commits

Each task was committed atomically:

  1. Task 1: Schema nullable OIDC identity + claimed marker + 0002 migration - 703fad2 (feat)
  2. Task 2: generate-secrets repo helper - 2d6dc14 (feat)
  3. Task 3: Stub setupGuard.ts + setup.ts router - 11e8102 (feat)
  4. Task 4: Wave-0 test scaffolds - e098be3 (test)

Files Created/Modified

  • apps/api/src/db/schema.ts — users.oidcIss/oidcSub made nullable; claimed boolean added; Phase 12 app_config keys documented with prohibition comment (D-01/SC-3)
  • apps/api/src/db/migrations/0002_lethal_millenium_guard.sql — MODIFY COLUMN for nullable + ADD COLUMN claimed + backfill UPDATE
  • apps/api/src/db/migrations/meta/_journal.json — 0002 entry added
  • apps/api/src/db/migrations/meta/0002_snapshot.json — Drizzle snapshot for 0002
  • scripts/generate-secrets.mjs — Bootstrap secret generator (SETUP-03 / D-05)
  • package.json — root "generate-secrets" script added
  • apps/api/src/lib/setupGuard.ts — isSetupLocked() stub (returns false; real impl Plan 02)
  • apps/api/src/routes/setup.ts — setupRouter = new Hono() stub (empty; handlers Plan 02)
  • apps/api/tests/routes/setup.test.ts — 15 it.todo() Wave-0 RED scaffolds
  • apps/api/tests/auth/user.test.ts — 5 it.todo() D-08 first-login-claims scaffolds added

Decisions Made

  • D-07-CJS-IMPORT: web-push is a CommonJS module — ESM scripts must use import webpush from '...' then destructure. Named ESM export form fails at Node 22 (SyntaxError: Named export 'generateVAPIDKeys' not found). Fixed inline as Rule 1 bug.
  • D-07-BACKFILL: Appended UPDATE users SET claimed=true WHERE oidc_iss IS NOT NULL to the generated migration SQL so existing OIDC users are pre-marked claimed, preventing the Plan 02 first-login-claims query (D-08) from matching them.
  • D-07-NULL-UNIQUE: Kept uniq_oidc_identity unique constraint on (oidcIss, oidcSub) unchanged — MariaDB treats NULL+NULL pairs as DISTINCT in unique indexes (ISO SQL semantics), allowing multiple unclaimed wizard rows with NULL oidc_iss. No structural change needed (RESEARCH Pitfall 9 awareness).

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] web-push CommonJS ESM named-import failure

  • Found during: Task 2 (generate-secrets.mjs execution)
  • Issue: import { generateVAPIDKeys } from 'web-push/src/index.js' throws SyntaxError: Named export 'generateVAPIDKeys' not found — web-push is CommonJS and Node 22 ESM loader does not auto-export CJS named exports
  • Fix: Changed to import webpush from '.../web-push/src/index.js'; const { generateVAPIDKeys } = webpush;
  • Files modified: scripts/generate-secrets.mjs
  • Verification: node scripts/generate-secrets.mjs prints all four correctly-shaped values
  • Committed in: 2d6dc14 (Task 2 commit)

Total deviations: 1 auto-fixed (Rule 1 bug — CJS import form) Impact on plan: Essential for generate-secrets to run. No scope creep.

Issues Encountered

  • drizzle-kit migrate requires DB env vars — ran with set -a; source .env; set +a; DB_HOST=127.0.0.1 pnpm exec drizzle-kit migrate. The dev DB hostname in .env is mariadb (Docker internal); overriding to 127.0.0.1 is the standard host-side dev pattern.
  • pnpm test -- setup (filter by name) triggered globalSetup which needs root DB credentials; acceptance criterion verified instead via full suite run with DB_HOST=127.0.0.1 showing 375 passed | 20 todo with no import errors.

Threat Surface Scan

No new network endpoints introduced in this plan. The schema migration is additive (ALTER + ADD, no DROP/recreate). Threat mitigations T-12-01, T-12-02, T-12-03 all verified:

  • T-12-01: generate-secrets.mjs contains no writeFile/appendFile/fetch/db (grep-checked)
  • T-12-02: 0002 migration uses MODIFY COLUMN (not DROP/recreate); backfill verified
  • T-12-03: prohibition comment in schema.ts for vapid_private_key / app_password_encryption_key

Next Phase Readiness

  • Plan 02 (setup routes) can import isSetupLocked from setupGuard.ts and extend setupRouter in setup.ts — both exist as valid TypeScript import targets
  • Plan 02 can also rely on users.claimed and nullable oidcIss/oidcSub being present in the dev DB
  • 20 RED it.todo() tests are waiting for Plan 02 and Plan 03 implementations to turn them GREEN
  • SETUP-03 (generate-secrets) is fully satisfied by this plan

Phase: 12-initial-setup-wizard Completed: 2026-06-15