Security & Trust
Last updated: May 2026
Bridge Town is a financial planning platform used by FP&A teams to build, run, and share models that touch real business data. This page describes the security controls we have implemented. It is intended for buyers, procurement reviewers, and connector reviewers who need to assess the platform without inspecting source code.
Your AI agent; no server-side LLM
Bridge Town does not invoke any language model on your behalf. The platform exposes an MCP server — a set of tools that your AI agent (Claude Desktop, Claude Code, Claude.ai, or any compatible MCP client) connects to. Your agent runs on your infrastructure, under your control.
When an AI-assisted editing feature is triggered, the prompt is assembled and sent by your client to your AI provider. Bridge Town never forwards your financial model content to a language model without you initiating the request from your own client.
This means:
- No shared LLM infrastructure processes your data
- AI provider terms and data-handling apply only to the model you configure in your own client
- Switching AI providers requires no change to Bridge Town
Tenant isolation
Every piece of data in Bridge Town — projects, models, data sources, API tokens, audit logs — belongs to exactly one tenant. Tenants cannot see or access each other’s data.
PostgreSQL Row-Level Security (RLS) is the enforcement layer. Before every database query, the application sets the session-level tenant context:
SET LOCAL app.current_tenant_id = '<your-tenant-uuid>';RLS policies on every table filter rows to those matching the session tenant. The application database role is not a superuser and cannot bypass RLS. This means:
- A SQL injection cannot read another tenant’s rows
- Application logic bugs cannot leak cross-tenant data
- Raw DB credentials (if ever compromised) still cannot read other tenants’ data
Project storage (Git repository hosting) is namespaced per tenant. All repository operations are scoped to the authenticated tenant’s namespace; cross-tenant access is not possible at the storage layer.
Object storage (S3) uses a {tenant_id}/ prefix for all objects. Presigned URLs are generated after validating the tenant prefix; there is no public bucket access.
For a deeper technical overview, see the Tenant Isolation docs.
Authentication & access control
Dual-mode authentication
| Method | Format | Typical use |
|---|---|---|
| Auth0 JWT | Browser session cookie | Web UI |
API token (btk_…) | Bearer header | MCP clients, CI, automation |
Both methods resolve to a (tenant_id, user_id) pair. Auth failures are always logged before returning a 401.
API tokens are argon2id-hashed. Only the 12-character lookup prefix is stored in plaintext; the full token is never stored. Tokens can be revoked at any time from the web UI or the authenticated token REST API.
Role-based access control (RBAC)
| Role | Permissions |
|---|---|
| Owner | All operations, including member management, token management, and tenant settings |
| Editor | Create, edit, and delete models; branch and merge; share models |
| Viewer | Read-only access to projects, models, and dashboards |
Role changes take effect immediately. Owners can demote or remove members, including other owners.
API token security risk
API tokens carry all permissions of the issuing user, including Owner-level admin actions. There is no per-tool or per-scope restriction. Use the minimum-privilege token for your use case:
- Prefer OAuth (Auth0) for interactive user sessions
- Use API tokens only for non-interactive automation
- Set
expires_inwhen creating tokens for time-bounded credentials - Revoke tokens immediately when no longer needed
Authenticated model sharing
Cross-tenant model sharing follows a two-step request/accept protocol logged in the audit trail. A share request from Tenant A to Tenant B requires an Owner or Editor on the target project to accept it. Neither tenant can read the other’s data outside the shared model.
Encryption
| Data | Encryption |
|---|---|
| PostgreSQL database | AES-256 at rest (AWS RDS encryption) |
| S3 objects | SSE-S3 (default); SSE-KMS with tenant CMK for enrolled Enterprise tenants |
| TLS in transit | TLS 1.2 or higher for all client connections |
| OAuth refresh tokens | AES-256-GCM; envelope encryption with tenant CMK for enrolled Enterprise tenants |
| API tokens | argon2id hash (full token never stored) |
OAuth refresh tokens for the Google Sheets integration are encrypted before storage using a key sourced from AWS Secrets Manager. For Enterprise tenants enrolled in customer-managed keys, a per-tenant data encryption key (DEK) is generated via the tenant’s AWS KMS key and used for envelope encryption. The plaintext token is never written to disk.
Customer-managed encryption keys (Enterprise)
Enterprise tenants can enroll a customer-provided AWS KMS key (BYOK) to control encryption of their most sensitive data. Bridge Town uses envelope encryption: your AWS KMS key acts as a key-encrypting key; Bridge Town generates and manages data encryption keys (DEKs) that it never stores in plaintext.
What is covered by your key (1.0 scope):
- S3 snapshots and exports — all new PutObject calls use SSE-KMS with your CMK ARN. Existing objects are re-encrypted on the next write.
- Data-source OAuth credentials — per-tenant DEKs encrypted under your CMK replace the platform-wide encryption key for enrolled tenants.
What remains platform-managed:
- PostgreSQL storage — AWS RDS does not support per-tenant KMS keys on a shared instance. RDS storage encryption uses Bridge Town’s platform KMS key.
- Model source (Gitea/EFS) — EFS does not support per-tenant KMS encryption. Model source is isolated by tenant access controls, not customer-managed keys.
- Redis cache — ephemeral data (TTL ≤ 24 hours); no financial row data is persisted in Redis.
Enrollment: Requires an Enterprise plan. You create a symmetric AWS KMS key in your AWS account and grant Bridge Town’s ECS task role kms:GenerateDataKey, kms:Decrypt, and kms:DescribeKey via key policy. Bridge Town validates access before persisting your ARN.
Revocation: Removing Bridge Town’s key policy grant immediately prevents new writes and reads of CMK-protected data. Your workspace enters a degraded read-only state. Restoring the key policy grant restores normal operation.
Key loss: AWS KMS enforces a minimum 7-day deletion window. If your CMK is permanently deleted, CMK-protected S3 data becomes permanently inaccessible. Bridge Town cannot recover data without the key. This is intentional: customer-managed keys give you — not Bridge Town — ultimate data custody.
For technical details, see the CMK design document and the CMK incident runbook.
Sandboxed model execution
Financial model code runs in ephemeral Docker containers with:
- No outbound network — container network namespace is isolated (
--network none) - No host filesystem access — only the model file and a read-only data snapshot are injected
- Resource limits — CPU and memory capped per container
- Ephemeral — containers are destroyed immediately after execution
The Docker socket is exposed to the sandbox runner only through a docker-socket-proxy that allows container lifecycle operations only. Volume management, image pulls, exec, and swarm operations are blocked.
DuckDB queries (query_data) run with enable_external_access=false, preventing read_csv, read_parquet, and HTTP table functions from accessing the filesystem or network. Only SELECT/WITH statements are accepted; DDL and DML are rejected by a statement validator before reaching DuckDB.
Rate limits and request size limits
| Control | Value |
|---|---|
| Global rate limit | 100 requests / minute per tenant (Redis sliding window) |
Body size — /mcp endpoint | 150 MiB (accommodates 100 MiB file uploads plus base64 overhead) |
| Body size — all other routes | 1 MiB |
| Max concurrent model runs | 5 per tenant |
| DuckDB result cap | 1,000 rows (max 10,000 with explicit parameter) |
| Query timeout | 30 seconds |
Rate limits are enforced at the application layer and reinforced at the edge (Caddy). When exceeded, the server returns 429 Too Many Requests with Retry-After and X-RateLimit-* headers.
Security headers
All HTTP responses include:
| Header | Value |
|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains |
X-Content-Type-Options | nosniff |
X-Frame-Options | DENY |
Referrer-Policy | strict-origin-when-cross-origin |
TLS termination is handled by Caddy with automatic certificate renewal.
Audit logging
Every MCP tool call and authentication event is written to:
- PostgreSQL
audit_logtable — append-only, tenant-scoped with RLS. Database triggers blockUPDATEandDELETEon this table. - AWS CloudWatch Logs — structured JSON, best-effort delivery.
Logged fields include: tenant_id, user_id, event_type, tool_name, success, error_message, ip_address, timestamp.
Authentication failures are logged even before a tenant is identified, enabling detection of token-stuffing and enumeration attempts.
Git-versioned auditability and reversibility
Every model is stored as plain Python in a tenant-namespaced Git repository. The complete version history is preserved. You can:
- Diff any two versions to see exactly what changed
- Roll back to any prior version with
revert_to_version - Branch, review, and merge model changes like code changes
- Re-run any past version against a point-in-time data snapshot for reproducibility
This version history is part of your tenant data and is covered by the tenant isolation controls described above.
Vulnerability disclosure
To report a security vulnerability, email security@bridgetown.builders.
Do not open a public GitHub issue for vulnerabilities.
How to report
Email security@bridgetown.builders with:
- A description of the vulnerability
- Steps to reproduce (as detailed as possible)
- An impact assessment — what an attacker could achieve
- The affected component (MCP server, web UI, infrastructure, etc.)
A machine-readable security contact is also published at /.well-known/security.txt per RFC 9116.
Response timeline
| Step | Target |
|---|---|
| Acknowledgement | Within 48 hours of report |
| Initial assessment | Within 5 business days |
| Fix deployed — critical / high | Within 7 days of confirmation |
| Fix deployed — medium / low | Within 30 days of confirmation |
| Coordinated disclosure | After fix is deployed |
Scope
In scope:
- Bridge Town MCP server and API (
api.bridgetown.builders) - Bridge Town web application (
app.bridgetown.builders) - Tenant isolation bypasses (cross-tenant data access)
- Authentication and authorisation flaws
- Sandboxed model execution escapes
- Infrastructure misconfigurations that affect user data
Out of scope:
- This landing page (
bridgetown.builders) — static content with no user data - Denial of service (DoS/DDoS) attacks
- Social engineering or phishing
- Vulnerabilities in third-party infrastructure (Auth0, Stripe, AWS) — please report these directly to the vendor
- Issues that require physical access to a user’s device
- Automated scan findings without demonstrated impact
Safe harbor
We will not pursue legal action against researchers who:
- Act in good faith and avoid privacy violations, data destruction, or service disruption
- Test only against accounts they own or have explicit permission to test
- Report findings through the process above before any public disclosure
- Allow us a reasonable time to remediate before disclosing
Severity reference
| Severity | Examples |
|---|---|
| Critical | Cross-tenant data access, authentication bypass, RLS bypass, remote code execution outside sandbox |
| High | Privilege escalation, sandbox escape, API token exposure, PII exposure |
| Medium | Stored XSS, CSRF, information disclosure (internal error details) |
| Low | Missing security headers, verbose errors, minor information leakage |
Compliance and data processing
Data Processing Agreement (DPA)
Enterprise customers and organisations subject to GDPR may require a signed DPA before going live with Bridge Town.
To request a DPA:
- Email security@bridgetown.builders with the subject line:
DPA Request — [Your Organisation Name]. - Include your organisation name, the name and email of the signatory, and your preferred turnaround window.
- We will respond within 5 business days with a draft DPA for review.
DPA reviews are handled directly by the Knightian Labs leadership team.
Security review requests
If your organisation requires a security review or security questionnaire before onboarding Bridge Town, we support the following:
- Security questionnaire: We can complete standard vendor security questionnaires (SIG Lite, CAIQ, or equivalent). Allow 10 business days.
- Architecture review call: We can arrange a 30-minute call with a technical lead to walk through our security architecture. Request via security@bridgetown.builders.
- Custom requirements: Contact us if your procurement process requires artefacts not listed here.
Certification Roadmap
We distinguish current controls from planned work:
| Area | Status |
|---|---|
| SOC 2 Type II certification | Not yet certified. The platform is designed around SOC 2 Trust Services Criteria. Evidence collection is in progress. |
| Independent penetration test | Scheduled; not yet completed. |
| Zero-retention agreements with AI providers | Not in place. AI providers are your own clients; their data handling is governed by your agreements with them, not ours. |
| GDPR/CCPA formal compliance attestation | We follow privacy-by-design principles and publish a Privacy Policy. Formal compliance attestations have not been obtained. |
If your procurement process requires evidence of any of the above, contact support@bridgetown.builders.
Related documents
- Privacy Policy — how we collect, use, and protect personal data
- Subprocessors — full list of third-party services that process data on our behalf
- Terms of Service — your agreement with Knightian Labs
- Tenant Isolation docs — technical deep-dive on PostgreSQL RLS
- Security Overview — full technical security reference in the docs
Questions? Contact security@bridgetown.builders or support@bridgetown.builders.