Architecture
What's shared, what's per-user, what's per-tenant, and how that maps to Docker containers in self-host vs hosted modes.
The layered model and container topology. Contributions from 2026-04-28 onward respect these layers.
Two deployment modes
| Self-host | Hosted-multitenant | |
|---|---|---|
| Who runs it | The user, on their own machine or VPS | Liz, on humancensys.com |
| Auth | None (single user) | OAuth (provider TBD) |
| Data isolation | Trivial (one user) | tenant_id everywhere |
| Cost model | User pays compute + their own API keys | Bring-your-own API keys; Liz pays infra |
| Same codebase | Yes — PLATFORM_MODE env var selects mode | Same |
The five layers
LAYER 1: Platform code (always shared, open-source)
├─ platform/api/ FastAPI, deepagents runtime, tools
├─ web/ Next.js UI shell
├─ skills/ curated skill library (read-only common)
└─ subagents/ template subagents
LAYER 2: Tenant identity & isolation (mode-dependent)
├─ Self-host: tenant_id = "default"; no auth code path runs
└─ Hosted: tenant_id from verified OAuth token
LAYER 3: Tenant configuration (per-tenant, user-editable)
├─ AGENTS.md orchestrator persona
├─ deepagents.toml model + skill paths
├─ Their subagents personalized clan
└─ Their skill choices what's loaded into their agent
LAYER 4: Tenant data (per-tenant, isolated, never shared without explicit publish)
├─ Conversations (LangGraph checkpoints in postgres, scoped by thread)
├─ Semantic memory (LanceDB records, tenant-scoped)
├─ Roadmap (their ROADMAP.md)
├─ Knowledge graph (their canonical nodes — when 3c lands)
└─ Promotion log (their skill→tool history)
LAYER 5: Publishable content (opt-in shared)
├─ Skills they author and publish
├─ Agents they share
└─ Knowledge graph contributions to a public commons (when 3c lands)Container topology
Three options:
Option X — One Docker stack, multi-tenant via tenant_id
┌─ ONE Docker stack on Liz's hosted infra ──────────────┐
│ │
│ FastAPI (single) ← all tenants hit this │
│ Postgres (single) ← rows scoped by tenant_id │
│ LanceDB (single dir) ← table per tenant │
│ Grafana (single) ← dashboards per tenant │
│ │
└────────────────────────────────────────────────────────┘Pros: cheap, simple, one thing to operate. Cons: noisy-neighbor risk, a single bug can leak data, less user control.
Option Y — Per-user Docker stack (each user = their own deployment)
User A: ┌─ Docker stack ──┐ User B: ┌─ Docker stack ──┐
│ FastAPI │ │ FastAPI │
│ Postgres │ │ Postgres │
│ LanceDB │ │ LanceDB │
│ Grafana │ │ Grafana │
└─────────────────┘ └─────────────────┘
their machine OR their machine OR
their Render service their Render servicePros: complete data isolation, user owns the infra, simpler legal/regulatory posture. Cons: more cost if Liz hosts (one Render service per user), harder to operate at scale, no cross-tenant features.
Option Z — Hybrid: shared platform, per-user data containers
┌─ Shared Docker (Liz operates) ─────────────────────────┐
│ FastAPI gateway · auth · public docs · public skills │
└──────────────┬─────────────────────────────────────────┘
│ routes per-tenant requests to:
│
┌───────────▼──────────┐ ┌──────────────────────┐
│ User A's data pod │ │ User B's data pod │
│ Postgres + LanceDB │ │ Postgres + LanceDB │
└──────────────────────┘ └──────────────────────┘Pros: shared platform code with isolated user data. Cons: highest operational complexity; requires per-user containers and a routing layer.
Today vs planned
| Mode | Topology today | Planned |
|---|---|---|
| Self-host | Option X with tenant_id="default" — one Docker stack on the user's machine, single user. | Same. |
| Hosted (humancensys.com) | Single-stack with UI on Vercel; agent backend on Render. No multi-tenancy yet. | Choice: X (shared with tenant_id), Y (per-user instances), or Z (hybrid). |
Engine, commons, personal toolkit
A three-tier model:
┌─ ENGINE — the larger organizational thing (shared by everyone) ──────────┐
│ │
│ Platform code · auth · public docs · runtime infrastructure │
│ The FastAPI app, the deepagents runtime, LanceDB engine, Postgres │
│ │
└──────────────────────────┬────────────────────────────────────────────────┘
│
┌──────────────────┼──────────────────────────────┐
│ │ │
┌───────▼────────┐ ┌──────▼──────────┐ ┌────────────────▼───────────┐
│ COMMONS │ │ PERSONAL │ │ PERSONAL │
│ (shared by │ │ TOOLKIT (User A)│ │ TOOLKIT (User B) │
│ everyone) │ │ │ │ │
│ │ │ Their clan │ │ Their clan │
│ Public skills │ │ Their convos │ │ Their convos │
│ Public agents │ │ Their memory │ │ Their memory │
│ Public knowledge graph (opt-in publish)│ Their roadmap │
│ Public templates│ │ Their KG canon │ │ Their KG canon │
└────────────────┘ └─────────────────┘ └─────────────────────────────┘Engine: the shared runtime. FastAPI, deepagents, LanceDB, Grafana — same code for every user.
Commons: the shared content layer. Public skills, public agent templates, opt-in public knowledge contributions.
Personal toolkit: per-user data. Conversations, memory, clan, roadmap, private knowledge. Never shared with another user without explicit publish.
Recommendation: Option X with explicit commons
A single shared Docker stack (the engine) with tenant_id partitioning all user-private data. The commons is data marked public = true (or in a separate namespace) that any tenant can read.
| Layer | Container topology | Data isolation |
|---|---|---|
| Engine | One shared Docker stack (FastAPI + Postgres + LanceDB + Grafana) | n/a — code |
| Commons | Shared services | Public namespace; readable by everyone; publish requires user consent |
| Personal toolkit | Shared services | tenant_id-scoped on every query |
Tradeoffs against the alternatives:
- Option Y (per-user Docker stacks) loses the commons. With every user in an isolated container, there is no shared knowledge graph or cross-user collaboration.
- Option Z (hybrid) adds operational complexity. Justified when per-tenant data volumes outgrow shared infra, which is not the case here.
- Option X matches the pattern used by most multi-tenant platforms (GitHub, HuggingFace, Notion, Linear).
Open work (Pillar 0)
For Option X in hosted mode, the foundational work in ROADMAP.md Pillar 0:
- Tenant abstraction —
tenant_idcolumn on every personal-toolkit table, scoped queries everywhere, isolation tests - Auth interface —
NoAuthBackend(self-host) andOAuthBackend(hosted) - Config loader abstraction — filesystem (self-host) ↔ multi-tenant config store (hosted)
PLATFORM_MODEenv var — single switch determines auth backend- Commons table partitioning — public namespace for skills, agents, KG nodes that opted in to publish
Self-host runs as Option X with tenant_id="default" — no multi-tenancy code path executes.
Codebase status
Done:
- Single Docker stack (
platform/deploy/docker-compose.yml) for both self-host and "hosted = your own instance" render.yamldeploys one instance per user- Web UI on Vercel, calling each user's agent backend via
NEXT_PUBLIC_AGENT_URL
Open (Option X path):
- Tenant abstraction (
tenant_idcolumn, scoped queries) - Auth interface
- Config loader abstraction
PLATFORM_MODE=multitenantenv var
Open (Option Y path):
- A "deploy to Render" flow on humancensys.com
- Optional: a routing layer mapping
<username>.humancensys.comto the user's Render URL
Two-mode discipline
Every change considers:
- Self-host user impact
- Hosted-multitenant user impact (when that mode exists)
- Tests for both modes
- Docs for both modes
Anti-patterns
- Hardcoded paths or queries without tenant scoping (in the Option X path)
- "Multi-tenancy can come later" — retrofitting after data is being written is a migration cost
- Mixing platform code and tenant data in the same repo
Concretely
Self-host: docker compose up runs the engine locally. tenant_id="default" is hardcoded in the no-auth path.
Hosted (humancensys.com): Liz operates one Docker stack as the engine. Users get tenant identities and share infrastructure; personal toolkit data is never shared. Public skill library and knowledge commons live in shared services with a public = true marker.
Every PR, query, and write goes through the tenant abstraction. Tests verify isolation. Anti-patterns at /docs/contributing.