# Capacity and cost model

This is the §8.1 specification for node sizing and monthly cost. Terraform is
still a scaffold (OD-1 open), so these numbers are planning targets, not
measured bills. Revisit when OD-1 and OD-6 have staging numbers.

## Performance budgets (ARCHITECTURE §6.1)

| Metric | Budget |
|---|---|
| Send → visible on second client (p95, in-region) | < 250 ms |
| Channel switch, warm cache | < 100 ms |
| Scrollback page of 50 (p95 server time) | < 120 ms |
| Cold app load to interactive (p95) | < 2.5 s |
| Sockets per gateway node (2 vCPU / 4 GB, Node + Socket.IO) | ≥ 8 000 |
| Event-loop lag under full load (p99) | < 50 ms |
| Reconnect storm: 5 000 clients fully caught up | < 30 s, zero I1–I9 violations |

## Node layout

| Process | Role | Sizing rule |
|---|---|---|
| `apps/api` | HTTP, writes, pg-boss producer | 2 vCPU / 4 GB; scale on p95 write latency, not sockets |
| `apps/gateway` | Socket.IO + Redis adapter | **one process per core**; 8k sockets/node target (OD-6) |
| `apps/worker` | pg-boss consumers (push, search drain, email) | 1 vCPU / 2 GB until queues back up |
| Postgres 16 | system of record, RLS, FTS | start 4 vCPU / 16 GB / 200 GB gp3; `channel_seq` lock waits from Phase 2 |
| Redis 7 | pub/sub adapter, presence sets, typing NX | 2 GB is enough at 5k concurrent; presence is device-count sets not pub/sub volume |
| Typesense | optional; Postgres FTS is live (ADR-007) | not sized until indexed |
| S3 / MinIO | files | growth ≈ 50 MB/user/month until retention jobs exist |
| LiveKit | media SFU | separate from chat nodes; TURN for NAT |

At 5 000 concurrent per region: **one gateway + one redundant**, one api, one
worker, one Postgres primary. Event-loop lag is the gateway health signal.

## Concurrent users → sockets

Assume 1.2 sockets/user (desktop + tab).

| Users | Concurrent (~20%) | Sockets | Gateway nodes @ 8k |
|---|---|---|---|
| 100 | 20 | ~24 | 1+1 |
| 1 000 | 200 | ~240 | 1+1 |
| 10 000 | 2 000 | ~2 400 | 1+1 |
| 40 000 | 8 000 | ~9 600 | 2+1 |

OD-6 must confirm the 8k/node figure before the 10k-user row is a promise.

## Monthly cost (order of magnitude, single region, list-ish cloud)

| Users | Compute | Postgres | Redis | Object storage | LiveKit/TURN | **Total** |
|---|---|---|---|---|---|---|
| 100 | ~$80 | ~$50 | ~$15 | ~$5 | ~$40 | **~$190** |
| 1 000 | ~$200 | ~$120 | ~$30 | ~$40 | ~$150 | **~$540** |
| 10 000 | ~$600 | ~$400 | ~$80 | ~$400 | ~$800 | **~$2.3k** |

These are not quotes. Bandwidth (WebRTC) dominates once calls are in daily
use; chat text is cheap. Linux PWA Web Push is cents. Signed desktop updates
are storage + CDN of artefacts, not a per-seat fee.

## Storage growth

Messages: ~2 KB/message including jsonb blocks. 1 000 users × 50 msgs/day ×
30 days ≈ 3 GB/month before indexes and FTS. Files dominate. Retention_days
on the workspace is the control; the `retention` pg-boss queue exists but is
not yet consumed.

## What is still unmeasured

- OD-1: real cloud SKUs in Terraform
- OD-6: sockets/node and event-loop lag at 8k
- Typesense if we dual-run it
- LiveKit recording egress (S3) per-hour cost
