Eight tiers, one supergraph

CyberOS has more than 8 distinct concerns (16 are enumerated below), but they cluster cleanly into 8 architectural tiers. Each tier is a separate deployment unit, scales independently, and exposes a stable contract to the tier above.

ClusterConcernsComponents
Persona / agent layerT1LangGraph supervisor (StateGraph + interrupt); Anthropic Skills format (47 C-suite persona workflows, hot-reload); LiteLLM client (routing core)
Frontend layerT2, T3Host shell (Vite + React 19 + Tauri desktop); module remotes (Webpack 5 + Module Federation)
API + agent surfaceT4, T5, T6Apollo Router (GraphQL Federation v2.5+); MCP Gateway (Streamable HTTP, 2025-11-25); AI Gateway (LiteLLM router, Bedrock primary)
Backend servicesT722 subgraphs (TypeScript Yoga or Rust async-graphql); 22 MCP servers (per-module, TS SDK or mcp-rs)
Data + searchT8, T9PostgreSQL 17 + pgvector HNSW + PGroonga; graph as a relational edge table (l2_edge), so any managed Postgres works; BGE-M3 embedder + BGE-rerank-v2-m3 (self-hosted)
InfrastructureT10, T11NATS JetStream (event spine); S3 / R2 / MinIO (object storage)
Cryptography + syncT12, T13, T14Yjs / Automerge (CRDTs for realtime); Ed25519 + scrypt key wrap + MMR + STH; msgspec canonical JSON + binlog framing
Compliance + UXT15, T16OPA + Conftest (policy); Trust Center (cert hosting); Be Vietnam Pro + CyberSkill design system

Three design constraints driving every pick

  1. Vietnamese data sovereignty - no SaaS dependency where Vietnamese-origin personal data must travel through a US-based vendor's servers. AWS Bedrock is acceptable because of the ap-southeast-1 region; OpenAI direct is not (no Singapore endpoint).
  2. Cost ceiling at scale - <= $150/mo LLM + $230/mo infra at 10-Member internal; <= $4/active user/mo LLM + $2,200/mo infra at 50-tenant (N(task pending), N(task pending)). Anything that does not fit that envelope is rejected.
  3. Migration door always open - every pick has a documented escape hatch. Storage is S3-compatible (DEC-005), so R2 <-> MinIO <-> AWS S3 is a config flip. SQL is portable, the audit chain is exportable, MCP servers are spec-conforming.

Tier 1 - Persona / agent layer

Pick: LangGraph (state graph) + LiteLLM (routing) + Anthropic Skills format (47 C-suite persona workflows, hot-reloadable).

LangGraph is the agentic-supervisor framework. Its StateGraph primitive models CUO as a graph of nodes (router, skill-load, tool-call, HITL-confirm, answer-compose) with first-class interrupt support for human-in-the-loop gates on destructive tools. LiteLLM is the model-routing core that owns provider failover and prompt caching. The Anthropic Skills format (SKILL.md + scripts/ + references/) keeps each C-level skill as a hot-reloadable directory.

Why these picks

Trade-offs

Production cost

<= $0 host cost (runs inside the CUO service Pod). LLM cost flows through the Tier 6 AI Gateway.

Tier 2 - Frontend host shell

Pick: Vite + React 19 + Tauri (desktop bundling).

The host shell is the thin orchestrator that hosts Module-Federation remotes. Vite (Rollup-based) for dev-server speed; React 19 for the new use hook and React Compiler's automatic memoisation; Tauri for the desktop bundling story.

Why these picks

Trade-offs

Tier 3 - Frontend remotes (per module)

Pick: Webpack 5 + Module Federation v2 per module.

Each module ships as an MF remote bundle. The host shell lazy-loads on route entry. CSS is scoped via CSS Modules to prevent cross-module collisions. Design tokens come from one published package (@cyberskill/tokens).

Why Webpack for remotes, Vite for the host?

NFR ceilings

Tier 4 - API gateway (GraphQL Federation)

Pick: Apollo Router (Rust) - Federation v2.5+ compliant.

The Rust-based Apollo Router executes the composed supergraph plan. It validates JWTs, attaches tenant + actor context, runs the persisted-query lookup, and dispatches the subgraph fanout in parallel. Detailed on the Infrastructure page.

Why Apollo Router, not Mesh / Hasura / Yoga?

Trade-offs

Tier 5 - MCP Gateway (per-module servers)

Pick: per-module MCP servers + federation router; 2025-11-25 spec.

Each module owns its MCP server. The gateway is a federation router, not a monolith. Streamable HTTP, OAuth-PRM, well-known discovery, tool annotations. Detailed on the Infrastructure page.

SDK choice

Spec version policy

Tier 6 - AI Gateway (LiteLLM router)

Pick: LiteLLM with a CyberOS middleware overlay.

One gateway, one cost ledger, one residency policy. Routes between Bedrock primary (Sonnet 4.6, Haiku 4.5), Anthropic ZDR fallback, OpenAI ZDR fallback. Detailed on the Infrastructure page.

Why these providers

Cost ceiling

Tier 7 - Backend services (per-module subgraphs)

Pick: TypeScript (GraphQL Yoga) or Rust (async-graphql) per subgraph; the choice is owned by the module owner.

Most subgraphs are TypeScript for developer ergonomics. The two performance-critical modules - memory (memory writer hot path) and Skill (Wasmtime runtime, capability broker) - are Rust. Module owners pick per module; the contract (Apollo Federation SDL) is identical regardless.

TypeScript stack (default)

Rust stack (perf-critical)

Tier 8 - Data layer (Postgres + extensions)

Pick: PostgreSQL 17 + pgvector HNSW + PGroonga, with graph traversal on a plain relational edge table.

One Postgres database per region, with extensions stacked: pgvector for vector search (HNSW index) and PGroonga for Vietnamese-tokenised lexical search. Graph traversal runs on a relational adjacency table (l2_edge) rather than the Apache AGE extension - so CyberOS runs on any managed Postgres (RDS, Cloud SQL, Supabase, Neon) with no custom-extension requirement. Per-module schema isolation; RLS on every tenant-keyed table.

Why Postgres + pgvector vs a separate vector DB?

RLS posture

Tier 9 - Search and embeddings (self-hosted)

Pick: BAAI/bge-m3 (embedder) + BAAI/bge-reranker-v2-m3 (reranker), self-hosted on one shared GPU node.

BGE-M3 produces 1024-dim dense + sparse + multi-vector embeddings in one pass. Multilingual native (top MIRACL Vietnamese scores). The reranker re-orders the top-150 hits to a top-20 using cross-encoder scoring. Both run on a single shared GPU node (Hetzner CCX23 + RTX A4000 or similar; ~$80/mo).

Why self-hosted vs OpenAI text-embedding-3-large?

NFR ceilings

Tier 10 - Event bus (NATS JetStream)

Pick: NATS Server 2.10+ with JetStream durable consumers.

Detailed on the Infrastructure page. The choice is driven by the subject-hierarchy-native fit (cyberos.{tenant}.{module}.{entity}.{verb}), sub-millisecond latency, and the single-binary operational footprint.

Why NATS, not Kafka / Redpanda?

See the alternatives tables below for the full comparison.

Tier 11 - Object storage (S3-compatible)

Pick: S3-compatible - Cloudflare R2 (zero-egress) or MinIO (self-host) per environment.

DEC-005 locks the choice to the S3-compatible protocol, not a specific vendor. Production internal uses Cloudflare R2 (zero egress fee, global CDN). Self-hosted demo / on-prem tenants use MinIO. Migration is a config flip.

Tier 12 - Realtime sync (CRDTs)

Pick: Yjs (CHAT, collaborative docs) + Automerge (offline-first complex models).

Yjs is the production-grade CRDT lib for text + lists (rich-text CHAT messages, KB docs). Automerge owns the offline-first model surface for clients that need to edit while disconnected (Tauri desktop <-> web). Both speak similar BinaryDoc formats; conversion when needed.

Where they are used

Tier 13 - Cryptography

Pick: Ed25519 signatures + scrypt key-wrap + Merkle Mountain Range (MMR) + Signed Tree Heads (STH).

memory's audit ledger uses an MMR for additive inclusion proofs. Each consolidation cycle signs a Tree Head with Ed25519. Signing keys are passphrase-wrapped via scrypt (P2 Stage 2). Detailed on the memory module page.

Primitive choice

Where used

Tier 14 - Audit ledger encoding

Pick: msgspec canonical-JSON + binlog framing (length + CRC32C + seq + ts + payload).

msgspec (Python; mirrored in Rust via custom serde) produces deterministic JSON (sorted keys, UTF-8 NFC, no insignificant whitespace), meeting RFC 8785 JCS. The binary frame header makes the ledger durable under partial-write conditions.

Format spec (Memory AGENTS.md section 6.2)

# Each ledger record frame:
[u32 length BE][u32 crc32c BE][u64 seq BE][u64 ts_ns BE][payload]

# Payload = msgspec canonical JSON of:
{
  "seq": 12345,
  "ts_ns": 1715683200000000000,
  "tenant": "cyberskill",
  "actor": "member:trinh",
  "op": "put",
  "path": "memories/decisions/...",
  "body_hash": "sha256:abc...",
  "prev_chain": "sha256:xyz...",
  "chain": "sha256:def..."  # SHA-256(canonical(record_minus_chain) || prev_chain)
}

Tier 15 - Compliance tooling

Pick: OPA (Open Policy Agent) + Conftest + Trust Center (static site).

OPA enforces Rego policies across Kubernetes manifests, GraphQL operation directives, and IAM transitions. Conftest runs OPA in CI for declarative-file validation. The Trust Center is a static site (Astro + MDX) hosting VPAT, SOC 2, ISO 27001, and CSA STAR docs.

What OPA enforces

Trust Center stack

Tier 16 - Typography and design tokens

Pick: Be Vietnam Pro (UI) + JetBrains Mono (code) + CyberSkill Global Design System v1.0.0.

Be Vietnam Pro is the diacritic-aware, Vietnamese-first typeface; Design System Part 5 specifies stack-fidelity (N(task pending)). Tokens are exported in W3C DTCG format (2025.10) for cross-platform consumption (Style Dictionary, Tailwind via a PostCSS plugin, iOS/Android, Figma).

Token surface

"What calls what" - dependency graph

The tiers compose left-to-right: every request from a user or agent flows through this graph. Cycles are forbidden by design.

One request traverses every tier:

  1. The user opens a route; the T2 host shell lazy-loads the T3 module remote.
  2. The remote posts a persisted-query hash to the T4 Apollo Router, which builds the federated query plan and dispatches it to the T7 subgraph (Bun/Tokio).
  3. The subgraph SELECTs from T8 Postgres (RLS-scoped), calls the T6 AI Gateway (POST /v1/embeddings), gets the vector from T9 BGE-M3 (GPU), and runs the pgvector SELECT for hits.
  4. The subgraph publishes an event to T10 NATS, writes any attachment to T11 R2, and appends an audit row (msgspec canonical) to the T14 ledger.
  5. The response composes back through the Router to the remote; the host shell paints.

The same graph in prose: users and agents reach the host shell (Vite + React 19), which loads module remotes (Webpack 5 + MF); remotes call the Apollo Router, which validates against AUTH JWKS and fans out to subgraphs (TS/Rust); subgraphs use Postgres 17 + extensions, the AI Gateway (LiteLLM -> AWS Bedrock / Anthropic ZDR / OpenAI ZDR / self-hosted BGE-M3), NATS JetStream, and R2 / MinIO. MCP clients (Claude / Cursor) reach the same subgraphs through the MCP Gateway, which also authenticates via AUTH. Subgraphs, the Router, the AI Gateway, and the MCP Gateway all trace to OBS (OTel).

Cost-vs-tier model

Two reference scales - 10 Members internal (P0-P2) and 50 tenants (P4 GA). Each tier's contribution maps to a hard NFR ceiling.

Where the dollar goes at internal scale (the $535/mo N(task pending) envelope):

ShareLine item$/mo
28%LLM (primarily Sonnet + Haiku via Bedrock)$150
17%K8s compute (22 subgraphs + gateways)$90
15%Postgres (primary + read replica)$80
15%OBS (LGTM: Loki + Tempo + Mimir + Grafana)$80
15%GPU embed (shared BGE-M3 node)$80
5%Object storage (R2 zero-egress)$25
4%NATS (single-node JetStream)$20
1%CDN + auth$10

Internal scale (10 Members): total <= $530/mo against the N(task pending) budget of $530/mo ($150 LLM + $380 infra). 50-tenant scale: LLM $800, Postgres (3 regions) $600, compute (k8s) $500, storage $200, OBS $200, NATS (cluster) $100, AUTH $50, GPU embed $200 - total <= $2,650/mo against the N(task pending) budget of $2,200/mo + $4/user/mo LLM.

Per-tier production cost (P1 exit, P4 mid projections)

TierPickInternal (10 Members)50-tenant scaleMigration door
T1 Persona / agentLangGraph + LiteLLM$0 host$0 hostReplace the LangGraph supervisor
T2 Host shellVite + React 19 + Tauri$5/mo CDN$50/mo CDNSwitch host to Next.js
T3 Module remotesWebpack 5 + MFincludedincludedPin the MF v2 spec
T4 Apollo RouterApollo Router$0 (OSS binary)$50/mo VM clusterElastic License v1.2 review
T5 MCP GatewayCustom router + per-module servers$0 (in-cluster)$30/moMCP spec preserves portability
T6 AI GatewayLiteLLM + Bedrock primary$150/mo$800/mo (+ per-user)Provider mix via config
T7 BackendBun / Tokio; 22 subgraphs$90/mo k8s$500/mo k8sContainers, portable
T8 DataPostgres 17 + pgvector + PGroonga (graph on l2_edge)$80/mo$600/mo (3 regions)SQL portable
T9 EmbeddingsBGE-M3 + reranker (GPU)$80/mo$200/mo (multi-GPU)Switch to OpenAI text-embed
T10 Event busNATS JetStream$20/mo VM$100/mo clusterNATS subjects -> Kafka topics
T11 Object storageR2 / MinIO$25/mo$200/moS3-compatible config flip
T12 CRDT syncYjs / Automerge$0 (libs)$0 (libs)Doc-format-portable
T13-14 Cryptography + ledgerEd25519 + MMR + msgspec$0$0Schema-portable
T15 ComplianceOPA + Trust Center$5/mo static host$20/moOPA Rego portable
OBS (LGTM)Grafana / Loki / Tempo / Mimir$80/mo$200/moOTel-native; switch backend
Total-<= $535/mo~$2,750/mo-

Alternatives considered (per major pick)

Five major architectural picks deserve an explicit alternatives table. Each rejected option has a documented rejection rationale and a "would reconsider when..." trigger.

Postgres + pgvector - vs a separate vector DB

OptionProsConsStatus
Postgres + pgvector HNSWOne DB; transactional embed-writes; structural joins; cheap; VN tokenisation via PGroongaOperational complexity (more extensions); ~10% slower than a dedicated vector DB at very large scaleSELECTED
PineconeBest-in-class recall; managed; horizontal scalingVendor lock; egress fees; no Singapore region; not transactional with the source of truthRejected - sovereignty + cost
WeaviateOSS; multi-modal; GraphQL nativeMemory-heavy; Janus runtime; embedded mode not prod-grade for 1M+ chunksRejected - operational cost
QdrantRust-native; fast; OSSTwo-system writes still required; smaller communityReconsider if pgvector p95 fails N(task pending)
Milvus / ZillizScales to billions; cloud-nativeK8s-heavy ops; same two-system issueOut of scope for 10-50 tenant scale

Apollo Federation v2 - vs REST / gRPC / single GraphQL

OptionProsConsStatus
Apollo Federation v2.5+Per-module subgraph ownership; single agent surface; persisted query budget; query plan cacheApollo Router Elastic License (non-OSI); Rust expertise to extendSELECTED
REST per moduleUniversal; cacheableN round-trips per page; no agent-friendly introspection; N OpenAPIs to maintainRejected - agent ergonomics
gRPC + ConnectRPCStrongly typed; fast; protobuf schemaBrowser story still weak; no native cross-subgraph composition; agents don't speak gRPC nativelyRejected - frontend friction
Single monolithic GraphQLSingle schemaMerge conflicts every PR; team coupling; deploy couplingRejected - team scale
tRPCExcellent DX; TS end-to-endTS-only; no agent-facing surface; per-module schemas don't composeRejected - language lock

LangGraph - vs DSPy / native LangChain / Semantic Kernel

OptionProsConsStatus
LangGraphStateGraph native; interrupt HITL; checkpointer for resumability; LangSmith tracingPython-only; ties to the LangChain ecosystemSELECTED
DSPyOptimisation-first; auto-promptingNot a router framework; long-lived agent loop awkwardReconsider for batch evals only
Native LangChain agentsMature; vast tool catalogImperative loop; hard to audit; HITL via callbacks is brittleRejected - auditability
Semantic KernelC# / Python native; Microsoft-backedSmaller community; MS ecosystem biasRejected - community size
CrewAIMulti-agent ergonomicsLess mature; HITL gates not first-classWatching

NATS JetStream - vs Kafka / Redpanda

OptionProsConsStatus
NATS JetStreamSubject hierarchy native; sub-ms latency; single 50 MB binary; runs on a $20/mo VM at internal scaleSmaller community than Kafka; less tooling around DLQ replaySELECTED
Apache KafkaIndustry standard; massive tooling; Confluent SaaS availableJVM ops complexity; flat topics; Zookeeper/Kraft cluster mandatory; expensive at 10-Member scaleRejected - footprint
RedpandaKafka-protocol-compatible; Rust; lower ops costSame flat-topic model; less mature than KafkaReconsider if Kafka tooling needed
AWS SQS / EventBridgeManaged; pay-per-messageVendor lock; no subject hierarchy; latency 30-100 ms typicalRejected - sovereignty
Apache PulsarMulti-tenant native; geo-replicationOperational complexity (BookKeeper); overkill at this scaleOut of scope

Tauri - vs Electron / Wails / native

OptionProsConsStatus
Tauri 23-10 MB bundle (vs Electron's 100+); Rust backend; OS webview; passes Apple notarisation by defaultOS-native webview means a CSS testing matrix; a Rust IPC layer to learnSELECTED
ElectronMature; Chrome consistency; vast plugin ecosystem100+ MB bundle; memory-hungry; security-update treadmillRejected - bundle size
Wails (Go backend)Go single-binary feel; webview-basedSmaller community; v3 still maturingReconsider
Native (Swift / WinUI / GTK)Best UX; smallest bundles3x the code; can't reuse React componentsRejected - scope
Web-only (PWA)No native shipNo file-system access; no native notification UXReconsider at the P3 mobile evaluation

Changelog

History lives in the changelog; this page describes only the current state.


Generated from docs/architecture/tech-stack.md — edit the markdown source, not this file (TASK-DOCS-002).