--- phase: 03-event-write-back-pwa-install plan: 08 type: execute wave: 5 depends_on: ["03-04", "03-06", "03-07"] files_modified: - .planning/phases/03-event-write-back-pwa-install/03-GATE2-RESULTS.md autonomous: false requirements: [CAL-04, CAL-05, CAL-06, PWA-01, PWA-02] user_setup: - service: authelia why: "Gate 2 verifies real Authelia OIDC login over the public Pangolin URL including the iOS standalone-PWA flow (success criterion 6, D-14)" env_vars: - name: OIDC_AUTH_EXTERNAL_URL source: "Set to the public familysync URL (e.g. https://familysync.) so redirect_uri is correct behind Pangolin" dashboard_config: - task: "Register FamilySync as an OIDC confidential client (code flow + PKCE S256, client_secret_basic) and ensure the redirect_uri matches the public URL" location: "Authelia configuration" - task: "Expose familysync through Pangolin/Newt (Mode A local test rig is sufficient — Unraid prod optional until go-live, D-15)" location: "Pangolin / Newt connector" must_haves: truths: - "A real member completes Authelia OIDC login over the public Pangolin URL in a desktop browser; the session persists across a browser restart" - "On iPhone, the member installs FamilySync to the Home Screen and completes login WITHOUT the redirect breaking out of standalone mode" - "Each member shows a distinct, stable color (AUTH-03) in the live deploy" - "Create, edit, and delete an event end-to-end through the live deploy; the change appears in the native Fastmail app on the next sync (CAL-04/05/06)" - "The installed PWA opens full-screen with no browser chrome on iOS and Android (PWA-01/02)" artifacts: - path: ".planning/phases/03-event-write-back-pwa-install/03-GATE2-RESULTS.md" provides: "Recorded Gate 2 verification results (pass/fail per checklist row)" key_links: - from: "live deploy" to: "docs/deployment.md Gate 2 checklist" via: "operator executes each row" pattern: "Gate 2" --- Execute and record the Phase 1 Gate 2 live-verification carried into Phase 3 (success criterion 6, D-14/D-15): real Authelia OIDC login over the public Pangolin URL — most critically the iOS standalone-PWA login flow — plus session persistence, distinct stable member colors, and an end-to-end create/edit/delete through the live stack. This is the first real external auth test and the load-bearing check for the non-technical member. Purpose: all prior plans build behind the dev-auth bypass (D-13). Nothing has proven the OIDC redirect survives iOS standalone mode or that writes round-trip to Fastmail in a real deploy. This plan closes that gap and records the outcome. Output: 03-GATE2-RESULTS.md with a pass/fail line per Gate 2 checklist row. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @docs/deployment.md @.planning/phases/03-event-write-back-pwa-install/03-RESEARCH.md Task 1: Build and deploy FamilySync to the Mode A local test rig behind Pangolin .planning/phases/03-event-write-back-pwa-install/03-GATE2-RESULTS.md - docs/deployment.md (§Mode A local test rig setup; §Pangolin idle/read timeout requirements; §Gate 2 checklist at line ~215) - .planning/PROJECT.md (D-14 dev-auth bypass context; D-15 Mode A local Newt rig) - apps/api/src/index.ts (OIDC guard mounts only when devBypassActive is false — production build must NOT set DEV_AUTH_BYPASS) Per docs/deployment.md Mode A: produce a production build (NODE_ENV=production so the OIDC guard is mounted, dev-bypass OFF), serve the PWA static build + API, and expose it through the local Newt connector / Pangolin test subdomain. Confirm `OIDC_AUTH_EXTERNAL_URL` is set to the public URL and the Authelia client redirect_uri matches. Confirm the public `/health` responds over the tunnel. Create `03-GATE2-RESULTS.md` and record the deploy details (URL, build SHA, date) as the header before the checklist. If any infra step requires operator-only credentials/config, stop and surface it via the checkpoint in Task 2 rather than guessing. cd /home/luc/Projects/familysync && pnpm --filter @familysync/pwa build && pnpm --filter @familysync/api build && test -f .planning/phases/03-event-write-back-pwa-install/03-GATE2-RESULTS.md - Both apps build clean for production. - 03-GATE2-RESULTS.md exists with the deploy header (URL, build, date). - Public `/health` reachable through the tunnel (record the curl result in the file). A production build is live on the Mode A rig behind Pangolin; results file scaffolded. Task 2: [Gate 2] Live Authelia OIDC + iOS standalone login + distinct colors - docs/deployment.md (§Gate 2 checklist, esp. row 4 — iOS PWA Add-to-Home-Screen + standalone login) - .planning/phases/03-event-write-back-pwa-install/03-RESEARCH.md (§Pitfall 2 — iOS standalone OIDC redirect; symptom = stuck in Safari after login; fix = scope:'/' + /callback not SW-intercepted) Execute the docs/deployment.md Gate 2 checklist on the live public URL: real Authelia OIDC desktop login, session persistence across a browser restart, distinct stable per-member colors, and the load-bearing iOS Add-to-Home-Screen + standalone login (redirect must not break out of standalone). Record each row PASS/FAIL in 03-GATE2-RESULTS.md; on iOS failure apply the Pitfall-2 remedy and retest. The full Phase 3 stack (write-back + outbox worker + PWA install) is deployed to the Mode A rig behind the public Pangolin URL with real Authelia OIDC (dev-bypass OFF). The service worker denylist (Plan 07) keeps `/callback` server-handled. Work through docs/deployment.md §Gate 2 checklist on the live public URL and record each row in 03-GATE2-RESULTS.md: 1. Desktop browser: open the public URL, complete Authelia OIDC login, land on the app — no Fastmail credentials prompted. 2. Restart the browser, revisit — still logged in (session persists, AUTH-02). 3. Confirm each of the two members shows a distinct, stable color (AUTH-03). 4. iPhone: open in Safari, follow the in-app iOS install walkthrough, Add to Home Screen, launch standalone. Complete login — confirm the redirect does NOT break out of standalone (you stay in the app, not dropped to Safari). This is the load-bearing check (Pitfall 2). 5. Confirm the installed PWA opens full-screen with no browser chrome on iOS and Android (PWA-01). - Each Gate 2 checklist row recorded PASS in 03-GATE2-RESULTS.md, especially the iOS standalone login row. - If iOS standalone login FAILS: record the symptom, apply the Pitfall-2 remedy (verify manifest scope:'/' + start_url:'/', confirm /callback is in the SW denylist and reaches the server), redeploy, retest. Type "gate2 pass" with the iOS standalone result, or describe the failure (e.g. "stuck in Safari after login"). Task 3: [Gate 2] End-to-end create / edit / delete round-trips to Fastmail - .planning/phases/03-event-write-back-pwa-install/03-UI-SPEC.md (§Interaction Contract — sync-state feedback, delete interaction) - .planning/phases/03-event-write-back-pwa-install/03-CONTEXT.md (success criteria 1-3: create/edit/delete appear in native Fastmail on next sync) On the live deploy, perform create (timed, all-day, weekly recurring), edit, and delete through the UI and confirm each round-trips to the native Fastmail app on the next sync; observe the SyncStateToast Syncing→Saved flow and (if reproducible) a 412 conflict re-fetch. Record each result in 03-GATE2-RESULTS.md. Create/edit/delete UI (EventForm, popover footer, delete dialog), the outbox worker, and the polled SyncStateToast are all live on the deploy. On the live deploy (logged in as a real member): 1. Create a timed event and an all-day event (and one weekly recurring event). Confirm the "Syncing…" toast → "Saved", and that each event appears in the native Fastmail app within the next sync cycle (CAL-04/CAL-07). 2. Edit an existing event's title and time; confirm the change persists in Fastmail (CAL-05). 3. Delete an event via the two-tap confirmation; confirm it disappears from all views on the next sync (CAL-06). 4. (Optional, if reproducible) Trigger a 412 conflict by editing the same event from the Fastmail app first; confirm the conflict toast appears and the calendar re-fetches (D-08). - Create (timed + all-day + recurring), edit, and delete each recorded PASS in 03-GATE2-RESULTS.md with confirmation they round-tripped to the native Fastmail app. - Sync toast behavior (Syncing → Saved; persistent error on failure) observed and recorded. Type "writeback pass" with the Fastmail round-trip results, or describe any write that did not appear. ## Trust Boundaries | Boundary | Description | |----------|-------------| | public internet → Pangolin → app | First real external exposure of the auth + write path | ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-03-23 | Spoofing | dev-auth bypass accidentally active in the live deploy | mitigate | Production build sets NODE_ENV=production → bypass forced OFF, OIDC unconditionally mounted (index.ts); verify the login is real Authelia, not the dev user | | T-03-24 | Information Disclosure | OIDC redirect_uri mismatch leaking codes or failing login | mitigate | OIDC_AUTH_EXTERNAL_URL set to the public URL; Authelia client redirect_uri matches (deployment.md) | | T-03-25 | Tampering | SW intercepting /callback in the live build | mitigate | Plan 07 denylist verified against the production build; Gate 2 row 4 confirms standalone login end-to-end | - Both apps build for production; public /health reachable through the tunnel. - 03-GATE2-RESULTS.md records PASS for: desktop OIDC login, session persistence, distinct colors, iOS standalone login, full-screen install, and create/edit/delete Fastmail round-trips. - Success criterion 6 satisfied: live Authelia OIDC over Pangolin works including iOS standalone-PWA login; sessions persist; distinct stable colors. - Success criteria 1-5 confirmed live: create/edit/delete round-trip to Fastmail; installable + full-screen on iOS and Android. Create `.planning/phases/03-event-write-back-pwa-install/03-08-SUMMARY.md` when done.