Architecture
Eleven services, REST-only boundaries, no shared databases, and why the numbers look the way they do.
Alter is eleven TypeScript services with REST-only boundaries and no shared databases. Each service also carries a hard budget of at most fifteen dependencies. A rule that ends up shaping a lot of decisions (it’s why the error-tracking client is hand-rolled and the compilation queue is plain Postgres).
| Service | What it does |
|---|---|
| alter-link | API gateway, auth (JWT + Google OAuth), billing |
| alter-mind | AI orchestration: recognition, the five modes, briefing, the provider abstraction |
| alter-memo | Captures, mode states, artifacts, chat, the Activity read model, SSE proxying |
| alter-wiki | The wiki substrate, compilation queue, briefing pipeline |
| alter-vectors | Embeddings + semantic/hybrid search (pgvector) |
| alter-data | Schema management, 111 migrations and counting |
| alter-crypto | Stateless AES-256-GCM encryption |
| alter-trace | Audit log, telemetry, privacy guards, the journey read model |
| alter-vault | S3-compatible file storage with pluggable connectors |
| alter-portal | The Next.js front-end |
| alter-service-ui | Shared dashboard components |
The stack, briefly: NestJS 11 and TypeScript 5.9 (strict, no any in new code), Next.js 15 + React 18, PostgreSQL with pgvector, Redis with Bull queues, and SSE for streaming; LLM tokens are piped as NDJSON from alter-mind through alter-memo to the browser, with one shared streaming helper reused across four modes.
Services don’t trust each other by default. Every service-to-service call carries a shared internal credential, added in a security remediation phase after an audit found the boundaries were only conventionally enforced: a REST boundary with no authentication on it is a diagram, not a wall.
Two read models sit beside the write path rather than in it. Activity, the receipt trail, is composed by alter-memo over the content-bearing tables, deliberately not over the telemetry event log, because the privacy invariant forbids content in events and a receipt without content is useless. The journey read model in alter-trace is the mirror image: derived entirely from the event spine, no content, for watching how the system behaves rather than what it holds.
For scale: ~289K lines of TypeScript across 2,171 files, about 40% of it tests, with 750 test files, 29 end-to-end paths, and roughly 1,270 commits over nine months.