Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
11 KiB
phase, plan, subsystem, tags, status, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | status | dependency_graph | tech_stack | key_files | decisions | metrics | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 19-local-auth-no-oidc-mode | 05 | auth |
|
checkpoint |
|
|
|
|
|
Phase 19 Plan 05: Dev-Bypass Rework + Harness + CI Summary
One-liner: Option C devSessionCookieMiddleware issues real local-session cookie under DEV_AUTH_BYPASS, break-glass reset-admin CLI, login.spec.ts real-form e2e, global-setup seeds local_credentials, and CI harness job gets LOCAL_SESSION_SECRET.
Status: CHECKPOINT REACHED
Task 4 is a type="checkpoint:human-verify" (gate="blocking"). Tasks 1-3 are complete and committed. The plan pauses for human confirmation that the full Playwright harness + CI run are green and that no dev artifact ships in the published image (D-15 boundary).
Tasks Completed
| Task | Name | Commit | Key Files |
|---|---|---|---|
| 1 | Option C — devSessionCookieMiddleware | 3094df8 |
devBypass.ts, index.ts |
| 2 | Break-glass reset-admin CLI | 8239187 |
apps/api/scripts/reset-admin.ts |
| 3 | global-setup seed + login.spec.ts + CI env | 1f94dc5 |
global-setup.ts, login.spec.ts, ci.yml + 7 test mocks |
Task 4: Checkpoint (Pending Human Verification)
Checkpoint type: human-verify (blocking)
What was verified locally
API tests: 446/446 tests pass (all 34 test files, including devBypass.test.ts: 3/3).
Typecheck: pnpm --filter @familysync/api typecheck and pnpm --filter @familysync/pwa typecheck both exit 0.
reset-admin --dry-run: Exit 0; no password value ("ignored") in output.
D-15 boundary verified:
.dockerignoreexcludesapps/api/scripts/(reset-admin.ts never ships) — confirmed in file..dockerignoreexcludesapps/pwa/e2e/(global-setup seed never ships) — confirmed in file.devSessionCookieMiddleware()production hard-guard is FIRST check (line 105 of devBypass.ts).reset-admin.tsNODE_ENV=production throw is FIRST executable statement (line 26).LOCAL_SESSION_SECRETin ci.yml is a documented dev-only value, never in the published image.
E2E login.spec.ts: Cannot run locally yet — LoginPage.tsx is being produced by the concurrent plan 04 executor in the same wave. The spec is structurally correct (matches UI-SPEC selectors id="login-username", role="heading" name="Sign in", etc.) and will run as part of the full harness after wave 4 merges.
What the human needs to verify
- Push and run CI: Push the branch → confirm the Gitea CI
harnessjob is green. The harness job now includesLOCAL_SESSION_SECRETand thelocal_credentialsseed step. The full Playwright suite (iphone + pixel + desktop) should pass includinglogin.spec.tson the desktop profile. - D-15 image boundary: Confirm the
publish.ymlimage-hygiene assertion still passes (noapps/api/scripts/orapps/pwa/e2e/artifacts in the published image). Spot-check.dockerignorecovers both dirs. - Confirm login.spec.ts passes: After wave 4 merges (plan 04 completes LoginPage.tsx), confirm
pnpm --filter @familysync/pwa test:e2e --grep "login"exits 0 on the desktop profile.
Resume signal: Type "approved" if the full harness + CI are green and no dev artifact ships.
What Was Built
Task 1: devSessionCookieMiddleware (Option C)
apps/api/src/auth/devBypass.ts — new export devSessionCookieMiddleware(): MiddlewareHandler:
- Production hard-guard FIRST check:
NODE_ENV === 'production'→ no-op (T-19-24, D-15) - No-op when
DEV_AUTH_BYPASS !== 'true' - No-op when
LOCAL_SESSION_SECRETnot set (degrades gracefully) - When active: if no
local-sessioncookie present, callsissueLocalSessionCookie(c, DEV_USER.id) - Imports:
getCookiefrom hono/cookie,issueLocalSessionCookiefrom localSession.ts
apps/api/src/index.ts — mounts devSessionCookieMiddleware() immediately after devAuthBypass() on /api/*.
Task 2: reset-admin.ts (Break-Glass CLI)
apps/api/scripts/reset-admin.ts — standalone break-glass CLI (149 lines):
- NODE_ENV=production throw as FIRST executable statement (D-13/D-15)
.dockerignore apps/api/scripts/excludes it from the prod image (IMG-02)- Inline scrypt PHC
hashPassword()(Pitfall 11 — cannot import compiled TS from plain script) - Parses
--username/--password/--dry-runfrom process.argv - Upserts
usersrow (is_admin=true, claimed=true) then upsertslocal_credentialsrow - Never logs the password value (T-19-26)
--dry-run: validates args + DB connection without writing; exit 0
Task 3: global-setup seed + login.spec.ts + CI harness env
apps/pwa/e2e/global-setup.ts:
- Added
hashPasswordInline()inline scrypt PHC (Pitfall 11 — plain Node.js) - Added
TRUNCATE TABLE local_credentialsto the TRUNCATE block - Added
INSERT INTO local_credentials (user_id, username, password_hash) VALUES (1, 'devuser', ?) ON DUPLICATE KEY UPDATE ...after member_credentials seed
apps/pwa/e2e/login.spec.ts (new, 98 lines):
- Scoped to desktop/Chromium only (other profiles use bypass cookie)
- Uses
context.clearCookies()before each test to strip the bypass-issued cookie - Test 1: unauthenticated navigation → /login; brand + "Sign in" heading + form visible
- Test 2: wrong password →
role="status"shows "Incorrect username or password." - Test 3: devuser/devpass → navigates away from /login
.gitea/workflows/ci.yml harness job:
- Added new "Seed local_credentials for dev user (id=1)" step (CJS inline script with hashPassword)
- Added
LOCAL_SESSION_SECRET: 'dev-secret-change-me-0000000000000000'to harness env - LOCAL_SESSION_SECRET is a dev-only value, never in the published image (IMG gates)
Test mock fixes (Rule 1 — Bug): Added devSessionCookieMiddleware: () => async (_c, next) => next() to all 7 vi.mock('../../src/auth/devBypass.js', ...) blocks that used an explicit factory return object (admin, setup, push, lists, localAuth, authMode, requireAdmin tests). events.test.ts uses importOriginal + spread and already picks up the new export automatically.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] vitest mock missing devSessionCookieMiddleware export
- Found during: Task 3 — running the full API test suite after Task 1's devBypass.ts change
- Issue: 7 test files mock
devBypass.jswith an explicit factory object. After addingdevSessionCookieMiddlewareto devBypass.ts, vitest reported "NodevSessionCookieMiddlewareexport is defined on the mock" for every mock that did not include it. - Fix: Added
devSessionCookieMiddleware: () => async (_c, next) => next()to all 7 explicit mock factories: admin.test.ts, setup.test.ts, push.test.ts (bothvi.mockandvi.doMock), lists.test.ts, localAuth.test.ts, authMode.test.ts, requireAdmin.test.ts. - Files modified: 7 test files
- Commit:
1f94dc5
D-15 Guarantee
| Artifact | Dev boundary | Enforcement |
|---|---|---|
devSessionCookieMiddleware |
NODE_ENV=production hard-guard (FIRST check) + IMG-01 boot guard | T-19-24 |
reset-admin.ts |
NODE_ENV=production throw (FIRST statement) + .dockerignore apps/api/scripts/ | T-19-25, IMG-02 |
local_credentials dev seed |
Lives in apps/pwa/e2e/global-setup.ts (.dockerignore apps/pwa/e2e/) + CI step only | T-19-23 |
LOCAL_SESSION_SECRET in CI |
Dev-only value in harness job env; never in Dockerfile or published image | IMG-01/02/03 |
Known Stubs
None. All new code performs real operations.
Threat Surface Scan
No new network endpoints introduced. New surface:
devSessionCookieMiddleware: internal middleware, no external exposure; guarded by NODE_ENV=production FIRST check (T-19-24).reset-admin.ts: CLI only (docker exec), guarded by NODE_ENV=production throw + .dockerignore exclusion (T-19-25).
All surfaces are within the plan's threat model (T-19-23 through T-19-26).
Self-Check: PASSED
All created files confirmed present on disk:
- FOUND: apps/api/scripts/reset-admin.ts
- FOUND: apps/pwa/e2e/login.spec.ts
All commits confirmed in git log:
3094df8: feat(19-05): Option C — devSessionCookieMiddleware issues real local-session cookie under bypass8239187: feat(19-05): add break-glass reset-admin CLI (dev-only, .dockerignore'd)1f94dc5: feat(19-05): global-setup local_credentials seed + login.spec.ts + CI harness env
API tests: 446/446 pass (all 34 test files); typecheck: exit 0.