# FILE: README_USE_WITH_CODEX.md # How to use this package with Codex This package contains a Codex-ready build brief for a browser-first group coordination platform. Recommended use: 1. Create an empty Git repository. 2. Copy `AGENTS.md`, `CODEX_TASK.md`, `PRODUCT_SPEC.md`, `ARCHITECTURE_CONTRACTS.md`, and `ACCEPTANCE_TESTS.md` into the repository root. 3. Open the repo in Codex. 4. Ask Codex: ```text Read AGENTS.md and CODEX_TASK.md. Implement the full product described in PRODUCT_SPEC.md, ARCHITECTURE_CONTRACTS.md, and ACCEPTANCE_TESTS.md. Build a runnable monorepo. Do not stop at a prototype shell; deliver working vertical slices first, then deepen features until the acceptance tests pass. ``` Alternative: paste the contents of `CODEX_TASK.md` directly into Codex after adding the other files to the repo. The intended result is a monorepo with: ```text backend/ FastAPI app, DB models, API routes, seed data, tests frontend/ React/Vite app, mobile-first UI, client API layer, tests docker-compose.yml README.md ``` The project deliberately excludes full federation, native apps, unofficial WhatsApp bridges, and production-grade end-to-end encryption. It includes local/self-hosted servers, a home-server model, and cross-server aggregation through scoped server connections. --- # FILE: AGENTS.md # AGENTS.md ## Project mission Build a browser-first group coordination platform that lowers dependence on WhatsApp-style group chats by giving organizations and members a better structured alternative: events, announcements, files, member management, tasks, polls, commitments, catch-up, and cross-group aggregation. The product is not a universal chat bridge. It is an exit ramp for groups. WhatsApp or other messengers may be represented only through manual migration aids, imports, digests, and invite/reminder copy. ## Required stack Use this stack unless the existing repository already has a strong conflicting convention: - Backend: Python, FastAPI, Pydantic, SQLAlchemy, SQLite by default, optional PostgreSQL configuration. - Frontend: React, Vite, TypeScript. - Styling: mobile-first responsive UI. Prefer Tailwind CSS or a clean CSS-variable design system if Tailwind setup becomes a distraction. - Tests: pytest for backend; Vitest/React Testing Library for frontend where practical. - Packaging: Docker Compose for local development. ## Implementation behavior - If the repository is empty, scaffold a complete monorepo. - Prefer a fully runnable vertical slice over many nonfunctional placeholders. - Do not ask product questions unless absolutely blocked. Make reasonable choices and document them. - Do not stop at mock screens. Implement working backend-backed flows for the core use cases. - Keep the browser-first/accountless join flow working at all times. - Make security-conscious defaults: invite token hashes, HttpOnly cookies for sessions, no long-lived auth secrets in localStorage, scoped permissions, CORS allowlists, role checks. - When production-grade implementation is too large for one pass, create a clean adapter/interface and a working development implementation. Mark the gap clearly in code and README. - Run formatting/tests/builds where possible before finishing. ## Non-goals Do not implement: - Full server-to-server federation. - A native mobile app. - Unofficial WhatsApp, Signal, iMessage, Facebook, or WeChat scraping/bridging. - Social feed mechanics unrelated to group coordination. - Password-first sign-up. - Mandatory login before a user can open an invite and complete the first useful action. - Production E2EE unless specifically requested in a later task. ## Product principles 1. The group, not the individual account, is the migration unit. 2. Account creation must be separate from membership identity. 3. The first useful action must happen before registration friction. 4. Chat is present but demoted. Structured objects are first-class. 5. The home screen answers: “What needs my attention?” 6. Users with multiple groups need aggregation, not just another inbox. 7. Self-hosting must be possible without forcing full federation. 8. Portability and export are part of the product promise. ## UX principles - Mobile-first. The first session likely starts from a link in a phone messenger. - The UI should feel more like a group command center than a chat clone. - Top-level navigation should prioritize: Home, Calendar, Groups, Files, Me. - Chat should live inside groups and threads, not as the primary navigation item. - Separate “official” from “chatter.” - Use clear empty states, skeletons, and friendly recovery paths. - Avoid jargon such as OAuth, WebAuthn, passkey, token, federation in user-facing copy unless necessary. ## Coding conventions - Use clear module boundaries: auth, groups, structured objects, aggregation, remote servers, files, notifications. - Use UUID primary identifiers externally. Internal numeric IDs are acceptable only if not exposed casually. - Validate all API inputs with Pydantic schemas. - Never store raw invite tokens. Store token hashes. - Use environment-driven configuration. - Include seed data for demonstration. - Include a README with setup, architecture, and known limitations. --- # FILE: CODEX_TASK.md # CODEX_TASK.md You are Codex. Build this project from scratch as a runnable monorepo. ## Goal Implement a browser-first, mobile-first group coordination platform that helps groups migrate away from WhatsApp-style chaotic chats by replacing the stream with structured coordination: events, announcements, files, member management, tasks, polls, commitments, catch-up, notification preferences, and cross-group aggregation. The product must support: - Accountless invite-link joining. - Later device recovery through email, passkey-compatible architecture, QR/device linking, or admin resend. - A home-server concept: each user can have a home profile on a home server that stores their group memberships and remote server connection config. - Multiple self-hosted servers without full federation. - Aggregation across servers through scoped remote connections. - A responsive React/Vite UI optimized for phone browsers first. - A FastAPI backend that can run as both a home server and a group server. Do not implement full federation. Do not implement native apps. Do not implement unofficial messenger bridges. ## Deliverables Create a repository with this shape: ```text . ├── backend/ │ ├── app/ │ │ ├── main.py │ │ ├── core/ │ │ ├── db/ │ │ ├── models/ │ │ ├── schemas/ │ │ ├── routers/ │ │ ├── services/ │ │ └── tests/ │ ├── pyproject.toml │ └── README.md ├── frontend/ │ ├── src/ │ │ ├── app/ │ │ ├── components/ │ │ ├── features/ │ │ ├── routes/ │ │ ├── api/ │ │ ├── styles/ │ │ └── test/ │ ├── package.json │ └── README.md ├── docker-compose.yml ├── .env.example ├── README.md └── AGENTS.md ``` Use a different clean structure only if there is a strong technical reason. Document the reason. ## Development commands Implement commands equivalent to: ```bash # from repo root docker compose up --build # backend only cd backend python -m app.db.seed pytest uvicorn app.main:app --reload # frontend only cd frontend npm install npm run dev npm run test npm run build ``` Prefer modern package tooling, but do not let tooling block the implementation. ## Core product behavior ### 1. Accountless join flow A group admin can create a group and generate invite links. A recipient opens an invite link in a browser and can immediately: - see the group name, purpose, and limited preview; - join with a display name; - read official announcements; - see upcoming events; - RSVP to an event; - participate in chat/comments if permissions allow; - choose notification/recovery options later. Do not require email, password, social login, or native app install before first useful action. Implement: - Invite tokens stored as hashes. - One-time or limited-use invite links. - Session cookie issued after invite claim. - Device record attached to the member. - Revocation/reset by admin. ### 2. Progressive identity Users start as membership-bound participants. They may later create or attach a home profile. Implement trust levels: ```text Guest link can view public/limited content Invite member joined via invite link Claimed browser browser session/device is attached Verified contact email verified for recovery/notifications Passkey-ready model/API/UI support for passkey registration/login Full home profile aggregates multiple memberships and servers ``` If full WebAuthn/passkey implementation is feasible in the pass, implement it. Otherwise create clean backend routes, frontend UI, and a pluggable `PasskeyProvider` interface with a development provider. The rest of the product must not depend on passkeys working in production mode. User-facing copy should say “Save access on this device” or “Protect access,” not “Create account” as the first step. ### 3. Multi-device access Implement at least two working paths: 1. **Recovery link** by email in development mode. If SMTP is not configured, log the link/code to the backend console and show a dev hint in the UI. 2. **Link another device** via QR/code pairing: - new device starts pairing and receives a code; - existing device approves the pending code; - new device completes pairing and receives a session; - the device list shows both devices; - user/admin can revoke a device. Model the architecture so passkey sign-in can be added cleanly. ### 4. Structured group objects Implement these first-class objects: - Announcements - Events - RSVPs - Tasks - Polls - Files - Threads/messages - Members - Devices - Notification preferences - Commitments/action items - Migration status Chat exists, but it is not the product center. Structured cards should appear in the group dashboard and home dashboard. ### 5. Home dashboard for multi-group users The home page must answer “What needs my attention?” across all groups and connected servers. Implement sections: ```text Needs me Today / Upcoming Changed since last visit Official updates Catch up ``` Action item examples: - missing RSVP; - task assigned to me; - poll I have not answered; - event changed; - file requiring acknowledgement; - direct mention or admin question. The dashboard should group by urgency and consequence, not only chronology. ### 6. Group dashboard The group page must not default to a raw chat feed. Implement a group dashboard with: ```text Important now Upcoming events Open actions Official announcements Files Active discussions Members / admin tools depending on role ``` Chat or discussion threads can be one tab/section, but not the first or only concept. ### 7. Calendar Implement a cross-group calendar view: - all events from local memberships; - events from connected remote servers after sync; - RSVP status; - event location; - attached files; - changed-event indicators; - simple list view optimized for mobile. Calendar export can be a later enhancement. Add TODO or stub if not implemented. ### 8. Files Implement file metadata and upload/download for local groups. Minimum: - upload file to local storage in dev mode; - show file list by group and globally; - role/permission checks; - file attached to event/announcement when useful; - safe filenames and size limits. ### 9. Migration kit Implement practical migration features: - admin-created invite link/QR-ready URL; - member migration status: invited, opened, joined, verified, notification-enabled; - WhatsApp reminder copy generator; - “legacy channel” status flag; - optional WhatsApp chat export importer for `.txt` export files, stored as an archive, not as live bridged chat. The app must never require or use WhatsApp APIs. ### 10. Notification preferences Implement data model and UI for notification preferences: ```text Immediate: - direct mentions - event changes - urgent admin announcements - tasks assigned to me Quiet / digest: - discussions - new files - photos/general chatter Mute: - reactions/off-topic messages ``` Backend should store preferences. Implement in-app notification behavior and email dev-mode notifications/digests if feasible. Web push can be a future adapter if not implemented. ### 11. Home server and self-hosted aggregation Every server instance can act as: - a group server: owns canonical groups and their data; - a home server: stores a user's home profile and remote server connection config; - both. A user belongs to a home server. The home server stores config for remote servers. Implement these concepts: ```text HomeProfile RemoteServerConnection RemoteMembership RemoteSyncCursor RemoteCapability RemoteActionItem cache ``` A remote group server exposes a structured sync API. A home server connects to it with a scoped token and aggregates structured objects. Important: this is **not federation**. Servers do not replicate groups peer-to-peer. A home server simply acts as a client to the remote servers selected by the user. Implement a minimal remote connection flow: 1. Remote server exposes `/.well-known/group-platform.json`. 2. Remote server can create a scoped connection token for a member/admin in dev mode. 3. Home server connects to a remote server using URL + token. 4. Home server fetches capabilities. 5. Home server syncs actions/events/announcements/files/threads through `/api/sync`. 6. Home dashboard displays both local and remote objects. 7. Remote objects are marked clearly with their source server/group. Store sensitive remote tokens server-side, not in browser localStorage. Use localStorage only for non-sensitive hints such as last selected home server URL or UI preferences. ### 12. Open protocol seed Expose: ```text GET /.well-known/group-platform.json GET /api/sync?since= ``` The manifest should contain: ```json { "server_name": "Example Group Server", "api_base": "http://localhost:8000/api", "protocol_version": "0.1", "capabilities": { "events": true, "tasks": true, "files": true, "chat": true, "polls": true, "federation": false } } ``` The sync endpoint should return structured objects, not just raw chat messages: ```json { "cursor": "next_cursor", "actions": [], "events": [], "announcements": [], "files": [], "threads": [] } ``` ### 13. Responsive UI Build a polished mobile-first UI. Required navigation: ```text Home Calendar Groups Files Me ``` Required screens: - Join invite screen - Home dashboard - Calendar - Group list - Group dashboard - Group discussions/chat - Group admin / migration dashboard - Event detail + RSVP - Announcement detail - Task detail - Poll detail - File list and upload - Me/profile - Devices - Notification preferences - Connected servers - Connect remote server Design requirements: - bottom navigation on mobile; - responsive desktop layout with side navigation; - cards for action items, events, announcements, files, and discussions; - clear “official” vs “discussion” visual distinction; - accessible form labels and keyboard behavior; - loading and empty states; - seed-data demo should look credible immediately. Use a coherent design system: spacing scale, typography, elevation, badges, status colors, rounded cards, and compact mobile layouts. ### 14. Seed data Create seed data that demonstrates the concept: - FC Kreuzberg U12 Parents - Class 4B Parents - Tenant Association - Food Bank Volunteers Include users/members: - Anna Müller as the main demo user - Coach Mark - Lisa Becker - Samir Khan - Priya N. - Tenant admin Include events: - football match with missing RSVP; - training location changed; - parent evening; - tenant vote deadline; - volunteer shift. Include actions: - RSVP required; - vote required; - task assigned; - file acknowledgement; - direct mention. Include files: - season schedule; - emergency contacts; - meeting minutes; - school form. Include discussions: - snack coordination; - carpool planning; - maintenance issue; - volunteer supplies. ### 15. API shape Implement or approximate these endpoints. Auth/session: ```text GET /api/health GET /api/me POST /api/auth/invite/{token}/claim GET /api/join/{token}/preview POST /api/auth/recovery/request POST /api/auth/recovery/consume POST /api/auth/device-link/start POST /api/auth/device-link/approve POST /api/auth/device-link/complete GET /api/me/devices DELETE /api/me/devices/{device_id} POST /api/auth/passkeys/register/options POST /api/auth/passkeys/register/verify POST /api/auth/passkeys/login/options POST /api/auth/passkeys/login/verify ``` Home/aggregation: ```text GET /api/home GET /api/home/actions GET /api/home/calendar GET /api/home/files GET /api/home/official-updates GET /api/home/catch-up ``` Groups: ```text GET /api/groups POST /api/groups GET /api/groups/{group_id} PATCH /api/groups/{group_id} POST /api/groups/{group_id}/invites GET /api/groups/{group_id}/members PATCH /api/groups/{group_id}/members/{member_id} ``` Structured objects: ```text GET /api/groups/{group_id}/announcements POST /api/groups/{group_id}/announcements GET /api/groups/{group_id}/events POST /api/groups/{group_id}/events POST /api/events/{event_id}/rsvp GET /api/groups/{group_id}/tasks POST /api/groups/{group_id}/tasks PATCH /api/tasks/{task_id} GET /api/groups/{group_id}/polls POST /api/groups/{group_id}/polls POST /api/polls/{poll_id}/vote GET /api/groups/{group_id}/files POST /api/groups/{group_id}/files GET /api/files/{file_id}/download GET /api/groups/{group_id}/threads POST /api/groups/{group_id}/threads POST /api/threads/{thread_id}/messages ``` Migration: ```text GET /api/groups/{group_id}/migration POST /api/groups/{group_id}/migration/reminder-copy POST /api/groups/{group_id}/migration/import-whatsapp-export ``` Notifications/preferences: ```text GET /api/me/notification-preferences PATCH /api/me/notification-preferences GET /api/me/notifications PATCH /api/me/notifications/{notification_id}/read ``` Remote/self-hosted aggregation: ```text GET /.well-known/group-platform.json GET /api/sync?since= POST /api/connection-tokens GET /api/remote/servers POST /api/remote/servers/connect POST /api/remote/servers/{connection_id}/sync DELETE /api/remote/servers/{connection_id} ``` ### 16. Data model Use a practical relational schema. Include at least: ```text HomeProfile HomeDevice RecoveryMethod Session Group Member MemberDevice MemberInvite Announcement Event EventRsvp Task Poll PollOption PollVote FileAsset Thread Message NotificationPreference Notification ActionItem MigrationState AuditLog RemoteServerConnection RemoteMembership RemoteSyncCursor RemoteCachedObject ConnectionToken ``` A user can have a HomeProfile, but joining a group must not require one. A Member can exist without a HomeProfile. Later, a Member can attach to a HomeProfile. ### 17. Permissions Implement simple roles: ```text owner admin moderator member guest ``` Enforce at least: - only admins/owners can create invite links; - only admins/owners/moderators can create official announcements; - members can RSVP, vote, comment, and complete their tasks; - file upload can be configured but default to members; - migration dashboard is admin-only; - remote connection tokens are scoped. ### 18. Security requirements - Store invite tokens and recovery tokens as hashes. - Use HttpOnly, Secure-in-production, SameSite cookies for sessions. - Add CSRF protection for cookie-authenticated mutating requests, or document and implement a safe dev alternative. - Use server-side storage for remote tokens. - Do not place long-lived remote access tokens in localStorage. - Validate upload size/type. - Sanitize filenames. - Add audit logs for admin actions, invite creation, device linking, token creation, remote connection changes. - Provide `.env.example` with secrets and configuration. - Make dev mode explicit. ### 19. README Create a root README that explains: - product concept; - what is implemented; - quick start; - seed accounts/flows; - how to join via invite link; - how to link another device; - how to create a remote connection between two local server instances; - architecture overview; - security model; - limitations and next steps. ### 20. Quality bar The final app must be demonstrable without manually editing the database. At minimum, after setup: 1. A user can open the frontend. 2. Seed data appears. 3. The user can use a demo invite link and join a group without account creation. 4. The user can RSVP to an event. 5. The user can see a cross-group action dashboard. 6. An admin can create an invite and view migration status. 7. A user can link a second device/session through the pairing flow. 8. A user can connect a remote self-hosted server with URL + token and sync structured objects. 9. The UI works well on a phone-sized viewport. 10. Backend tests cover auth/join, permissions, actions, and remote sync. ## Suggested implementation order 1. Scaffold backend/frontend/docker. 2. Implement DB models and seed data. 3. Implement session/auth primitives and invite flow. 4. Implement groups, members, announcements, events, RSVPs. 5. Implement home dashboard/action derivation. 6. Implement group dashboard UI. 7. Implement files, tasks, polls, threads/messages. 8. Implement devices/recovery/device linking. 9. Implement migration dashboard and reminder copy generator. 10. Implement remote server manifest, connection token, sync endpoint, home aggregation. 11. Polish UI and responsive behavior. 12. Add tests and README. If time is limited, prioritize vertical completeness in this order: ```text invite join → group dashboard → event RSVP → home actions → admin migration → device linking → remote aggregation ``` Do not spend all effort on the chat feature. The product is structured coordination. --- # FILE: PRODUCT_SPEC.md # PRODUCT_SPEC.md ## Product name placeholder Use a neutral working name in code and UI, such as **GroupHome**. Keep it easy to rename. ## Product thesis Groups remain stuck on WhatsApp-like messengers because switching costs are social, not technical. The product lowers switching costs by making the group itself movable and by making the new home materially better than chat: structured events, files, announcements, member management, tasks, polls, commitments, migration tracking, browser-first access, and no mandatory account on first use. The product must not become a universal inbox. It must become a command center for group life. ## Target users Primary: - sports clubs; - school and parent groups; - tenant associations; - volunteer organizations; - local campaigns and civic groups; - small professional or nonprofit communities. Secondary: - members who belong to several groups and need a unified action view; - admins who want to leave WhatsApp without excluding people. ## Positioning For organizations: > Run your group professionally without forcing members onto WhatsApp. For members: > See what matters across all your groups without reading every chat. For admins: > Announcements, events, files, RSVPs, and members in one place. For anti-lock-in positioning: > Your group should not depend on one company's messenger. ## Required product flows ### Flow A: Admin creates a group and starts migration 1. Admin opens app. 2. Admin creates group with name, description, visibility, and default permissions. 3. Admin adds seed members manually or imports a simple CSV. 4. Admin creates invite link. 5. Admin gets QR-ready URL and WhatsApp reminder copy. 6. Migration dashboard tracks invited/opened/joined/verified/notification-enabled. 7. Admin can mark the WhatsApp channel as legacy and set a transition deadline. ### Flow B: Member joins without login 1. Member taps invite link from WhatsApp/email/SMS/QR. 2. Browser opens join screen. 3. Member sees group preview and official reason for moving. 4. Member enters/accepts display name. 5. Member is assigned a browser session/device. 6. Member can immediately read official updates and perform one useful action, such as RSVP. 7. Member is then nudged to save access via recovery email, passkey-ready protection, or browser notifications. ### Flow C: Multi-group home 1. Anna belongs to FC Kreuzberg U12, Class 4B Parents, Tenant Association, and Food Bank Volunteers. 2. Anna opens Home. 3. Home shows: - missing RSVPs; - open votes; - tasks assigned to Anna; - changed events; - important announcements; - catch-up summary of chatter. 4. Anna does not need to open every group chat. ### Flow D: Multi-device access 1. Anna joined on phone. 2. On PC, Anna opens the app and chooses “Link from existing device.” 3. PC displays code/QR. 4. Phone approves the pending device. 5. PC receives a session. 6. Anna sees both devices in Me → Devices. 7. Anna can revoke a device. ### Flow E: Self-hosted remote aggregation 1. Club runs `club.example` as a group server. 2. Anna's home server is `home.example` or the hosted default. 3. Club server creates a scoped connection token for Anna. 4. Anna connects `club.example` to her home server. 5. Home server syncs structured objects through `/api/sync`. 6. Anna's Home dashboard shows club actions alongside other groups. 7. No group data is replicated peer-to-peer between unrelated servers. ## Top-level navigation Use these nav items: ```text Home Calendar Groups Files Me ``` Chat must not be top-level navigation. ## Home screen details The Home screen must include the following cards/sections: ### Needs me High priority, actionable objects: - RSVP required; - vote required; - task assigned; - file requires acknowledgement; - direct mention; - admin request. Each card must show: - group name; - type badge; - due date/time if any; - primary action button; - source server when remote. ### Today / Upcoming Unified agenda with: - events; - deadlines; - changed locations/times; - RSVP status; - attached file indicators. ### Changed since last visit Examples: - event changed; - new official announcement; - task reassigned; - poll result finalized; - file uploaded. ### Official updates Announcements and admin posts, separated from discussion chatter. ### Catch up Rule-based summary in MVP: ```text While you were away: - 2 official announcements - 1 event changed - 3 open actions - 47 discussion messages ``` Do not require AI to implement catch-up. AI hooks can be future work. ## Group page details Default group dashboard sections: ```text Important now Upcoming Open actions Announcements Files Discussions Members/Admin tools ``` Example for sports team: ```text FC Kreuzberg U12 Parents - Match Saturday, RSVP open - Training moved to Pitch 2 - 3 drivers still needed - Files: Season schedule, Emergency contacts - Discussions: Snacks, Carpool ``` ## Composer behavior When creating group content, allow explicit content types: ```text Announcement Event Task Poll Question/thread File Chat message ``` MVP can implement simple forms for each type. Later AI can suggest structure from natural language; do not block MVP on that. ## Notifications Implement preferences by type, not only by group. User settings: ```text Immediate - direct mentions - event changes - urgent announcements - tasks assigned to me Digest - normal discussions - new files - photos/general chatter Muted - reactions - off-topic chatter ``` UI must make the pitch clear: > Mute the noise, not the group. ## Search and files At MVP level: - global file list; - by-group filter; - simple text search over names/descriptions; - upload/download local files; - show source server for remote files. Later enhancements can include full-text search across messages. ## Migration kit details Admin dashboard should show: ```text 31 invited 24 opened 21 joined 14 enabled notifications 4 verified recovery 7 not reached ``` Generate reminder copy, for example: ```text 23 of 31 people have joined our new group space. The match schedule, RSVP, and files are now here: {link} From {date}, official announcements will only be posted there. ``` WhatsApp export import: - accept `.txt` export; - parse basic lines if format is recognized; - store as archive items; - do not treat imported messages as active verified members; - make archive clearly read-only. ## Self-hosting and aggregation Definitions: - Group server: canonical owner of one or more groups. - Home server: stores a user's personal dashboard config and remote server connections. - Remote server connection: scoped link from a home server to a group server. - Federation: server-to-server peer network. Explicitly not included. V2 self-hosting target: - A server can be self-hosted by an organization. - A user can connect multiple servers to their home profile. - A home server aggregates structured objects. - Servers expose an open, documented, versioned sync API. - Users can export their data/config. ## Copy guidelines Avoid words that create friction: - Avoid “register” as first action. - Avoid “create account” before value. - Avoid “federation” in end-user UI. - Avoid “token” in end-user UI. Use: - “Open group” - “Join this group” - “Save access” - “Protect access” - “Link another device” - “Connect another group server” - “Get updates without WhatsApp” ## What not to build - Do not put chat as the whole product. - Do not require the native app. - Do not bridge or scrape WhatsApp. - Do not make localStorage the source of identity. - Do not force all self-hosted servers into one federation. - Do not hide important state in chronological messages. --- # FILE: ARCHITECTURE_CONTRACTS.md # ARCHITECTURE_CONTRACTS.md ## Architecture overview The same FastAPI server can operate as: ```text local group server home server both ``` The React frontend talks primarily to one selected home server. The home server stores local memberships and remote server connection configuration. It aggregates structured objects from connected remote group servers. ```text Browser/PWA | | HTTPS/API + HttpOnly session cookie v Home Server | owns home profile, devices, preferences, remote config | fetches remote structured sync using scoped server-side tokens v Remote Group Servers | own canonical groups and group data | expose /.well-known/group-platform.json and /api/sync ``` No server-to-server federation is required. Remote group servers do not know about each other. ## Browser storage policy Allowed in localStorage: - last selected home server URL; - UI preferences; - theme choice; - non-sensitive onboarding flags; - cached non-sensitive IDs for convenience. Not allowed in localStorage: - raw invite tokens after claim; - session tokens; - remote server access tokens; - recovery tokens; - admin credentials. Use HttpOnly cookies for sessions. Use server-side storage for remote connection credentials. ## Core entity relationships ```text HomeProfile 1..n HomeDevice HomeProfile 1..n RecoveryMethod HomeProfile 1..n RemoteServerConnection HomeProfile 0..n Member via Member.home_profile_id Group 1..n Member Group 1..n MemberInvite Group 1..n Announcement Group 1..n Event Event 1..n EventRsvp Group 1..n Task Group 1..n Poll Poll 1..n PollOption PollOption 1..n PollVote Group 1..n FileAsset Group 1..n Thread Thread 1..n Message Member 1..n MemberDevice Member 1..n ActionItem Member 1..n Notification RemoteServerConnection 1..n RemoteMembership RemoteServerConnection 1..n RemoteSyncCursor RemoteServerConnection 1..n RemoteCachedObject ``` A `Member` can exist without a `HomeProfile`. This is required for accountless invite-link joining. Later, a member may be attached to a home profile. ## Suggested SQLAlchemy fields Use exact names where practical. ### HomeProfile ```text id: UUID primary_display_name: string created_at: datetime updated_at: datetime last_seen_at: datetime nullable status: active/disabled ``` ### HomeDevice ```text id: UUID home_profile_id: UUID nullable member_id: UUID nullable label: string user_agent_summary: string created_at: datetime last_seen_at: datetime revoked_at: datetime nullable trust_level: invite_member/claimed_browser/verified/passkey_ready ``` ### RecoveryMethod ```text id: UUID home_profile_id: UUID kind: email/phone/recovery_code/passkey value_hash: string nullable display_hint: string verified_at: datetime nullable created_at: datetime ``` ### Session ```text id: UUID home_profile_id: UUID nullable member_id: UUID nullable home_device_id: UUID nullable member_device_id: UUID nullable csrf_token_hash: string expires_at: datetime created_at: datetime revoked_at: datetime nullable ``` ### Group ```text id: UUID server_origin: string name: string description: text visibility: private/public/listed legacy_channel_status: none/transition/legacy transition_deadline: date nullable created_at: datetime updated_at: datetime archived_at: datetime nullable ``` ### Member ```text id: UUID group_id: UUID home_profile_id: UUID nullable display_name: string role: owner/admin/moderator/member/guest status: invited/opened/joined/verified/suspended/left joined_at: datetime nullable last_seen_at: datetime nullable notification_enabled_at: datetime nullable ``` ### MemberInvite ```text id: UUID group_id: UUID member_id: UUID nullable created_by_member_id: UUID label: string scope: specific_member/open_seat/admin_invite permission_role: guest/member/admin plain_token_display_once: not persisted token_hash: string expires_at: datetime nullable max_uses: int use_count: int consumed_at: datetime nullable revoked_at: datetime nullable created_at: datetime ``` ### Announcement ```text id: UUID group_id: UUID author_member_id: UUID title: string body: text priority: normal/urgent official: bool requires_ack: bool created_at: datetime updated_at: datetime ``` ### Event ```text id: UUID group_id: UUID created_by_member_id: UUID title: string description: text nullable starts_at: datetime ends_at: datetime nullable location_name: string nullable location_address: string nullable rsvp_required: bool changed_at: datetime nullable created_at: datetime updated_at: datetime ``` ### EventRsvp ```text id: UUID event_id: UUID member_id: UUID status: yes/no/maybe/unknown note: text nullable updated_at: datetime ``` ### Task ```text id: UUID group_id: UUID created_by_member_id: UUID assigned_to_member_id: UUID nullable title: string description: text nullable due_at: datetime nullable status: open/done/cancelled created_at: datetime updated_at: datetime ``` ### Poll / PollOption / PollVote ```text Poll: id, group_id, title, description, closes_at, status, created_by_member_id, created_at PollOption: id, poll_id, label, position PollVote: id, poll_id, option_id, member_id, created_at ``` ### FileAsset ```text id: UUID group_id: UUID uploaded_by_member_id: UUID filename_original: string filename_stored: string content_type: string size_bytes: int storage_path: string visibility: members/admins/public_link created_at: datetime ``` ### Thread / Message ```text Thread: id, group_id, title, kind discussion/question/archive, created_by_member_id, created_at, updated_at Message: id, thread_id, author_member_id, body, created_at, edited_at nullable, deleted_at nullable ``` ### ActionItem ```text id: UUID home_profile_id: UUID nullable member_id: UUID nullable remote_connection_id: UUID nullable source_type: local/remote source_server_origin: string source_group_id: string source_group_name: string type: rsvp_required/vote_required/task_assigned/event_changed/file_ack/direct_mention/admin_request status: open/done/dismissed priority: low/normal/high/urgent title: string summary: text object_type: event/task/poll/file/announcement/message object_id: string due_at: datetime nullable created_at: datetime updated_at: datetime ``` ### RemoteServerConnection ```text id: UUID home_profile_id: UUID server_origin: string server_name: string api_base: string protocol_version: string capabilities_json: json access_token_encrypted: string status: active/error/revoked last_sync_at: datetime nullable last_error: text nullable created_at: datetime updated_at: datetime ``` ### RemoteCachedObject ```text id: UUID remote_connection_id: UUID object_type: action/event/announcement/file/thread remote_id: string group_remote_id: string group_name: string payload_json: json updated_at_remote: datetime nullable cached_at: datetime ``` ## API contracts Use JSON. Use ISO 8601 datetimes. Use consistent error response: ```json { "error": { "code": "permission_denied", "message": "You do not have permission to do that.", "details": {} } } ``` ### Invite preview ```http GET /api/join/{token}/preview ``` Response: ```json { "group": { "id": "...", "name": "FC Kreuzberg U12 Parents", "description": "Planning, matches, files, and announcements." }, "invite": { "label": "Parent invite", "expires_at": null, "role": "member" }, "preview": { "announcements": [], "events": [] } } ``` ### Claim invite ```http POST /api/auth/invite/{token}/claim ``` Body: ```json { "display_name": "Anna Müller", "device_label": "iPhone Safari" } ``` Response sets session cookie and returns: ```json { "member": {}, "group": {}, "next_steps": ["save_access", "enable_notifications"] } ``` ### Home dashboard ```http GET /api/home ``` Response: ```json { "profile": {}, "sections": { "needs_me": [], "today": [], "changed": [], "official_updates": [], "catch_up": [] }, "connections": [] } ``` ### Sync endpoint ```http GET /api/sync?since=cursor Authorization: Bearer ``` Response: ```json { "cursor": "next-cursor", "server_time": "2026-06-29T10:00:00Z", "actions": [], "events": [], "announcements": [], "files": [], "threads": [] } ``` ## Remote aggregation behavior The home server stores a cursor per remote connection. Sync should be idempotent. Remote cached objects should include enough payload to render in the Home dashboard without live remote calls every time. When a user performs a write action on a remote object, the MVP can open the remote group page in a new tab if write-through is not implemented. Implement write-through for RSVP if feasible. ## Derived action logic Generate local action items from structured state: - Event with `rsvp_required=true` and no RSVP by current member → `rsvp_required`. - Task assigned to current member and status `open` → `task_assigned`. - Poll open with no vote by current member → `vote_required`. - Event changed after member's last seen time → `event_changed`. - Announcement requires acknowledgement and no acknowledgement exists → `file_ack` or future `announcement_ack`. It is acceptable to recompute on request in MVP, but persist/cache for remote sync. ## Permissions contract Implement a simple helper: ```python require_role(member, group, min_role="admin") can(member, action, resource) ``` Role hierarchy: ```text guest < member < moderator < admin < owner ``` Make role checks visible in route handlers or service methods. ## Development remote-server demo Provide a way to run two local instances: ```text home server: http://localhost:8000 remote server: http://localhost:8001 ``` Options: - docker-compose service duplication; or - README command using different ports and DB paths. Seed each with distinct server names and groups. Show how to create a remote connection token on server 8001 and connect it from server 8000. ## Production limitations to document Document these limitations in README: - Dev email sends to console unless SMTP configured. - Passkey implementation may be development/pluggable if not fully implemented. - Web push may be a future adapter. - No full federation. - No end-to-end encryption. - Remote sync tokens require secure secret management in production. - File storage is local filesystem in dev; production should use S3-compatible storage or mounted volume. --- # FILE: ACCEPTANCE_TESTS.md # ACCEPTANCE_TESTS.md Codex should use this file as a checklist. Implement automated tests for as many as practical and document any manual checks. ## Smoke tests - `docker compose up --build` starts backend and frontend. - Backend health endpoint returns OK. - Frontend loads without console-breaking errors. - Seed data can be created with a documented command. ## Accountless join Automated/backend: - Creating invite stores only token hash. - Valid invite preview returns group preview. - Claiming invite creates/updates member, member device, and session. - Expired/revoked invite cannot be claimed. - Limited-use invite increments use count and blocks after max uses. Manual/frontend: - Opening invite link shows a polished mobile join screen. - User can join without email/password. - User can RSVP immediately after join. ## Structured group behavior Automated/backend: - Admin can create announcement. - Member cannot create official announcement unless permitted. - Admin can create event. - Member can RSVP to event. - Missing RSVP generates action item. - Task assigned to member generates action item. - Poll without member vote generates action item. Manual/frontend: - Group page defaults to dashboard, not raw chat. - Announcements/events/files/tasks/polls appear as cards. - Chat/discussions are available but secondary. ## Home dashboard Automated/backend: - `/api/home` returns sections for needs_me, today, changed, official_updates, catch_up. - Action ordering prioritizes urgent/due items. - Local and remote objects can be represented consistently. Manual/frontend: - Home answers “What needs me?” - Seed demo shows at least three actionable items across groups. - Remote items show source server/group badge. ## Multi-device Automated/backend: - Device link start creates pending pairing code. - Existing session can approve pending device. - New device completes pairing and receives session. - Device list includes both devices. - Revoked device cannot use session. Manual/frontend: - User can start “Link another device.” - Code/QR screen is understandable. - Existing device approval UI is clear. - Device management page shows current and linked devices. ## Recovery Automated/backend: - Recovery request creates hashed recovery token/code. - Recovery consume creates session or attaches to home profile. - Expired/revoked recovery token fails. Manual/frontend: - Dev-mode recovery shows/logs a usable link or code. - UI says “Save access”/“Recover access,” not mandatory account creation. ## Migration kit Automated/backend: - Admin can fetch migration dashboard. - Migration status reflects member invite/open/join/verified fields. - Reminder copy endpoint returns useful text with link and transition date. - WhatsApp export import accepts a `.txt` file and stores read-only archive messages if implemented. Manual/frontend: - Admin migration dashboard is clear. - Reminder copy is one-click copyable. - UI explains legacy channel concept. ## Files Automated/backend: - Upload enforces auth and file size. - Download enforces permission. - Filename is sanitized. Manual/frontend: - Files page shows global and by-group files. - File source group/server is visible. ## Remote/self-hosted aggregation Automated/backend: - `/.well-known/group-platform.json` returns manifest. - `/api/sync` requires valid scoped token. - Connection token has scopes and expiry. - Home server can connect to remote server with URL + token. - Sync stores/caches remote structured objects. - `/api/home` includes remote action items after sync. Manual/frontend: - Connected servers page lists remote server. - Sync status/errors are visible. - Remote actions appear on Home. - No UI claims full federation. ## Responsive UI Manual: - 375px-wide mobile viewport is usable. - Bottom nav appears on mobile. - Desktop layout uses more horizontal space without becoming sparse. - Forms are readable and touch targets are adequate. - Empty/loading/error states are implemented. ## Security checklist Automated or code review: - Invite/recovery tokens hashed. - Sessions use HttpOnly cookies. - Long-lived tokens not stored in localStorage. - Role checks on admin endpoints. - Remote tokens stored server-side. - Upload validation implemented. - `.env.example` exists and no secrets are committed. ## README checklist Root README must include: - product concept; - architecture diagram/text; - setup commands; - seed demo instructions; - invite flow instructions; - device-linking instructions; - remote aggregation demo instructions; - known limitations; - security notes; - next-step roadmap.