Files
familysync/.planning/PROJECT.md
T

102 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FamilySync
## What This Is
FamilySync is a self-hosted, Dockerized family organization hub for a two-person household, modeled loosely on Skylight. It gives the family one unified, color-coded calendar and shared collaborative lists (groceries, gift ideas), reachable from a single low-friction React PWA — across a mixed Android/Fastmail and Apple ecosystem — built as one custom app on top of the family's existing paid Fastmail account. Email stays with members' existing mail clients and is out of scope.
## Core Value
The household can see and co-edit one color-coded family calendar (shared + each member's personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store, no per-member calendar credential juggling.
## Requirements
### Validated
<!-- Shipped and confirmed valuable. -->
(None yet — ship to validate)
### Active
<!-- v1 scope. Hypotheses until shipped and validated. -->
- [ ] Unified, color-coded calendar view aggregating all Fastmail-hosted calendars (shared family + each member's personal)
- [ ] Create / edit / delete events written back to the correct Fastmail calendar via the app's single broker token
- [ ] Shared collaborative lists (groceries, gift ideas) that both members co-edit, stored in MariaDB
- [ ] Live list sync so co-edits appear without manual refresh (Redis optional)
- [ ] Authelia OIDC login for every member (true SSO)
- [ ] React PWA installable on iPhone and Android via "Add to Home Screen" (no App Store)
- [ ] Web Push notifications for event reminders and list changes
- [ ] Low-friction onboarding for the non-technical Apple member — visit one URL, sign in
### Out of Scope
<!-- Explicit boundaries with reasoning to prevent re-adding. -->
- Wall-display / kiosk dashboard — deferred to v2; calendar + lists are the v1 core, display is the deferrable piece
- Email features — members keep existing mail clients; never the product's job
- Self-hosted calendar server (Baikal) — Fastmail hosts all calendars; one fewer service. Reversible if self-hosted calendar is ever wanted
- Vikunja / external task backend — lists live in the custom MariaDB; cross-ecosystem native task sync is impossible anyway
- DAVx5 — not needed
- React Native / App Store publishing — PWA delivers app-like UX without publishing overhead
- PostgreSQL — not in the existing stack; MariaDB is the database
- Forking existing apps — the value is the aggregation/display layer, not the backends
## Context
- **Household:** Primary user (project owner) on Fastmail + Android, uses the Fastmail app natively. Other member (wife) on Apple (iPhone/Mac). Wife adoption is a hard UX constraint.
- **One app to build:** backend + MariaDB + React PWA behind Authelia, sitting on top of Fastmail. This is far lighter than the prior Baikal + Vikunja + PWA design that was evaluated and dropped.
- **Calendars are all on Fastmail.** Personal calendars are also Fastmail-hosted calendar collections, so the app reads and writes everything (shared + personal) through a single Fastmail JMAP/CalDAV broker token — no external ICS feeds, no per-member credential management. The primary user gets the shared calendar natively in the Fastmail app; Apple members use the PWA (optionally subscribe in native Apple Calendar via CalDAV).
- **Prior exploration:** Architecture was revised across two sessions (explore → Opus verification). See `.planning/notes/familysync-architecture.md` for full reasoning behind the dropped options.
- **Open questions (for phase research):**
- Fastmail API — JMAP vs CalDAV for the app's calendar read/write. Which is cleaner to build against?
- PWA Web Push — sufficient/reliable enough for family alerts on iOS, or is a fallback needed?
- Mechanics of surfacing each member's *personal* Fastmail calendar to the broker token (calendar sharing/ACLs within Fastmail).
## Constraints
- **Tech stack**: MariaDB for the database — PostgreSQL is not available in the stack
- **Tech stack**: Redis available (optional, for live list sync / push)
- **Infrastructure**: Unraid host running Docker + Docker Compose
- **Auth**: Authelia (already deployed) — OIDC/OAuth2 for the custom app; all members authenticate through it
- **Calendar backend**: Fastmail (paid, existing) is the single source for all calendars via JMAP/CalDAV
- **Frontend**: React PWA only — no React Native, no App Store
- **Networking**: Split-DNS internal domain, private IPs internally; public exposure via Pangolin/Newt tunnel, no open ports
- **UX**: Must be slick and low-friction for a non-technical Apple member — hard constraint, drives design tradeoffs
- **Scope**: No email features; no forking existing apps
## Key Decisions
| Decision | Rationale | Outcome |
|----------|-----------|---------|
| Calendar hosted on Fastmail, not self-hosted (Baikal dropped) | Fastmail reaches the whole household native-or-PWA with one fewer self-hosted service; Fastmail Android app can't show self-hosted CalDAV anyway | — Pending |
| Personal calendars also Fastmail-hosted collections, aggregated via one broker token | Avoids external ICS feeds and per-member credential management; uniform read/write path | — Pending |
| Shared lists in custom MariaDB, not Vikunja | Cross-ecosystem native task sync is impossible; a list table in the backend being built anyway is trivial vs another container + SSO integration | — Pending |
| React PWA only, no React Native / App Store | App-like UX with one codebase for all surfaces; lowest onboarding friction (one URL) | — Pending |
| Authelia OIDC for all members | True SSO consistent with existing infra; no separate calendar-credential problem | — Pending |
| Web Push notifications in v1 (not deferred) | Family alerts judged essential day one; build push into the foundation | — Pending |
| Personal-calendar overlay in v1 (not shared-only) | Unified view of everyone's schedules is the Skylight magic worth shipping early | — Validated (CAL-08 GO, Phase 1): per-member app password reaches all of an account's calendars; no cross-account ACL needed |
| **D-14:** Defer Phase 1 Gate 2 (live Authelia/Pangolin verification). SSE-over-Pangolin smoke = hard gate before Phase 4; live AUTH smoke incl. iOS standalone-PWA folded into Phase 3; full 2-member prod login verified there. Phases 23 develop behind a documented dev-auth bypass. | Gate 2 needs operator infra (Authelia config + tunnel) + docs that didn't exist; deferring unblocks Phase 2/3 code without rework risk, since the broker data path (CAL-01/CAL-08) is already proven live. SSE must still be verified before Phase 4 to avoid building live-sync on an unverified transport (#1034). | Tracked: `01-HUMAN-UAT.md`, `docs/deployment.md` |
| **D-15:** Validate the real external topology via a **local Newt connector + test subdomain** through existing Pangolin (Mode A), not an Unraid deploy. Unraid (Mode B) reserved for go-live. | Authelia OIDC + SSE pass-through behaviour live in Authelia + Pangolin/Newt, not in where the origin runs — so a local Newt rig faithfully tests both, decoupling "does the topology work" from "is it in production." Newt dials outbound (no open ports). Only shared touch is an additive, reversible Authelia client. | — Pending (Gate 2) |
| **D-16 (2026-06-05, Phase 2):** No dedicated Fastmail "broker" account. The **shared-family calendar is a calendar collection created on the operator's primary Fastmail account** (`me@lucasberger.ca`) and shared out to the wife + others via Fastmail's own calendar sharing. The app's single app password enumerates it like any other collection; the `calendars.is_shared` flag (operator-set) marks which row is the shared one. | Clarified during the Wave 2 checkpoint: "broker account" was only ever the role the primary account's app password plays. id=1 ("Calendar") is the operator's **personal** calendar, not the shared one — so it must NOT be marked `is_shared`. Aggregating each *other* member's **personal** calendar still follows the D-09 per-member app-password model (open for Phase 3 onboarding: a member may get a personal color lane, or only the shared calendar). | — Pending (shared calendar not yet created) |
## Evolution
This document evolves at phase transitions and milestone boundaries.
**After each phase transition** (via `/gsd-transition`):
1. Requirements invalidated? → Move to Out of Scope with reason
2. Requirements validated? → Move to Validated with phase reference
3. New requirements emerged? → Add to Active
4. Decisions to log? → Add to Key Decisions
5. "What This Is" still accurate? → Update if drifted
**After each milestone** (via `/gsd-complete-milestone`):
1. Full review of all sections
2. Core Value check — still the right priority?
3. Audit Out of Scope — reasons still valid?
4. Update Context with current state
---
*Last updated: 2026-06-03 after initialization*