How tenant isolation works
Per-brand data isolation states the promise; this page states the machinery. The boundary between brands is enforced by the database engine itself, on every query, for every consumer — console, SDK, integration, or agent. Application code can add checks, but no application bug can remove the boundary.
The tenant column discipline
Every operational table carries a tenant column — venture_id, the schema's name for the brand boundary — as a non-nullable foreign key. Rows exist inside exactly one brand. Alongside it, standard audit columns (created/updated actor and timestamp) and a nullable soft-delete marker mean deletion is normally expressed as a marker on the row, not a removed row. A table that does not fit this shape requires a documented exception; the default is not negotiable.
The three standard policies
Row-level security is enabled on tenant tables with three standard policies:
- Tenant isolation: a row is visible only when its tenant column matches the brand context set for the current transaction. Mismatch returns nothing — the query fails closed, and the absence is loud rather than silently wrong.
- Administrative bypass: a narrow, audited path for platform-level operations that must cross brands (see the guarded bypass below).
- Soft-delete filtering: deleted rows are invisible to ordinary reads, so "deleted" means deleted for every consumer without each query remembering to filter.
Transaction-scoped tenant context
Brand context is injected per transaction, as a transaction-local database setting — never as a session-wide flag. The distinction matters under connection pooling: a session-wide setting can leak into the next borrower's queries, while a transaction-local one dies with the transaction. The platform's data layer wraps context injection and the query in a single transaction so the two can never be separated by a refactor.
Two defense lines are always present together: application middleware sets the context, and the database enforces it. Either one alone is a single point of failure; the pair is the design.
The guarded bypass
A role that bypasses row-level enforcement exists, because migrations, backups, and break-glass operations need it — and it is treated as the most dangerous credential in the system:
- Held only by backend services and operations tooling, never by applications or individuals as a matter of course.
- Every use requires a recorded reason, logged at the highest audit severity — see the audit trail & evidence model.
- Break-glass use follows two-person authorization; the opening is recorded loudly, by design.
Database audit triggers
Independent of application logging, database triggers on audited tables record every insert, update, and delete with the actor and the before/after row state. This is the plane that catches what application code forgot to log — and it is why "the application never logs that action" is not a hole in the trail.
Derived data follows the same boundary
Isolation is not only for rows of record. Vector embeddings — the platform's derived search data — carry the same tenant column, so similarity search stays inside the boundary. Where information is deliberately shared across ventures, it is a designed aggregate tier that records its sources — never a read path onto another brand's rows.
Boundary
This page describes the enforcement model at orientation level. Live schemas, policy definitions, role grants, and environment procedures ship with authenticated developer access.
