# FamChore v2 — Architecture & Developer Reference ## Overview Multi-tenant chore tracking SaaS. Families ("fams") are isolated tenant groups. Fam admins use email/password. Members join via invite code + device token (no password). Super admin (you) can see everything. **Demo reference:** Current prototype at `/home/threejjjs/development/famchore/` --- ## 1. Stack | Component | Role | Deploy | Port | |---|---|---|---| | SvelteKit | SSR frontend, all UI | Coolify Docker (chores.app.com) | :3000 | | Hono proxy | Stripe, CRON, webhooks | Same container as SvelteKit, proxied via `/api/*` | :3001 (internal) | | PocketBase | DB, auth, realtime, storage, Admin UI | Coolify Docker (pb.chores.app.com) | :8090 | ### Deployment Topology ``` chores.app.com ────┬──► SvelteKit (:3000) │ └── /api/* ──► Hono proxy (:3001) │ pb.chores.app.com ──► PocketBase (:8090) │ Admin UI at /_ │ Volume: /pb_data (persistence + backups) │ stripe.com ─────────► Hono /api/stripe/webhook ``` --- ## 2. Auth Model ### 2.1 Roles & Methods | Role | Auth | Session | Scope | PB Entity | |---|---|---|---|---| | **Super admin** (you) | PB email + password | 24hr JWT | All collections, all fams | PB `users` (set via seed) | | **Fam admin** | PB email + password | 24hr JWT | Their fam only | PB `users` | | **Member** | Invite code + device token | localStorage, no expiry | Own data only | `members` collection | ### 2.2 Member Auth Flow 1. Fam admin generates invite code → stored on `fams.inviteCode` 2. Admin shares as text (`chores.app.com/join/XYZ123`) or QR code 3. Member opens link, enters name, browser generates `crypto.randomUUID()` as device token 4. PB API creates `members` record with SHA-256 hashed token 5. Device token stored in `localStorage`, sent as `X-Device-Token` header 6. If token lost → admin regenerates invite code, member re-registers 7. Admin can revoke access by deleting member record ### 2.3 PB Auth Rules (row-level security) Every collection has `famId` field. PB rule pattern: ``` famId = @request.auth.famId ``` Super admin bypasses via admin API (PB superuser credentials). --- ## 3. Data Model All collections live in PocketBase. Every tenant-scoped collection includes `famId` (relation to `fams`). ### `fams` | Field | Type | Notes | |---|---|---| | `id` | auto | PB default | | `name` | text | Display name | | `slug` | text | URL segment, unique | | `inviteCode` | text | Short alphanumeric, regeneratable | | `stripeCustomerId` | text? | Set after first donation | | `featureFlags` | json | `{ "monthlyBonus": true }` | | `created` | auto | | ### `members` | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | | | `name` | text | | | `color` | text | Hex | | `deviceToken` | text | SHA-256 hash of raw token | | `deviceTokenHint` | text | First 8 chars of raw token (for admin display) | | `pointsThreshold` | number? | Override fam default | | `weeklyBonus` | number? | Override fam default | | `created` | auto | | ### `chore_templates` | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | | | `name` | text | | | `description` | text | | | `defaultFrequency` | select | `daily` or `weekly` | | `defaultType` | select | `points` or `money` | | `defaultValue` | number | | ### `assigned_chores` | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | | | `memberId` | relation→members | | | `templateId` | relation→chore_templates | | | `frequency` | select | | | `type` | select | | | `value` | number | | | `customName` | text? | | ### `completions` | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | | | `memberId` | relation→members | | | `assignedChoreId` | relation→assigned_chores | | | `date` | date | ISO date | | `completedAt` | auto | | ### `rewards` | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | | | `memberId` | relation→members | | | `source` | select | `weekly_bonus`, `monthly_bonus`, `custom` | | `label` | text | | | `value` | number | | | `weekStart` | date? | | | `month` | text? | "2026-06" | | `claimed` | bool | | | `claimedAt` | auto? | | ### `weekly_history` | Field | Type | |---|---| | `famId` | relation→fams | | `memberId` | relation→members | | `weekStart` | date | | `pointsEarned` | number | | `moneyEarned` | number | | `choresCompleted` | number | | `bonusEarned` | number | ### `monthly_bonuses` | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | | | `month` | text | "2026-06" | | `prizeType` | select | `cash`, `string` | | `prizeValue` | text | | | `winnerMemberId` | relation→members? | Nullable until computed | | `pointsScored` | number? | | | `claimed` | bool | | ### `settings` (singleton per fam) | Field | Type | Notes | |---|---|---| | `famId` | relation→fams | Unique | | `pointsThreshold` | number | Default: 100 | | `weeklyBonus` | number | Default: 2 (e.g. £2) | | `webhookUrl` | text? | N8N/notification URL | --- ## 4. Routes ### SvelteKit ``` / Landing page (SaaS marketing) /join/:code Member invite code + name entry /{fam} Fam dashboard (weekly overview) /{fam}/admin Admin panel /{fam}/admin/chores Chore template CRUD + assignment grid /{fam}/admin/rewards Reward management, claim history /{fam}/admin/settings Thresholds, webhook, invite code, features /{fam}/:username Member kanban ?token= Auto-auth via query param (from QR/share) ``` ### Hono proxy (`/api/*`) ``` /api/stripe/create-checkout Create Stripe Checkout Session /api/stripe/webhook Stripe event webhook /api/weekly-cron Coolify CRON target ``` --- ## 5. Data Flow ### 5.1 Chore Toggle ``` User clicks checkbox → Browser → PB SDK (direct, auth= device token or admin JWT) → PB inserts/deletes completion → PB realtime SSE push to all subscribers → UI updates (kanban card, progress bar, money counter) → If weekly threshold crossed: → SvelteKit server handler creates Reward (weekly_bonus) ``` ### 5.2 Weekly CRON Triggered by Coolify CRON job → `GET /api/weekly-cron`: ``` Hono receives request → Queries all fams → For each fam: → Compute weekly summaries per member (completions tally) → Upsert weekly_history records → Evaluate monthly bonus (end-of-month) → If webhookUrl set on fam settings: → POST summary to webhook (pluggable — WhatsApp later) → Returns 200 ``` ### 5.3 Stripe Donation ``` User clicks "Donate" on landing or modal → Hono /api/stripe/create-checkout → Creates Stripe Checkout Session → Returns session.url → redirect user to Stripe → Stripe redirects back to app → Stripe webhook → Hono /api/stripe/webhook → Updates fam.stripeCustomerId → Sets fam.featureFlags.donated = true (or similar) ``` ### 5.4 Admin CRUD All admin operations go through PB admin API (Hono proxy or `+page.server.ts`). This ensures: - Server-side validation of famId - Audit trail option - Consistent error handling --- ## 6. Project Structure ``` /chores /src SvelteKit app /lib /components Shared UI components /stores Svelte stores (auth, current fam) /pb PB SDK client helpers /routes SvelteKit file-based routing /hooks.server.ts Auth hooks, PB client init /proxy Hono proxy /src /routes Stripe webhook, CRON handler /services PB admin client, notification service package.json /seed JSON dump files for PB collections /pb PB collection schema definitions package.json Workspace root Dockerfile.frontend SvelteKit + Hono build Dockerfile.backend PocketBase (custom, or use official image) coolify.json Coolify deployment config (optional) AGENTS.md AI reference (this file's sibling) ARCHITECTURE.md This document ``` --- ## 7. Environment Variables ### Frontend + Hono container ``` PUBLIC_PB_URL=https://pb.chores.app.com PB_ADMIN_EMAIL=admin@chores.app PB_ADMIN_PASSWORD= STRIPE_SECRET_KEY=sk_live_xxx STRIPE_WEBHOOK_SECRET=whsec_xxx DONATION_MODAL_INTERVAL=30 ``` ### PocketBase container ``` PB_SUPERUSER_EMAIL=you@email.com PB_SUPERUSER_PASSWORD= ``` --- ## 8. Implementation Phases ### Phase 0 — Infrastructure (2-4 hrs) - [ ] Deploy PocketBase on Coolify (`pb.chores.app.com`) - Official `pocketbase/pocketbase` image - Mount `/pb_data` volume - Set super admin env vars - Test: visit `pb.chores.app.com/_/`, login, data persists after restart - [ ] Scaffold monorepo with SvelteKit + Hono - [ ] Create Dockerfiles - [ ] Deploy to Coolify (`chores.app.com`) - [ ] Cloudflare DNS for both ### Phase 1 — Auth (4-6 hrs) - [ ] Create PB collections: `fams`, `members`, `settings` - [ ] Super admin seed script - [ ] Fam signup → PB user + fam record - [ ] Fam admin login (24hr JWT) - [ ] Invite code generation - [ ] Member join page (`/join/:code`) - [ ] Device token auth - [ ] Route guards - [ ] Seed data (1 fam + 3 members + chores matching demo) ### Phase 2 — Core Chore Tracking (6-8 hrs) - [ ] PB collections: `chore_templates`, `assigned_chores`, `completions`, `weekly_history` - [ ] Admin chore CRUD - [ ] Admin chore assignment grid - [ ] Member kanban (3 columns: Daily Pending, Weekly Pending, Completed) - [ ] Completion toggle - [ ] Realtime updates (PB SSE) - [ ] Weekly progress + chart - [ ] Admin dashboard ### Phase 3 — Rewards + Claims (3-4 hrs) - [ ] PB collection: `rewards` - [ ] Auto-create weekly bonus reward on threshold - [ ] Claim section with visual feedback - [ ] Admin reward CRUD - [ ] Monthly bonus (set prize → compute winner → create reward) - [ ] Pluggable notification interface in CRON handler ### Phase 4 — SaaS (4-6 hrs) - [ ] Landing page (hero, features, CTA) - [ ] Stripe one-time checkout (Hono) - [ ] Donation modal (triggered after N admin page loads) - [ ] Feature flags (checked in routes via settings) - [ ] Super admin dashboard (stats, all-fams view, feature toggles) ### Phase 5 — Polish (ongoing) - [ ] QR invite code - [ ] PB backup scheduler - [ ] Error/loading/empty states - [ ] Responsive mobile layout - [ ] Accessibility --- ## 9. Key Conventions - **`famId` on every query** — PB auth rules enforce `famId = @request.auth.famId` - **PB SDK client-side for members** — Browser talks to PB directly for toggles, reads. PB auth rules handle security. - **PB admin API server-side for admins** — Hono proxy or SvelteKit server handlers for admin CRUD. - **Device tokens as SHA-256** — Never store or log raw tokens. - **JSON dump for seed data** — Portable, version-controllable, restorable via PB backup CLI. - **All collection schema files in `/pb/`** — Tracked in git, used for CI/CD schema migration. - **Notifications via pluggable interface** — Hono CRON handler has `NotificationService` interface; WhatsApp is one implementation (deferred). --- ## 10. Open / Deferred - **WhatsApp notifications** — Will be implemented as a `NotificationService` plugin for the weekly CRON handler. N8N or Twilio, TBD. - **Subscription payments** — Currently one-time donation only. Subscription model can be added later via Stripe webhooks. - **Member re-auth on new device** — Current design requires admin to regenerate invite code. Could add "re-issue link" feature in admin panel. - **Multi-language** — Not yet scoped. All text in English for now. --- ## 11. Reference: Current Prototype The existing HonoJS prototype at `/home/threejjjs/development/famchore/` contains the reference logic for: - Weekly bonus calculation (`src/services.ts` → `checkWeeklyBonus`) - Monthly bonus evaluation - Reward claim flow - Chore toggle event delegation - Chart/stat formatting - Date/timezone helpers Refer to `src/types.ts` for the original type definitions, and `src/views/` for the Alpine.js template structure that maps to the new SvelteKit components.