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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user