# Backend Architecture This document describes the Happy backend structure as implemented in `packages/happy-server`. It focuses on how the server is wired, how data flows through the system, and which subsystems handle which responsibilities. ## System overview ```mermaid graph TB subgraph Clients CLI[CLI Client] Mobile[Mobile App] Daemon[Machine Daemon] end subgraph "Happy Server" API[Fastify API] Socket[Socket.IO] Events[Event Router] end subgraph Storage PG[(Postgres)] Redis[(Redis)] S3[(S3/MinIO)] end CLI --> API Mobile --> API Daemon --> API CLI --> Socket Mobile --> Socket Daemon --> Socket API --> PG API --> S3 Socket --> Events Events --> Redis Events --> PG ``` ## At a glance - Runtime: Node.js + Fastify for HTTP, Socket.IO for realtime. - Database: Postgres via Prisma. - Cache/bus: Redis client is initialized (currently only pinged). - Blob storage: S3-compatible (MinIO) for uploaded assets. - Crypto: privacy-kit for auth tokens and encrypted service tokens. - Metrics: Prometheus-style `/metrics` server + per-request HTTP metrics. ## Process lifecycle Entry point: `packages/happy-server/sources/main.ts`. ```mermaid flowchart TD Start([main.ts]) --> DB[Connect Postgres] DB --> Cache[Init Activity Cache] Cache --> Redis[Redis ping] Redis --> Crypto[Init Crypto Modules] subgraph Crypto Initialization Crypto --> Encrypt[initEncrypt - KeyTree] Crypto --> GitHub[initGithub - OAuth/Webhooks] Crypto --> S3[loadFiles - S3 Bucket] Crypto --> Auth[auth.init - Token Gen] end Encrypt & GitHub & S3 & Auth --> Servers[Start Servers] subgraph Server Startup Servers --> API[API Server] Servers --> Metrics[Metrics Server] Servers --> DBMetrics[DB Metrics Updater] Servers --> Presence[Presence Timeout Loop] end API & Metrics & DBMetrics & Presence --> Running([Running]) Running --> |SIGTERM| Shutdown[Shutdown Hooks] Shutdown --> DBDisconnect[DB Disconnect] Shutdown --> FlushCache[Flush Activity Cache] ``` Startup sequence: 1. Connect Postgres (`db.$connect()`). 2. Init activity cache (presence) and Redis connection check (`redis.ping()`). 3. Initialize crypto modules: - `initEncrypt()` derives a KeyTree from `HANDY_MASTER_SECRET`. - `initGithub()` configures GitHub App/webhooks if env vars exist. - `loadFiles()` verifies S3 bucket access. - `auth.init()` prepares token generator/verifier. 4. Start API server (`startApi()`), metrics server, database metrics updater, and presence timeout loop. 5. Remain alive until shutdown signal. Shutdown hooks are registered for DB disconnect and activity-cache flush. ## API layer `startApi()` in `sources/app/api/api.ts` wires the HTTP server: - Fastify instance with Zod validators/serializers. - Global hooks for monitoring and error handling. - `authenticate` decorator that verifies Bearer tokens. - Route modules under `sources/app/api/routes`. - Socket.IO server attached at `/v1/updates`. ```mermaid graph LR subgraph "Fastify Server" Hooks[Global Hooks] Auth[authenticate decorator] subgraph Routes direction TB R1[authRoutes] R2[sessionRoutes] R3[machinesRoutes] R4[artifactsRoutes] R5[accessKeysRoutes] R6[kvRoutes] R7[accountRoutes] R8[userRoutes / feedRoutes] R9[pushRoutes] R10[connectRoutes / voiceRoutes] end end SocketIO[Socket.IO /v1/updates] Client --> Hooks --> Auth --> Routes Client --> SocketIO ``` HTTP routes are organized by domain: - Auth (`authRoutes`) - Sessions + messages (`sessionRoutes`) - Machines (`machinesRoutes`) - Artifacts (`artifactsRoutes`) - Access keys (`accessKeysRoutes`) - Key-value store (`kvRoutes`) - Account + usage (`accountRoutes`) - Social + feed (`userRoutes`, `feedRoutes`) - Push tokens (`pushRoutes`) - Integrations (`connectRoutes`, `voiceRoutes`) - Version checks (`versionRoutes`) - Dev-only logging (`devRoutes`) ## Authentication and tokens ```mermaid sequenceDiagram participant Client participant Server participant DB as Postgres participant Cache as Token Cache Client->>Server: POST /v1/auth (signed challenge + public key) Server->>DB: Upsert account by public key DB-->>Server: Account record Server->>Server: Generate Bearer token (privacy-kit) Server->>Cache: Cache token Server-->>Client: Bearer token Note over Client,Cache: Subsequent requests Client->>Server: Request + Bearer token Server->>Cache: Verify token Cache-->>Server: Valid / Account ID Server-->>Client: Response ``` The backend does not store passwords. Instead: - Clients authenticate with a signed challenge (`/v1/auth`) using a public key. - The server upserts the account by public key and returns a Bearer token. - Tokens are generated and verified by privacy-kit using `HANDY_MASTER_SECRET`. - Tokens are cached in-memory for fast verification. GitHub OAuth uses short-lived "ephemeral" tokens to protect the callback and is separate from normal auth. ## Realtime sync architecture ```mermaid graph TB subgraph Connections U1[User Client 1] U2[User Client 2] S1[Session Client] M1[Machine Daemon] end subgraph "Socket.IO Server" Router[Event Router] subgraph Scopes US[user-scoped] SS[session-scoped] MS[machine-scoped] end end U1 & U2 --> US S1 --> SS M1 --> MS US & SS & MS --> Router Router --> |persistent update| DB[(Postgres)] Router --> |ephemeral event| Clients((Filtered Recipients)) ``` ### Connection types Socket.IO connections are tagged by scope: - `user-scoped`: receive all user updates. - `session-scoped`: receive updates only for one session. - `machine-scoped`: daemon connections for machine state. ### Event router `EventRouter` (`sources/app/events/eventRouter.ts`) maintains per-user connection sets and routes: - **Persistent `update` events**: database-backed changes with a user-level monotonic `seq`. - **Ephemeral events**: presence/usage signals that are not persisted. The router implements recipient filters so updates go only to interested connections (e.g., all session listeners or a specific machine). ### Update sequence numbers - `Account.seq` is the per-user update counter. It is incremented by `allocateUserSeq` and used as `UpdatePayload.seq`. - Sessions and artifacts maintain their own `seq` for per-object ordering. ## Presence and activity ```mermaid flowchart LR subgraph "High Frequency" Events[session-alive / machine-alive] Cache[Activity Cache] end subgraph "Batched Writes" Batch[Batch Processor] DB[(Postgres)] end subgraph "Timeout Loop" Timer[10 min timer] Offline[Mark Inactive] Emit[Emit offline update] end Events --> |debounce| Cache Cache --> |batch| Batch --> DB Timer --> Cache Cache --> |stale entries| Offline --> DB Offline --> Emit ``` Presence is handled in `sources/app/presence`: - `session-alive` and `machine-alive` events are debounced in memory (ActivityCache). - Database writes are batched to reduce write load. - A timeout loop marks sessions/machines inactive after 10 minutes of silence and emits an offline ephemeral update. This splits high-frequency presence from durable storage updates. ## Storage and persistence ### Database (Prisma) Prisma models live in `prisma/schema.prisma`. Key tables: ```mermaid erDiagram Account ||--o{ Session : owns Account ||--o{ Machine : owns Account ||--o{ Artifact : owns Account ||--o{ UserKVStore : owns Account ||--o{ UsageReport : tracks Account ||--o{ UserRelationship : has Account ||--o{ UserFeedItem : receives Session ||--o{ SessionMessage : contains Session ||--o{ AccessKey : grants Machine ||--o{ AccessKey : receives Account { string publicKey string profile int seq } Session { string metadata int seq } Machine { string metadata string daemonState } Artifact { string header bytes body string key } ``` - `Account`: public key identity, profile, settings, seq counters. - `Session` + `SessionMessage`: encrypted session metadata and message blobs. - `Machine`: encrypted machine metadata + daemon state. - `Artifact`: encrypted header/body + per-artifact key. - `AccessKey`: encrypted per-session-per-machine access keys. - `UserKVStore`: encrypted values with optimistic versions. - `UsageReport`: usage aggregation per session/key. - `UserRelationship` + `UserFeedItem`: social graph and feed. ### Transactions and retries ```mermaid flowchart TD Start([inTx call]) --> Begin[Begin Transaction] Begin --> |Serializable| Exec[Execute Operations] Exec --> Commit{Commit} Commit --> |Success| After[afterTx callbacks] After --> Emit[Emit Socket Updates] Emit --> Done([Complete]) Commit --> |P2034 Error| Retry{Retry?} Retry --> |Yes| Begin Retry --> |Max retries| Fail([Throw Error]) ``` `inTx()` wraps Prisma transactions with: - Serializable isolation. - Automatic retry on `P2034` (serialization failures). - `afterTx()` to emit socket updates after commit. This pattern is used for multi-write operations like batch KV mutation and session deletion. ### Blob storage (S3/MinIO) The server uses S3-compatible storage for user assets (e.g., avatars): - `storage/files.ts` configures the S3 client. - `uploadImage` processes and stores files and writes metadata to `UploadedFile`. - Public URLs are derived from `S3_PUBLIC_URL`. ### Redis A Redis client is initialized in `main.ts` and pinged at startup. It can be expanded for caching or pub/sub if needed. ## Data confidentiality model ```mermaid graph TB subgraph "Client-side Encryption" C1[Session metadata] C2[Agent state] C3[Daemon state] C4[Message content] C5[Artifacts] C6[KV values] end subgraph "Server-side Encryption" S1[GitHub OAuth tokens] S2[OpenAI tokens] S3[Anthropic tokens] S4[Gemini tokens] end C1 & C2 & C3 & C4 & C5 & C6 --> |opaque blobs| DB[(Postgres)] S1 & S2 & S3 & S4 --> |KeyTree from HANDY_MASTER_SECRET| DB style C1 fill:#e1f5fe style C2 fill:#e1f5fe style C3 fill:#e1f5fe style C4 fill:#e1f5fe style C5 fill:#e1f5fe style C6 fill:#e1f5fe style S1 fill:#fff3e0 style S2 fill:#fff3e0 style S3 fill:#fff3e0 style S4 fill:#fff3e0 ``` - Session metadata, agent state, daemon state, and message content are stored as opaque encrypted strings or blobs. - Artifacts and KV values are stored encrypted and encoded as base64 on the wire. - The server only encrypts/decrypts **service tokens** (GitHub OAuth tokens, vendor tokens) using the KeyTree derived from `HANDY_MASTER_SECRET`. ## Integrations - **GitHub**: OAuth connect + webhook verification, optional if env vars are set. - **AI vendors**: encrypted token storage for `openai`, `anthropic`, `gemini`. - **Voice**: RevenueCat subscription check + ElevenLabs token minting. - **Push tokens**: stored for later notification delivery. ## Observability - `/health` route checks DB connectivity. - Metrics server exposes `/metrics` for Prometheus. - HTTP request counters and duration histograms are captured via Fastify hooks. - WebSocket event counters and connection gauges are in `metrics2.ts`. ## Key implementation references - Entrypoint: `packages/happy-server/sources/main.ts` - API server: `packages/happy-server/sources/app/api/api.ts` - Socket server: `packages/happy-server/sources/app/api/socket.ts` - Event routing: `packages/happy-server/sources/app/events/eventRouter.ts` - Presence: `packages/happy-server/sources/app/presence` - Storage: `packages/happy-server/sources/storage` - Prisma schema: `packages/happy-server/prisma/schema.prisma`