docs(01): capture phase context
This commit is contained in:
@@ -0,0 +1,108 @@
|
|||||||
|
# Phase 1: Foundation + Broker Spike - Context
|
||||||
|
|
||||||
|
**Gathered:** 2026-06-04
|
||||||
|
**Status:** Ready for planning
|
||||||
|
|
||||||
|
<domain>
|
||||||
|
## Phase Boundary
|
||||||
|
|
||||||
|
Stand up the FamilySync stack (Docker Compose + MariaDB, deployed through the real Pangolin tunnel) and deliver: Authelia OIDC login with persistent sessions, stable per-member identity, and an auto-assigned per-member color; a CalDAV broker that reads and caches at least one real event from a Fastmail calendar via a per-member app password; and a documented go/no-go on the personal-calendar overlay (CAL-08).
|
||||||
|
|
||||||
|
**In scope:** AUTH-01/02/03, CAL-01, CAL-08 (spike + decision), infra scaffold, Pangolin deployment, SSE pass-through smoke test.
|
||||||
|
**Out of scope (later phases):** calendar UI/views (Phase 2), event write-back (Phase 3), lists + live sync feature (Phase 4), push (Phase 5). No single-occurrence recurring edits ever in v1.
|
||||||
|
</domain>
|
||||||
|
|
||||||
|
<decisions>
|
||||||
|
## Implementation Decisions
|
||||||
|
|
||||||
|
### Personal-Calendar Access & Spike (CAL-08 gate)
|
||||||
|
- **D-01:** The wife's personal calendar lives on **Fastmail** (confirmed by user). It is a Fastmail-hosted calendar collection, so the broker can reach it via CalDAV. This removes the iCloud "unreachable" risk entirely.
|
||||||
|
- **D-02:** Broker access model = **per-member app passwords**, NOT single-token cross-account share+accept. Each member generates their own Fastmail app password; the broker holds N credentials and reads each account directly. This eliminates the unconfirmed cross-account CalDAV ACL discovery risk — the original highest project risk. The CAL-08 spike therefore simplifies to: confirm an app password reads its own account's shared + personal calendars via PROPFIND/REPORT.
|
||||||
|
- **D-03:** Phase 1 proceeds with **only the primary user's (Lucas) app password**. Success criterion #4 (read+cache a real event) is proven against Lucas's personal + the shared family calendar. The wife's app password is added in Phase 2 — Phase 1 is NOT blocked on coordinating with her. CAL-08 is structurally proven (N-credential broker) without her credential present.
|
||||||
|
- **D-04:** App passwords are stored in an **encrypted-at-rest DB table**, keyed by user `oidc_sub`, with the encryption key supplied from env. This supports adding the wife later via an onboarding/settings flow with no redeploy, and scales to the N-credential broker. App passwords are backend-only — never exposed to the frontend (locked, security).
|
||||||
|
- **D-05:** Go/no-go record: since access is per-member app passwords and her calendar is on Fastmail, the expected outcome is GO. The documented fallback (only relevant if a Fastmail app password unexpectedly cannot read a personal calendar) is shared-family-only for v1, moving CAL-08 to v1.x.
|
||||||
|
|
||||||
|
### Member Color Assignment (AUTH-03)
|
||||||
|
- **D-06:** Each member's color is **auto-assigned from a curated palette on first login** and persisted on the user row (keyed by `oidc_iss + oidc_sub`). Stable across sessions, no settings UI in Phase 1, works for both current members and any future member. Not user-pickable in v1 (a settings color-picker is a deferred idea).
|
||||||
|
|
||||||
|
### Infrastructure & Deployment Scope
|
||||||
|
- **D-07:** Phase 1 **deploys through the real Pangolin tunnel + Authelia**, not local-only. OIDC redirect URIs, HTTPS, and session cookies are validated in the real topology from day one to avoid a "worked locally, broke in prod" OIDC failure. Ensure FamilySync and Authelia share the same parent domain so Authelia session cookies are same-site (Pitfall 17).
|
||||||
|
- **D-08:** Phase 1 **folds in the Pangolin SSE pass-through smoke test** (a trivial long-lived SSE endpoint confirmed over the public URL). De-risks Phase 4 transport choice early (issue #1034). A failure here changes the Phase 4 real-time transport decision; it does NOT block Phase 1's auth/broker success criteria.
|
||||||
|
|
||||||
|
### Locked Upstream (carried forward — do NOT re-litigate)
|
||||||
|
- **D-09:** CalDAV-only via `tsdav`; broker auth = Fastmail **app password**, never JMAP/API token. Principal URL form `https://caldav.fastmail.com/dav/principals/user/{email}/` (Pitfall 1).
|
||||||
|
- **D-10:** Identity = `oidc_iss + oidc_sub` composite key, never email (AUTH-03).
|
||||||
|
- **D-11:** **Skip the Authelia `groups` claim** — two equal members; authentication not authorization (Pitfall 16).
|
||||||
|
- **D-12:** **Backend confidential client holds the refresh token**; no iframe silent renewal (Pitfall 17). Persistent session (AUTH-02) via backend-managed refresh, not browser iframe.
|
||||||
|
- **D-13:** Calendar cache: store raw VEVENT blob + `dtstart_utc`; all-day events as `DATE` / `{date, allDay}` struct, never coerced to DATETIME/UTC (Pitfall 3). Write-through cache invalidation; only cache server-returned objects (Pitfall 14). Use sync-token (WebDAV-Sync) with ctag-poll fallback from day one (Pitfall 4).
|
||||||
|
|
||||||
|
### Claude's Discretion
|
||||||
|
- **Phase 1 landing page:** a thin authenticated shell that ALSO displays the one cached event as broker proof (not a bare health page, not a real calendar UI). Confirms end-to-end auth + broker in one screen.
|
||||||
|
- **Color palette:** a small set of visually-distinct, accessible hues assigned round-robin by join order. Exact values are Claude's choice.
|
||||||
|
- **Broker internals:** sync-token vs ctag detection, poll interval (research suggests conservative 5-min / 60s acceptable for v1), Drizzle schema specifics, OIDC middleware wiring, encryption helper implementation.
|
||||||
|
- **Stack libraries/versions:** per locked research stack (Hono + Drizzle/mysql2 + tsdav + ical.js + rrule + @hono/oidc-auth).
|
||||||
|
</decisions>
|
||||||
|
|
||||||
|
<canonical_refs>
|
||||||
|
## Canonical References
|
||||||
|
|
||||||
|
**Downstream agents MUST read these before planning or implementing.**
|
||||||
|
|
||||||
|
### Phase definition & requirements
|
||||||
|
- `.planning/ROADMAP.md` § "Phase 1: Foundation + Broker Spike" — goal + 5 success criteria (the scope anchor)
|
||||||
|
- `.planning/REQUIREMENTS.md` — AUTH-01, AUTH-02, AUTH-03, CAL-01, CAL-08 (full requirement text + traceability)
|
||||||
|
- `.planning/PROJECT.md` — constraints, key decisions, household context
|
||||||
|
|
||||||
|
### Research (read before planning — flagged NEEDS research-phase by SUMMARY)
|
||||||
|
- `.planning/research/SUMMARY.md` — cross-cutting findings; Phase 1 section + confidence assessment
|
||||||
|
- `.planning/research/PITFALLS.md` — Phase-1-relevant pitfalls: #1 (CalDAV-only), #3 (all-day DATE), #4 (ETag/sync-token), #7 (personal-cal sharing), #14 (cache double-write), #16 (Authelia groups), #17 (Authelia silent renewal/cookies), #18 (Pangolin WS/SSE)
|
||||||
|
- `.planning/research/STACK.md` — locked library versions + import paths
|
||||||
|
- `.planning/research/ARCHITECTURE.md` — broker-cache pattern, component layout, MariaDB schema guidance
|
||||||
|
|
||||||
|
### External docs (authoritative)
|
||||||
|
- Fastmail CalDAV principal URL + app passwords (see SUMMARY/PITFALLS Sources)
|
||||||
|
- Authelia OIDC client config — PKCE S256, `client_secret_basic`, response_type `code`, grant types `authorization_code`+`refresh_token` (see CLAUDE.md "Authelia OIDC Integration")
|
||||||
|
- Pangolin WebSocket/SSE issue #1034 (referenced in PITFALLS #18)
|
||||||
|
|
||||||
|
</canonical_refs>
|
||||||
|
|
||||||
|
<code_context>
|
||||||
|
## Existing Code Insights
|
||||||
|
|
||||||
|
### Reusable Assets
|
||||||
|
- None — greenfield repo. Only `.planning/`, generated `CLAUDE.md`, and an empty `README` exist. No source tree yet.
|
||||||
|
|
||||||
|
### Established Patterns
|
||||||
|
- None established yet. Phase 1 sets the foundational patterns (Docker Compose layout, Drizzle schema/migrations, broker module boundary, OIDC session middleware) that later phases inherit.
|
||||||
|
|
||||||
|
### Integration Points
|
||||||
|
- Authelia (already deployed, both members have accounts) — register FamilySync as an OIDC confidential client; no Authelia deploy/provisioning.
|
||||||
|
- Pangolin/Newt tunnel (already running) — FamilySync gets a public hostname under the same parent domain as Authelia.
|
||||||
|
- MariaDB + Redis available in the Unraid stack; no PostgreSQL.
|
||||||
|
|
||||||
|
</code_context>
|
||||||
|
|
||||||
|
<specifics>
|
||||||
|
## Specific Ideas
|
||||||
|
|
||||||
|
- The broker module must be a hard boundary: all Fastmail I/O isolated in `broker/`; nothing else imports Fastmail credentials or tsdav directly (architecture note).
|
||||||
|
- "No per-member credential juggling" is a core-value phrase, but the user explicitly accepted per-member app passwords as the access model — the juggling avoided is calendar-credential *login* friction for members (they still log in only via Authelia SSO), not broker-side secrets.
|
||||||
|
|
||||||
|
</specifics>
|
||||||
|
|
||||||
|
<deferred>
|
||||||
|
## Deferred Ideas
|
||||||
|
|
||||||
|
- **User-pickable member color** (settings color-picker) — deferred; v1 auto-assigns. Could land in a later settings/profile phase.
|
||||||
|
- **Single-token broker via Fastmail share+accept** — not chosen; per-member app passwords selected instead. Kept on record only as an alternative if credential management ever becomes painful.
|
||||||
|
- **Wife's app password onboarding flow** — the encrypted-credential DB table is built in Phase 1 to support it, but the actual onboarding UX/endpoint is Phase 2 work.
|
||||||
|
|
||||||
|
### Reviewed Todos (not folded)
|
||||||
|
- "Kick off FamilySync with /gsd:new-project" — stale project-kickoff todo, already completed during initialization; not relevant to Phase 1 scope.
|
||||||
|
|
||||||
|
</deferred>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Phase: 1-Foundation + Broker Spike*
|
||||||
|
*Context gathered: 2026-06-04*
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# Phase 1: Foundation + Broker Spike - Discussion Log
|
||||||
|
|
||||||
|
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||||
|
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||||
|
|
||||||
|
**Date:** 2026-06-04
|
||||||
|
**Phase:** 1-Foundation + Broker Spike
|
||||||
|
**Areas discussed:** Personal-cal spike & fallback, Member color assignment, Infra validation scope
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Area Selection
|
||||||
|
|
||||||
|
| Option | Selected |
|
||||||
|
|--------|----------|
|
||||||
|
| Personal-cal spike & fallback | ✓ |
|
||||||
|
| Member color assignment | ✓ |
|
||||||
|
| Phase 1 landing scope | (skipped → Claude's discretion) |
|
||||||
|
| Infra validation scope | ✓ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Personal-cal spike & fallback
|
||||||
|
|
||||||
|
### Where the wife's personal calendar lives
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| Fastmail | Fastmail-hosted collection; broker can reach it | ✓ |
|
||||||
|
| iCloud / Apple Calendar | Broker cannot read; CAL-08 degrades | |
|
||||||
|
| Not sure yet | Confirm before spike | |
|
||||||
|
| Both / mixed | Define overlay target | |
|
||||||
|
|
||||||
|
**User's choice:** Fastmail
|
||||||
|
**Notes:** Removes the iCloud "unreachable" risk; CAL-08 is technically achievable.
|
||||||
|
|
||||||
|
### Broker access model
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| Single broker token + share | One token; share+accept per member | |
|
||||||
|
| Per-member app passwords | Each member's own app password; broker holds N creds | ✓ |
|
||||||
|
| Prove single-token, keep per-member ready | Hybrid | |
|
||||||
|
|
||||||
|
**User's choice:** Per-member app passwords
|
||||||
|
**Notes:** Eliminates the unconfirmed cross-account CalDAV ACL discovery risk (the project's original highest risk). Spike simplifies to confirming an app password reads its own account.
|
||||||
|
|
||||||
|
### Spike credential sequencing
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| Just mine now | Prove broker with Lucas's app password; wife's added Phase 2 | ✓ |
|
||||||
|
| Both before Phase 1 done | Get wife's password too; fully close CAL-08 now | |
|
||||||
|
|
||||||
|
**User's choice:** Just mine now
|
||||||
|
**Notes:** Phase 1 not blocked on coordinating with wife. CAL-08 structurally proven via N-credential broker.
|
||||||
|
|
||||||
|
### Credential storage
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| DB table, encrypted at rest | Keyed by oidc_sub, key from env; supports later onboarding | ✓ |
|
||||||
|
| Env vars / secrets file | Simple, but redeploy to add/rotate | |
|
||||||
|
|
||||||
|
**User's choice:** DB table, encrypted at rest
|
||||||
|
**Notes:** Supports adding wife later with no redeploy; backend-only, never exposed to frontend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Member color assignment
|
||||||
|
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| Auto-assign from palette | Assign on first login, persist on user row | ✓ |
|
||||||
|
| User-pickable in settings | Default + settings color-picker | |
|
||||||
|
| Hardcoded per account | Fixed color in config per oidc_sub | |
|
||||||
|
|
||||||
|
**User's choice:** Auto-assign from palette
|
||||||
|
**Notes:** No settings UI in Phase 1; stable across sessions; works for future members. Pickable color deferred.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Infra validation scope
|
||||||
|
|
||||||
|
### Deployment scope
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| Deploy through Pangolin | Real tunnel + Authelia; validate OIDC topology day one | ✓ |
|
||||||
|
| Local docker-compose only | Localhost; defer tunnel validation | |
|
||||||
|
|
||||||
|
**User's choice:** Deploy through Pangolin
|
||||||
|
**Notes:** Avoids "worked locally, broke in prod" OIDC failure; same-parent-domain cookies (Pitfall 17).
|
||||||
|
|
||||||
|
### Pangolin SSE smoke test
|
||||||
|
| Option | Description | Selected |
|
||||||
|
|--------|-------------|----------|
|
||||||
|
| Fold it in now | Trivial SSE endpoint over public URL; de-risk Phase 4 | ✓ |
|
||||||
|
| Defer to Phase 4 | Keep Phase 1 strict to its 5 criteria | |
|
||||||
|
|
||||||
|
**User's choice:** Fold it in now
|
||||||
|
**Notes:** Cheap while stack+tunnel are up; a failure changes Phase 4 transport but does not block Phase 1.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Claude's Discretion
|
||||||
|
|
||||||
|
- Phase 1 landing page: thin authenticated shell that also displays the one cached event as broker proof.
|
||||||
|
- Color palette: small set of visually-distinct, accessible hues, round-robin by join order.
|
||||||
|
- Broker internals (sync-token vs ctag, poll interval), Drizzle schema, OIDC middleware wiring, encryption helper.
|
||||||
|
- Stack libraries/versions per locked research stack.
|
||||||
|
|
||||||
|
## Deferred Ideas
|
||||||
|
|
||||||
|
- User-pickable member color (settings color-picker) — future settings/profile phase.
|
||||||
|
- Single-token broker via share+accept — not chosen; alternative on record only.
|
||||||
|
- Wife's app-password onboarding UX/endpoint — Phase 2 (the encrypted credential table is built in Phase 1 to support it).
|
||||||
Reference in New Issue
Block a user