Multi-tenancy
Isolation model
Section titled “Isolation model”Every API request carries a tenant identity extracted from the trust header.
The platform enforces isolation at three layers:
| Layer | Mechanism |
|---|---|
| API | X-Tenant-Id validated by trust middleware |
| Metadata (D1) | All queries are scoped with WHERE tenant_id = ? |
| Storage (DO) | Each shard is a separate Durable Object instance |
No Durable Object is ever shared between tenants. Physical isolation is structural, not policy-based.
Trust flow
Section titled “Trust flow”External caller │ ▼edge-gateway ──(validates credential, adds X-Internal-Sig + X-Tenant-Id)──▶ core-api │ trustMiddleware verifies sig │ ▼ handlers use c.var.tenantIdThe trust.middleware.ts on core-api uses @core-labs/trust-kit to:
- Verify the
X-Internal-SigHMAC signature (shared secret with the upstream gateway) - Extract and validate
X-Tenant-Id - Set
c.var.tenantIdfor all downstream handlers
Tenant data ownership
Section titled “Tenant data ownership”- A tenant owns their graph projects, node/edge types, and all graph data.
- Platform operators (admin) can view tenant metadata via the control-plane API using an admin token.
- Tenants cannot see each other’s data, even if they use the same graph type names.
Soft multi-tenancy in D1
Section titled “Soft multi-tenancy in D1”Graph metadata queries always include tenantId:
SELECT * FROM graphs WHERE tenant_id = ? AND id = ?Drizzle ORM enforces this at the repository layer — raw SQL with missing tenant filters will fail TypeScript compilation if the schema types are used correctly.
Local development
Section titled “Local development”In ENVIRONMENT=development, the trust middleware accepts any X-Tenant-Id without signature verification. This lets you test multi-tenant scenarios locally:
# Tenant Acurl http://localhost:8787/graphs -H "X-Tenant-Id: tenant-a"
# Tenant Bcurl http://localhost:8787/graphs -H "X-Tenant-Id: tenant-b"Each tenant sees only their own graphs.