insigz docs
Engineering

Tenant Isolation

The product's core promise to regulated buyers - one customer's data can never reach another, with defense in depth.

The product's core promise to regulated buyers: one customer's data can never reach another. Defense in depth — isolation at compute, data, network, and query layers.

The silo model

Each tenant gets a fully dedicated stack (the full-stack-silo deployment model): its own Cloud Run service, Cloud SQL Postgres, Mongo database, GCS bucket, secrets, KMS key, and (optionally) custom domain. Nothing in the data path is shared between tenants. This is the costliest model operationally — and the easiest to reason about for compliance and the smallest blast radius if one tenant is compromised.

Layers of isolation

LayerMechanism
ComputeCloud Run service per tenant, own service account; no shared process memory
Data (PG)Cloud SQL instance per tenant + row-level security as defense-in-depth
Data (Mongo)DB-per-tenant, scoped Mongo user; private networking
BlobsGCS bucket per tenant, CMEK with the tenant's key
SecretsSecret Manager namespace + service account scoped per tenant
Networkper-tenant VPC connector; private IPs for SQL/Mongo; no public DB listeners
Query/RAGevery query injects tenant_id; vector search filters on tenant_id (22, ai/31)
AuthZhard cross-tenant deny in the policy engine (security/41)

Row-level security (even in a silo)

Even though each tenant has its own database, RLS is enforced so a forgotten WHERE can't leak across the in-tenant boundaries that matter (e.g. wargame cells, classification):

ALTER TABLE observations ENABLE ROW LEVEL SECURITY;
CREATE POLICY obs_visibility ON observations USING (
  tenant_id = current_setting('app.tenant_id')::uuid
  AND ( classification = 'OPEN'
        OR current_setting('app.cell', true) IS NULL
        OR cell_id = current_setting('app.cell', true)::uuid )
);

The application sets app.tenant_id / app.cell per transaction (security/40). RLS is not the primary tenant boundary (the separate DB is) — it's the layer that holds when application code is wrong.

The wargame lesson (a real isolation bug class)

Faculty need a visibility bypass (the umpire sees all cells) but must not inherit an approval bypass. Keep visibility and approval as independent policies; never let one imply the other. Each is enforced in the DB/policy engine, not in app code.

Cross-tenant surfaces (audit them)

The only components that legitimately span tenants are the control plane (reads aggregate telemetry, never raw data without logged break-glass) and shared infra (the connector catalog, the container image). Audit these especially: jobs that "scan all data," caches without a tenant key, admin queries that don't understand placement.

Verification

  • Automated tests that attempt cross-tenant reads/writes and assert denial (in CI).
  • A periodic "isolation audit" job: sample queries with a wrong tenant_id must return zero rows.
  • Pen-test scope includes tenant-escape attempts (security/44).

Gaps to close

  • Confirm VPC/private-endpoint topology for Cloud SQL + Atlas.
  • Decide Redis isolation (prefix vs. instance-per-tenant).
  • Write the cross-tenant negative tests and the isolation-audit job.