Tenant and client isolation
DenialOS applies isolation at two independent layers, so a mistake in one is not the only thing standing between two customers.
Application layer
Customer-bound reads and writes are scoped by organization and client in application code:
- voice delegations query by
organizationIdandclientId; - hospital voice provider/campaign/audience objects are scoped to both;
- AI context manifests are stored with organization/client authorization context;
- EHR canonical records carry organization/client/connection lineage;
- denial cases are read through scoped authorization helpers.
The design goal is:
organization A + client A
≠
organization B + client B
and likewise:
connection A
cannot substitute for
connection B
Database layer
Tenant tables also carry row-level security policies keyed on the record's organization:
- one idempotent migration applies the policy set to every table that carries an
organization_idcolumn; - the policies are enabled in FORCE mode, so they constrain the table owner as well as ordinary roles;
- a connection must declare its tenant scope inside the transaction that uses it, and a connection that declares no scope reads nothing;
- global reference data remains readable to tenants but is not writable by them;
- a platform scope exists for legitimate cross-organization work, and the runtime refuses to nest a tenant scope inside a platform scope, so a boundary cannot be widened by a nested call.
The boundary fails closed
The application refuses to serve from a database connection that can bypass these policies. The API boot path, the worker boot path, and the serverless entrypoint each check the connected database role; the serverless entrypoint answers 503 environment_not_ready rather than serving requests with isolation disabled. A preflight command (npm run verify:runtime-role) performs the same check before a deployment is promoted.
What is verified
The isolation suite runs against a real PostgreSQL instance as a role that row-level security can actually constrain — not a superuser, and not a role with BYPASSRLS:
- the policy set covers every table that carries an
organization_id; - a tenant sees its own rows and not another tenant's;
- cross-tenant writes are refused even when the query asks for them explicitly;
- an unscoped connection reads nothing;
- the boundary is crossed only where a platform scope says so;
- a real router request is served correctly under enforced row-level security;
- the runtime role cannot rewrite an audit-chain anchor.
Boundary
Row-level security is defense in depth on top of scoped authorization code, not a replacement for it. The database policies enforce the organization boundary; isolation between separate clients inside one organization is enforced by application authorization. This documentation does not claim customer-specific validation for any particular deployment, and production enablement requires the deployment to run under a role that cannot bypass the policies — the runtime gate refuses traffic until that is true.