Tenant Isolation
Overview
Section titled “Overview”Bridge Town is a multi-tenant platform. Every piece of data — models, data sources, tokens, audit logs — belongs to exactly one tenant. Tenants cannot see or access each other’s data.
Row-Level Security (RLS)
Section titled “Row-Level Security (RLS)”PostgreSQL Row-Level Security enforces isolation at the database level. Every request sets the tenant context before executing any queries:
SET LOCAL app.current_tenant_id = '<tenant-uuid>';RLS policies on every table filter rows to only those matching the current tenant. This means:
- A SQL injection cannot access another tenant’s data
- Application bugs cannot leak cross-tenant data
- Even raw database queries are scoped
Authentication flow
Section titled “Authentication flow”- Request arrives with a Bearer token (JWT or API token)
- Auth middleware validates the token and extracts
tenant_idanduser_id - Database session is opened with
SET LOCAL app.current_tenant_id - Route handler executes — all queries are automatically scoped
- Session closes — tenant context is cleared
Dual-mode authentication
Section titled “Dual-mode authentication”Bridge Town supports two authentication methods:
| Method | Format | Use case |
|---|---|---|
| Auth0 JWT | eyJ... (contains .) | Web UI sessions |
| API token | btk_... (argon2id hashed) | MCP clients, CLI, CI/CD |
Both methods resolve to the same (tenant_id, user_id) pair at authentication time. The auth middleware auto-detects the format.
For MCP tool calls specifically, an authenticated Auth0 JWT session may additionally resolve to a different (tenant_id, user_id) pair per call: a caller with active memberships in more than one workspace can pass a workspace_id argument to a tool to run that one call against another workspace they belong to (see docs/mcp-workspace-aware-access-prd.md in the repo for the full contract). Each such call still opens its database session with SET LOCAL app.current_tenant_id scoped to the target workspace, so RLS isolation is unchanged — only the workspace being targeted moves. API tokens never get this: a token’s (tenant_id, user_id) pair is fixed to the workspace it was issued in for every call.
Model storage isolation
Section titled “Model storage isolation”Each tenant’s models are stored in a dedicated namespace within the internal model storage layer. All model storage operations are scoped to the authenticated tenant’s namespace — cross-tenant access at the storage layer is not possible.
Sandbox isolation
Section titled “Sandbox isolation”Model execution runs in Docker containers with --network none. Containers are created per-run and destroyed after execution. No persistent state, no network access, no cross-tenant contamination.
Customer-owned agent model
Section titled “Customer-owned agent model”Bridge Town does not invoke server-side language models. You connect your own AI agent (Claude, Claude Code, Codex, or any MCP-compatible client) to Bridge Town using your own model provider account.
Your agent calls Bridge Town’s MCP tools on your behalf. The language model that interprets your instructions runs inside your agent’s session — Bridge Town never sees your prompts or model reasoning. Bridge Town stores only the structured inputs and outputs of MCP tool calls, model files, run outputs, data-source snapshots, and audit events.
This architecture means:
- Your prompts are governed solely by your model provider’s privacy policy.
- Bridge Town has zero visibility into your agent’s reasoning or conversation history.
- Tenant isolation at the data layer applies to all MCP tool outputs and stored artifacts.
See the data path reference for the canonical explanation of what Bridge Town receives and stores in an agent session.