style(13-03): apply Prettier formatting across repo

Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
Lucas Berger
2026-06-11 20:35:18 -04:00
parent 4bc0445173
commit 982438dc10
398 changed files with 19050 additions and 16382 deletions
@@ -3,7 +3,7 @@ phase: 01-foundation-broker-spike
plan: 04
type: execute
wave: 3
depends_on: ["01-02", "01-03"]
depends_on: ['01-02', '01-03']
files_modified:
- apps/api/src/routes/sse.ts
- apps/api/src/index.ts
@@ -17,35 +17,35 @@ requirements: [CAL-08, CAL-01, AUTH-01, AUTH-02]
must_haves:
truths:
- "The full app is wired: broker poller starts on boot, /api/events + /api/me + /api/sse are mounted behind OIDC, /health public"
- "The landing page shows the logged-in member (name + color) AND one real cached Fastmail event as broker proof"
- 'The full app is wired: broker poller starts on boot, /api/events + /api/me + /api/sse are mounted behind OIDC, /health public'
- 'The landing page shows the logged-in member (name + color) AND one real cached Fastmail event as broker proof'
- "A CAL-08 spike confirms Lucas's app password reads BOTH the shared family calendar and his personal calendar; the go/no-go decision is recorded in a committed doc"
- "GET /api/sse/heartbeat streams events over the real Pangolin tunnel for 5+ minutes without the proxy closing the stream (smoke test result recorded)"
- "Both members can authenticate through Authelia over the public URL and land on the shell (verified live)"
- 'GET /api/sse/heartbeat streams events over the real Pangolin tunnel for 5+ minutes without the proxy closing the stream (smoke test result recorded)'
- 'Both members can authenticate through Authelia over the public URL and land on the shell (verified live)'
artifacts:
- path: "apps/api/src/routes/sse.ts"
provides: "GET /api/sse/heartbeat (streamSSE) — Pangolin pass-through smoke test"
exports: ["sseRouter"]
- path: "apps/api/src/broker/spike.ts"
provides: "CAL-08 spike script: createFastmailClient → fetchCalendars → print URLs"
- path: ".planning/phases/01-foundation-broker-spike/CAL-08-DECISION.md"
provides: "Documented go/no-go for personal-calendar overlay"
contains: "Decision:"
- path: "apps/pwa/src/components/EventProof.tsx"
provides: "Renders one cached event from /api/events"
- path: 'apps/api/src/routes/sse.ts'
provides: 'GET /api/sse/heartbeat (streamSSE) — Pangolin pass-through smoke test'
exports: ['sseRouter']
- path: 'apps/api/src/broker/spike.ts'
provides: 'CAL-08 spike script: createFastmailClient → fetchCalendars → print URLs'
- path: '.planning/phases/01-foundation-broker-spike/CAL-08-DECISION.md'
provides: 'Documented go/no-go for personal-calendar overlay'
contains: 'Decision:'
- path: 'apps/pwa/src/components/EventProof.tsx'
provides: 'Renders one cached event from /api/events'
key_links:
- from: "apps/api/src/index.ts"
to: "apps/api/src/broker/poller.ts"
via: "startBrokerPoller on boot"
- from: 'apps/api/src/index.ts'
to: 'apps/api/src/broker/poller.ts'
via: 'startBrokerPoller on boot'
pattern: "startBrokerPoller\\(\\)"
- from: "apps/api/src/index.ts"
to: "apps/api/src/routes/events.ts"
via: "app.route /api/events"
pattern: "/api/events"
- from: "apps/pwa/src/components/EventProof.tsx"
to: "/api/events"
via: "React Query fetch"
pattern: "api/events"
- from: 'apps/api/src/index.ts'
to: 'apps/api/src/routes/events.ts'
via: 'app.route /api/events'
pattern: '/api/events'
- from: 'apps/pwa/src/components/EventProof.tsx'
to: '/api/events'
via: 'React Query fetch'
pattern: 'api/events'
---
<objective>
@@ -75,6 +75,7 @@ Output: fully wired app, landing page with member + event proof, CAL-08 decision
</context>
<artifacts_produced>
## Artifacts this phase produces (Plan 04)
New files: `apps/api/src/routes/sse.ts`, `apps/api/src/broker/spike.ts`, `apps/pwa/src/components/EventProof.tsx`, `.planning/phases/01-foundation-broker-spike/CAL-08-DECISION.md`.
@@ -103,6 +104,7 @@ Modified: `apps/api/src/index.ts` (mount events/sse routers, startBrokerPoller o
Update `src/index.ts` to the final bootstrap (RESEARCH "Hono app bootstrap"): order = `/callback` (processOAuthCallback) → `/health` (public, Plan 01) → `app.use('/api/*', oidcAuthMiddleware())``app.route('/api/me', meRouter)``app.route('/api/events', eventsRouter)``app.route('/api/sse', sseRouter)``startBrokerPoller()` → serveStatic(./public) → `serve({ port: 3000 })`. Confirm /health stays before the /api guard.
PWA: add `fetchEvents()` to `src/api/client.ts` (GET /api/events, credentials include). Create `src/components/EventProof.tsx`: React Query `['events']`, render the first event's title/date (parse from the returned shape) or an empty-state "No cached events yet". Update `App.tsx` to render member (name + color, from Plan 02) AND `<EventProof />` together — the single broker-proof landing screen (Claude's Discretion landing page).
</action>
<verify>
<automated>cd apps/api && pnpm exec tsc --noEmit && grep -q "startBrokerPoller()" src/index.ts && grep -q "'/api/events'" src/index.ts && grep -q "'/api/sse'" src/index.ts && grep -q "streamSSE" src/routes/sse.ts</automated>
@@ -149,24 +151,26 @@ Modified: `apps/api/src/index.ts` (mount events/sse routers, startBrokerPoller o
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Public internet → Pangolin → Hono | Untrusted; only authenticated /api/* requests proceed; /health + /callback are the only pre-auth routes |
| SSE stream (browser ↔ /api/sse) | Long-lived; must remain behind OIDC, must not leak data beyond heartbeat |
| Spike credential handling | Lucas's app password used once for enumeration; stored encrypted, never logged/committed |
| Boundary | Description |
| --------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Public internet → Pangolin → Hono | Untrusted; only authenticated /api/\* requests proceed; /health + /callback are the only pre-auth routes |
| SSE stream (browser ↔ /api/sse) | Long-lived; must remain behind OIDC, must not leak data beyond heartbeat |
| Spike credential handling | Lucas's app password used once for enumeration; stored encrypted, never logged/committed |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-04-01 | Elevation of Privilege | /api/sse/heartbeat | mitigate | Mounted under /api/* behind oidcAuthMiddleware; no unauthenticated SSE access (ASVS V4) |
| T-04-02 | Information Disclosure | SSE payload | accept | Heartbeat carries only `{ ts, id }` — no user data or secrets |
| T-04-03 | Spoofing | Same-site session cookie behind Pangolin | mitigate | FamilySync + Authelia share parent domain (Pitfall 1/17); OIDC_AUTH_EXTERNAL_URL set so redirect_uri matches |
| T-04-04 | Information Disclosure | Fastmail app password during spike | mitigate | Passed via env for one-off enumeration or stored encrypted via Plan 03 crypto; never echoed to logs or committed; spike output prints only calendar URLs/displayNames, never the password |
| T-04-05 | Tampering | client_secret plain vs hashed | mitigate | Plain secret only in OIDC_CLIENT_SECRET env (Pitfall 7); Authelia YAML holds the pbkdf2-sha512 hash; .env never committed |
| T-04-SC | Tampering | tsx (dev runner for spike) | accept | tsx is a widely-used TypeScript runner; spike script is dev-only, not shipped in the Docker image |
| Threat ID | Category | Component | Disposition | Mitigation Plan |
| --------- | ---------------------- | ---------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| T-04-01 | Elevation of Privilege | /api/sse/heartbeat | mitigate | Mounted under /api/\* behind oidcAuthMiddleware; no unauthenticated SSE access (ASVS V4) |
| T-04-02 | Information Disclosure | SSE payload | accept | Heartbeat carries only `{ ts, id }` — no user data or secrets |
| T-04-03 | Spoofing | Same-site session cookie behind Pangolin | mitigate | FamilySync + Authelia share parent domain (Pitfall 1/17); OIDC_AUTH_EXTERNAL_URL set so redirect_uri matches |
| T-04-04 | Information Disclosure | Fastmail app password during spike | mitigate | Passed via env for one-off enumeration or stored encrypted via Plan 03 crypto; never echoed to logs or committed; spike output prints only calendar URLs/displayNames, never the password |
| T-04-05 | Tampering | client_secret plain vs hashed | mitigate | Plain secret only in OIDC_CLIENT_SECRET env (Pitfall 7); Authelia YAML holds the pbkdf2-sha512 hash; .env never committed |
| T-04-SC | Tampering | tsx (dev runner for spike) | accept | tsx is a widely-used TypeScript runner; spike script is dev-only, not shipped in the Docker image |
</threat_model>
<verification>
@@ -179,13 +183,14 @@ Modified: `apps/api/src/index.ts` (mount events/sse routers, startBrokerPoller o
</verification>
<success_criteria>
- SC1 (AUTH-01): both members authenticate via Authelia over the public URL, land on home, no Fastmail prompt
- SC2 (AUTH-02): sessions persist across browser restart
- SC3 (AUTH-03): each member has a stable distinct color
- SC4 (CAL-01): broker fetched + cached ≥1 real event; shown on the landing page
- SC5 (CAL-08): go/no-go decision documented in CAL-08-DECISION.md
- Bonus (D-08): Pangolin SSE pass-through smoke result recorded for Phase 4
</success_criteria>
</success_criteria>
<output>
Create `.planning/phases/01-foundation-broker-spike/01-04-SUMMARY.md` when done. Include the SSE smoke-test result and a pointer to CAL-08-DECISION.md.