ThinkingRoot Docs
Security & Billing

Tenant isolation

One engine per project — isolation enforced at the container boundary.

The engine is single-tenant per instance. The cloud doesn't try to make one engine serve many tenants; instead it gives every project its own engine — its own container, its own volume, and its own CozoDB graph. Isolation is enforced at the container boundary, which is the strongest, simplest line to draw.

What the provisioner owns

The provisioner manages each engine's lifecycle and records its route in the control plane (engine_instances):

  • Spawn — on demand, it starts a container from the engine image, mounts the project's persistent volume, mints a unique internal API key, and injects secrets + the egress allowlist as environment variables.
  • Route — it records the engine's host, port, and key so the gateway can reach it.
  • Idle GC — engines idle beyond a TTL are stopped to save resources; the persistent volume survives, so the next request cold-starts a fresh container with the same data.

Why a separate process (not a thread)

Beyond isolation, there's a hard reason the engine can't be embedded: the engine and the Postgres-based control plane both claim the global links="sqlite3" slot, which Cargo won't allow in one binary. So the engine must run as its own process — which dovetails perfectly with per-project isolation. See Cloud vs Self-host.

No cross-tenant surface

Because nothing is shared between projects' engines, there's no query, cache, or graph that spans tenants. A project's data lives only in that project's volume, reachable only through the gateway with that project's key.