diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 9f0f1ca..d3361a6 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -88,36 +88,34 @@ Explicitly excluded. Documented to prevent scope creep. Anti-features sourced fr ## Traceability -Which phases cover which requirements. Populated during roadmap creation. - | Requirement | Phase | Status | |-------------|-------|--------| -| AUTH-01 | TBD | Pending | -| AUTH-02 | TBD | Pending | -| AUTH-03 | TBD | Pending | -| CAL-01 | TBD | Pending | -| CAL-02 | TBD | Pending | -| CAL-03 | TBD | Pending | -| CAL-04 | TBD | Pending | -| CAL-05 | TBD | Pending | -| CAL-06 | TBD | Pending | -| CAL-07 | TBD | Pending | -| CAL-08 | TBD | Pending | -| LIST-01 | TBD | Pending | -| LIST-02 | TBD | Pending | -| LIST-03 | TBD | Pending | -| LIST-04 | TBD | Pending | -| NOTIF-01 | TBD | Pending | -| NOTIF-02 | TBD | Pending | -| NOTIF-03 | TBD | Pending | -| PWA-01 | TBD | Pending | -| PWA-02 | TBD | Pending | +| AUTH-01 | Phase 1 | Pending | +| AUTH-02 | Phase 1 | Pending | +| AUTH-03 | Phase 1 | Pending | +| CAL-01 | Phase 1 | Pending | +| CAL-08 | Phase 1 | Pending | +| CAL-02 | Phase 2 | Pending | +| CAL-03 | Phase 2 | Pending | +| CAL-04 | Phase 3 | Pending | +| CAL-05 | Phase 3 | Pending | +| CAL-06 | Phase 3 | Pending | +| CAL-07 | Phase 3 | Pending | +| PWA-01 | Phase 3 | Pending | +| PWA-02 | Phase 3 | Pending | +| LIST-01 | Phase 4 | Pending | +| LIST-02 | Phase 4 | Pending | +| LIST-03 | Phase 4 | Pending | +| LIST-04 | Phase 4 | Pending | +| NOTIF-01 | Phase 5 | Pending | +| NOTIF-02 | Phase 5 | Pending | +| NOTIF-03 | Phase 5 | Pending | **Coverage:** - v1 requirements: 20 total -- Mapped to phases: 0 (set by roadmapper) -- Unmapped: 20 ⚠️ (resolved at roadmap step) +- Mapped to phases: 20 +- Unmapped: 0 ✓ --- *Requirements defined: 2026-06-03* -*Last updated: 2026-06-03 after initial definition* +*Last updated: 2026-06-03 — traceability populated by roadmapper* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md new file mode 100644 index 0000000..8e133b5 --- /dev/null +++ b/.planning/ROADMAP.md @@ -0,0 +1,99 @@ +# Roadmap: FamilySync + +## Overview + +FamilySync is built in five phases, each delivering an end-to-end user-observable capability. Phase 1 is both the foundation and the highest-risk gate: OIDC auth must work and the CalDAV broker must prove it can read personal Fastmail calendars before any calendar UI is built. Phases 2–3 complete the calendar. Phase 4 delivers shared lists with live co-edit sync. Phase 5 wires up Web Push notifications. The dependency chain is strict: each phase is a prerequisite for the next, except the lists track (Phase 4) which is independent of the calendar write path. + +## Phases + +**Phase Numbering:** +- Integer phases (1, 2, 3): Planned milestone work +- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED) + +Decimal phases appear between their surrounding integers in numeric order. + +- [ ] **Phase 1: Foundation + Broker Spike** - Auth, Docker scaffold, CalDAV broker read path, and personal-calendar ACL spike (go/no-go gate) +- [ ] **Phase 2: Calendar Display** - Read-only unified color-coded calendar (week/month/day/agenda) built on the confirmed broker +- [ ] **Phase 3: Event Write-Back + PWA Install** - Full event CRUD written back to Fastmail, PWA manifest + service worker, guided iOS install flow +- [ ] **Phase 4: Shared Lists + Live Sync** - Named collaborative lists with item CRUD and real-time SSE co-edit sync +- [ ] **Phase 5: Web Push Notifications** - VAPID push for event reminders, event changes, and list-change alerts + +## Phase Details + +### Phase 1: Foundation + Broker Spike +**Goal**: The app stack is running, both members can authenticate, and the CalDAV broker can read Fastmail calendars — with a confirmed go/no-go decision on personal-calendar cross-account sharing +**Mode:** mvp +**Depends on**: Nothing (first phase) +**Requirements**: AUTH-01, AUTH-02, AUTH-03, CAL-01, CAL-08 +**Success Criteria** (what must be TRUE): + 1. Both members can reach the app URL, authenticate through Authelia OIDC, and land on the app home page without entering any Fastmail credentials + 2. Sessions persist across browser restarts — neither member is asked to log in again on the next visit + 3. Each member is assigned a stable, distinct display color that does not change between sessions + 4. The broker successfully fetches and caches at least one event from the shared Fastmail calendar via CalDAV PROPFIND/REPORT + 5. The personal-calendar ACL spike produces a documented go/no-go decision: either the broker token sees the wife's personal calendar after Fastmail share+accept, or the fallback strategy (shared-family-only or per-member app password) is chosen and recorded +**Plans**: TBD + +### Phase 2: Calendar Display +**Goal**: Both members can see a unified, color-coded calendar aggregating all accessible Fastmail calendars across day, week, month, and agenda views — read-only, no write-back yet +**Mode:** mvp +**Depends on**: Phase 1 +**Requirements**: CAL-02, CAL-03, CAL-07 +**Success Criteria** (what must be TRUE): + 1. Opening the app shows a color-coded calendar where each member's events appear in their assigned color, with shared events distinguishable from personal events + 2. The user can switch between day, week, month, and agenda views and all events render correctly in each view + 3. A recurring event (e.g., weekly meeting) displays all its occurrences correctly in the current view window, including correct behavior across DST boundaries + 4. All-day events (birthdays, holidays) appear as full-day banners on the correct date with no timezone shift +**Plans**: TBD +**UI hint**: yes + +### Phase 3: Event Write-Back + PWA Install +**Goal**: Both members can create, edit, and delete events that are written back to the correct Fastmail calendar, and the app is installable to the iPhone and Android home screens with a guided onboarding flow +**Mode:** mvp +**Depends on**: Phase 2 +**Requirements**: CAL-04, CAL-05, CAL-06, CAL-07, PWA-01, PWA-02 +**Success Criteria** (what must be TRUE): + 1. A member can create a timed or all-day event (including recurring events) in the app and see it appear in the native Fastmail app within the next sync cycle + 2. A member can edit an existing event's title, time, or description and the change persists correctly in Fastmail + 3. A member can delete an event and it disappears from all views on the next sync + 4. On Android, the app shows a browser install prompt and installs to the home screen; on iOS, the app shows a guided "Add to Home Screen" walkthrough with annotated screenshots that a non-technical user can follow independently + 5. The installed PWA opens full-screen without browser chrome on both iOS and Android +**Plans**: TBD +**UI hint**: yes + +### Phase 4: Shared Lists + Live Sync +**Goal**: Both members can create and manage shared named lists with real-time co-edit sync — edits by one member appear for the other without any manual refresh +**Mode:** mvp +**Depends on**: Phase 1 +**Requirements**: LIST-01, LIST-02, LIST-03, LIST-04 +**Success Criteria** (what must be TRUE): + 1. Either member can create a named list (e.g., "Groceries") and delete a list they no longer need + 2. Either member can add items to a list, check items off, reorder them by drag-and-drop, and delete individual items + 3. When one member adds or checks off an item, the other member sees the change appear in the list within a few seconds without refreshing — even if they reconnect after a brief network gap +**Plans**: TBD +**UI hint**: yes + +### Phase 5: Web Push Notifications +**Goal**: Both members receive timely Web Push alerts for upcoming events, event changes made by the other member, and list changes — reliably on both iOS and Android +**Mode:** mvp +**Depends on**: Phase 3, Phase 4 +**Requirements**: NOTIF-01, NOTIF-02, NOTIF-03 +**Success Criteria** (what must be TRUE): + 1. A member receives a push notification on their phone approximately 15 minutes before a calendar event starts — delivered to the installed PWA, including on iOS + 2. When the other member adds or changes a calendar event, the first member receives a push notification with the event title and action described in the payload + 3. When the other member modifies a shared list (adds, checks off, or deletes an item), the first member receives a push notification identifying the list and the change + 4. After an extended period of app inactivity, push notifications are still delivered (subscription health-check prevents silent revocation on iOS) +**Plans**: TBD + +## Progress + +**Execution Order:** +Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 +Note: Phase 4 depends only on Phase 1 and can begin as soon as Phase 1 is complete. It is serialized here to reduce work-in-progress. + +| Phase | Plans Complete | Status | Completed | +|-------|----------------|--------|-----------| +| 1. Foundation + Broker Spike | 0/? | Not started | - | +| 2. Calendar Display | 0/? | Not started | - | +| 3. Event Write-Back + PWA Install | 0/? | Not started | - | +| 4. Shared Lists + Live Sync | 0/? | Not started | - | +| 5. Web Push Notifications | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md new file mode 100644 index 0000000..a91764f --- /dev/null +++ b/.planning/STATE.md @@ -0,0 +1,87 @@ +--- +gsd_state_version: '1.0' +status: planning +progress: + total_phases: 5 + completed_phases: 0 + total_plans: 0 + completed_plans: 0 + percent: 0 +--- + +# Project State + +## Project Reference + +See: .planning/PROJECT.md (updated 2026-06-03) + +**Core value:** One color-coded family calendar (shared + personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store +**Current focus:** Phase 1 — Foundation + Broker Spike + +## Current Position + +Phase: 1 of 5 (Foundation + Broker Spike) +Plan: 0 of ? in current phase +Status: Ready to plan +Last activity: 2026-06-03 — Roadmap created + +Progress: [░░░░░░░░░░] 0% + +## Performance Metrics + +**Velocity:** +- Total plans completed: 0 +- Average duration: - +- Total execution time: 0 hours + +**By Phase:** + +| Phase | Plans | Total | Avg/Plan | +|-------|-------|-------|----------| +| - | - | - | - | + +**Recent Trend:** +- Last 5 plans: - +- Trend: - + +*Updated after each plan completion* + +## Accumulated Context + +### Decisions + +Decisions are logged in PROJECT.md Key Decisions table. +Recent decisions affecting current work: + +- Phase 1 gate: Personal-calendar CalDAV ACL must be spiked before calendar UI is built. Fallback is shared-family-only if spike fails. +- CalDAV locked: Fastmail does not expose calendars over JMAP. CalDAV via tsdav is the only protocol. No reconsideration. +- Identity: Use oidc_iss + oidc_sub as stable composite key. Never email. +- Real-time transport: Prefer SSE over WebSocket (proxy-resilient through Pangolin). Verify Pangolin SSE pass-through in Phase 1 infra spike. +- Recurring events: Create + display only in v1 (CALDAV:expand on server side). Single-occurrence edit deferred to v1.x. + +### Pending Todos + +None yet. + +### Blockers/Concerns + +- Phase 1: Personal-calendar CalDAV ACL behavior on Fastmail is LOW confidence (must spike). Failure degrades unified view to shared-family-only for v1. +- Phase 1: Pangolin SSE/WebSocket pass-through is an open infra question (known issue #1034). Must smoke-test before Phase 4 real-time sync is built. +- Phase 3: iOS install guide is load-bearing for the wife — she will never receive push notifications if she does not install the PWA. +- Phase 5: iOS push subscriptions silently revoked after 3 silent pushes. Subscription health-check and event.waitUntil() are mandatory from day one. + +## Deferred Items + +| Category | Item | Status | Deferred At | +|----------|------|--------|-------------| +| Calendar | Single-occurrence recurring edit (RECURRENCE-ID) | v1.x | Roadmap | +| Calendar | "This and following" recurring edit | v1.x | Roadmap | +| Calendar | Apple Calendar native subscribe URL docs | v1.x | Roadmap | +| Calendar | Secondary timezone display toggle | v1.x | Roadmap | +| Display | Wall-display / kiosk dashboard | v2 | PROJECT.md | + +## Session Continuity + +Last session: 2026-06-03 +Stopped at: Roadmap created, STATE.md initialized. Ready to plan Phase 1. +Resume file: None diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8ad247a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,222 @@ + + +## Project + +**FamilySync** + +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. + +### 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 + + + + + +## Technology Stack + +## Recommended Stack + +### Core Technologies + +| Technology | Version | Purpose | Why Recommended | +|------------|---------|---------|-----------------| +| Node.js + TypeScript | 22 LTS | Backend runtime | First-class typing, same language as frontend, largest CalDAV/OIDC library ecosystem | +| Hono | 4.12.23 | HTTP framework | Web-Standards-native, first-class TypeScript, built-in SSE helper, WebSocket via `@hono/node-server`; lighter than Express and better ergonomics than Fastify for this size | +| Drizzle ORM | 0.45.2 | MariaDB query layer | Type-safe SQL, zero runtime overhead, native `mysql2` driver support, schema-as-code migrations via `drizzle-kit` | +| mysql2 | 3.22.4 | MariaDB driver | The only maintained native MariaDB/MySQL driver; Drizzle targets it explicitly | +| React 19 | 19.x | PWA frontend | Required by project; concurrent features, stable | +| Vite | 8.0.x | Build tooling | De-facto standard for React PWAs; fast HMR, native ESM | +| vite-plugin-pwa | 1.3.0 | Service worker + manifest | Zero-config Workbox integration, handles install prompt, offline cache, background sync scaffolding | + +### Supporting Libraries + +| Library | Version | Purpose | When to Use | +|---------|---------|---------|-------------| +| tsdav | 2.2.2 | CalDAV client for Node.js | All calendar reads and writes against Fastmail CalDAV endpoint; handles PROPFIND, REPORT, PUT, DELETE | +| ical.js | 2.2.1 | iCalendar (.ics) parsing | Parse raw VCALENDAR/VEVENT payloads returned by tsdav; handles VTIMEZONE, RDATE, EXDATE | +| rrule | 2.8.1 | Recurrence rule expansion | Expand RRULE strings into concrete event occurrences for the calendar view; ical.js's built-in expansion is less ergonomic for UI consumption | +| web-push | 3.6.7 | Server-side VAPID push | Generate VAPID keys, sign and dispatch push messages to browser push services (APNs for iOS, FCM for Android) | +| @hono/oidc-auth | 1.8.3 | OIDC session middleware for Hono | Storage-less JWT session cookies; authorization-code + PKCE flow; works with any RFC-compliant OIDC provider including Authelia | +| openid-client | 6.8.4 | Low-level OIDC primitives | If `@hono/oidc-auth` proves insufficient (e.g., custom token introspection), use this as the lower-level escape hatch | +| ioredis | 5.11.0 | Redis client | Pub/sub for broadcasting list-change events to SSE connections across Node processes | +| zod | 3.24.x | Schema validation | Validate API request bodies and CalDAV event payloads before writing back to Fastmail | +| @hono/zod-validator | 0.8.0 | Hono middleware for Zod | Validate request body/query in route handlers with Zod schemas | +| @tanstack/react-query | 5.101.0 | Server state + caching | Manages calendar and list data fetching, background refetch, stale-while-revalidate; pairs with SSE for live list updates | +| zustand | 5.0.14 | Client state | UI-only state (selected date range, color assignments, drawer open/closed); keep server state in React Query | +| drizzle-kit | 0.31.10 | Schema migrations | Generates and runs MariaDB migrations from Drizzle schema definitions | + +### Development Tools + +| Tool | Purpose | Notes | +|------|---------|-------| +| TypeScript 5.x | Strict typing across backend + frontend | `strict: true`; share types between packages via a `packages/shared` workspace | +| ESLint + Prettier | Lint + format | Standard config; no bikeshedding needed | +| Docker Compose | Local dev + production parity | Match Unraid stack exactly in dev | +| Vitest | Unit + integration tests | Vite-native, same config as frontend | + +## Installation + +# Backend + +# Frontend + +# Dev + +## Calendar Integration: CalDAV, Not JMAP + +- Principal URL: `https://caldav.fastmail.com/dav/principals/user/broker@fastmail.com/` +- `tsdav` performs `PROPFIND` on the principal to discover all calendar collections, then fetches each collection's events via `REPORT` (calendar-query or calendar-multiget). +- One app password covers all calendars owned by that account under the default "Mail, Contacts & Calendars" scope. +- `tsdav` returns raw iCalendar strings. Pass each to `ical.js` for parsing into event objects, then use `rrule` for RRULE expansion into the date range the UI needs. +- Write-back (create/edit/delete): PUT a new `.ics` to the collection URL; DELETE by UID. +- `node-ical`: older fork with weaker RRULE support; ical.js is maintained by Mozilla and is the reference implementation +- Direct `fetch`/`axios` against CalDAV: re-inventing XML namespace handling and PROPFIND parsing; tsdav exists specifically to avoid this +- JMAP: not available for calendars on Fastmail today + +## Backend Framework + +- First-class TypeScript with RPC-style type sharing (Hono RPC can export typed client for the React frontend — eliminates API drift) +- Built-in SSE streaming helper (`streamSSE`) for live list updates +- WebSocket support via `@hono/node-server` +- Runs on Node.js 22 LTS in Docker with `@hono/node-server` +- Prisma generates a binary engine that adds complexity in Docker images and has weaker MariaDB compatibility signals +- Drizzle uses `mysql2` directly — the same driver you'd use raw; no runtime translation layer +- Drizzle's `mysqlTable` schema is fully MariaDB-compatible (MariaDB is wire-compatible with MySQL; Drizzle's `mysql` dialect works) +- Type inference from schema → query results is the core value proposition; zero runtime overhead + +## React PWA Stack + +- TanStack Query owns all server-side state: calendar events, lists, user profile. It handles background refetch, cache invalidation, and loading states. Use `queryClient.invalidateQueries` from SSE event handlers to keep list data live. +- Zustand owns pure UI state: selected month, color assignments per calendar, drawer states. Do not put server data in Zustand. + +| Requirement | Detail | +|-------------|--------| +| Minimum iOS version | 16.4 — push is silently unavailable on earlier versions | +| Installation required | PWA **must** be added to Home Screen; push does not work from Safari browser tabs | +| User gesture | `pushManager.subscribe()` must be called inside a tap handler, not on page load | +| EU users on iOS 17.4+ | PWAs may open in Safari tabs instead of standalone mode due to DMA; affects push reach | +| Silent push | Not supported on iOS; all push messages must display a visible notification | +| Background sync | Not supported on iOS; no `BackgroundSync` or `PeriodicBackgroundSync` | + +## Authelia OIDC Integration + +- `response_types: [code]` +- `grant_types: [authorization_code, refresh_token]` +- `require_pkce: true`, `pkce_challenge_method: S256` +- `token_endpoint_auth_method: client_secret_basic` + +## Live List Sync + +## Alternatives Considered + +| Recommended | Alternative | Why Not | +|-------------|-------------|---------| +| Hono | Express | No native TypeScript ergonomics; no built-in SSE; larger ecosystem but more boilerplate | +| Hono | Fastify | Good choice but heavier plugin model; Hono's Web Standards alignment is better for this size | +| Drizzle | Prisma | Binary engine complicates Docker; weaker explicit MariaDB support; heavier | +| tsdav | Raw fetch + xml2js | CalDAV XML namespace handling is tedious; tsdav is the established TypeScript CalDAV client | +| ical.js | node-ical | node-ical is a fork that has diverged; ical.js is the Mozilla-maintained reference implementation | +| @hono/oidc-auth | express-openid-connect | express-openid-connect is Express-specific; Hono middleware is the correct fit | +| SSE | WebSockets | WebSockets are bidirectional; list sync is server→client only; SSE is simpler and proxy-friendly | +| CalDAV | JMAP | JMAP calendars not available on Fastmail as of 2026 | + +## What NOT to Use + +| Avoid | Why | Use Instead | +|-------|-----|-------------| +| JMAP for calendars | Not implemented by Fastmail; spec not finalized | CalDAV via tsdav | +| Prisma | Binary engine, weaker MariaDB compat, larger footprint in Docker | Drizzle ORM | +| oidc-client-ts | Browser-side OIDC library; wrong layer for a backend-session app | @hono/oidc-auth | +| node-ical | Older fork of ical.js, less maintained, weaker RRULE handling | ical.js | +| Create React App | Deprecated February 2025 | Vite | +| PostgreSQL | Not in the Unraid stack; hard constraint | MariaDB | +| NestJS | Massive framework overhead for a two-user household app | Hono | +| Firebase/FCM as push broker | Third-party dependency; VAPID direct push works without it | web-push (VAPID) | + +## Version Compatibility + +| Package | Compatible With | Notes | +|---------|-----------------|-------| +| drizzle-orm@0.45.x | mysql2@3.x | Use `drizzle-orm/mysql2` import path; mysql2@3.x uses Promises API by default | +| vite-plugin-pwa@1.3.x | Vite@8.x, Workbox@7.x | vite-plugin-pwa 0.16+ requires Node 16+; 1.x tracks Vite 6+ | +| @hono/oidc-auth@1.8.x | hono@4.x, oauth4webapi | Peer-depends on hono 4.x | +| ical.js@2.x | rrule@2.8.x | Use together: ical.js parses the RRULE string, pass to `new RRule(RRule.parseString(...))` | +| web-push@3.6.x | Node.js 18+ | VAPID uses Web Crypto; works in Node.js 18+ natively | + +## Open Questions Flagged for Phase Research + +## Sources + +- [Fastmail API Documentation](https://www.fastmail.com/dev/) — Confirmed CalDAV-only for calendars; JMAP calendars not available +- [Fastmail App Passwords](https://www.fastmail.help/hc/en-us/articles/360058752854-App-passwords) — Scope covers CalDAV; single password covers all calendars in account +- [Using Fastmail with CalDAV libraries](https://utf9k.net/blog/fastmail-caldav/) — Principal URL pattern, app password auth +- [Fastmail Calendar Sharing](https://www.fastmail.help/hc/en-us/articles/1500000279781-Sharing-calendars-with-other-users) — Sharing is multi-user-account scoped; cross-account sharing unconfirmed +- [tsdav npm](https://www.npmjs.com/package/tsdav) — Version 2.2.2 confirmed +- [ical.js npm](https://www.npmjs.com/package/ical.js) — Version 2.2.1 confirmed; Mozilla-maintained +- [rrule npm](https://www.npmjs.com/package/rrule) — Version 2.8.1 confirmed +- [Hono](https://hono.dev/) — Version 4.12.23; Node.js adapter confirmed +- [Drizzle ORM MySQL](https://orm.drizzle.team/docs/get-started-mysql) — MariaDB via mysql2 confirmed +- [vite-plugin-pwa](https://vite-pwa-org.netlify.app/) — Version 1.3.0; Workbox 7 integration +- [web-push npm](https://www.npmjs.com/package/web-push) — Version 3.6.7 +- [Meet Declarative Web Push — WebKit](https://webkit.org/blog/16535/meet-declarative-web-push/) — Safari 18.4+, iOS 18.4+ confirmed +- [PWA iOS Limitations 2026](https://www.magicbell.com/blog/pwa-ios-limitations-safari-support-complete-guide) — iOS 16.4 minimum; home screen required; EU DMA regression +- [Authelia Express.js Integration](https://www.authelia.com/integration/openid-connect/clients/expressjs/) — Authorization code + PKCE flow; client_secret_basic +- [@hono/oidc-auth GitHub](https://github.com/honojs/middleware/tree/main/packages/oidc-auth) — Storage-less JWT session cookies; Version 1.8.3 + + + + + +## Conventions + +Conventions not yet established. Will populate as patterns emerge during development. + + + + +## Architecture + +Architecture not yet mapped. Follow existing patterns found in the codebase. + + + + +## Project Skills + +No project skills found. Add skills to any of: `.claude/skills/`, `.agents/skills/`, `.cursor/skills/`, `.github/skills/`, or `.codex/skills/` with a `SKILL.md` index file. + + + + +## GSD Workflow Enforcement + +Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync. + +Use these entry points: + +- `/gsd-quick` for small fixes, doc updates, and ad-hoc tasks +- `/gsd-debug` for investigation and bug fixing +- `/gsd-execute-phase` for planned phase work + +Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it. + + + + +## Developer Profile + +> Profile not yet configured. Run `/gsd-profile-user` to generate your developer profile. +> This section is managed by `generate-claude-profile` -- do not edit manually. +