Security architecture
Server-side, where no prompt can reach
Read-only defaults, caps from the policy row, preview-before-apply, selective approval, hash-chained audit — every mechanism documented below, failure modes included.
The request path
Fixed order, every request
Authentication, scoping, and policy run before any connector code touches a platform. The path is the same for every one of the 226 tools.
- 01
Authenticate
Bearer token or OAuth 2.1 resolves to your org and workspace. Rate limits apply before anything else runs.
bearer → org → workspace → rate limit
- 02
Scope
The agent only sees tools for platforms you've connected and the token allows — no 226-tool context dump.
tools = connected ∩ token.scopes
- 03
Policy
The policy row is read from Postgres. No row means read-only — the gateway fails closed, not open.
policy row absent ⇒ read_only = true
- 04
Cap and preview
Budget and bid caps come from the policy row, never from the model. Every mutation dry-runs first; apply requires a matching live preview.
caps ← policy row · preview.fingerprint == mutation
- 05
Approval — when you've flagged it
waits for a humanTool classes you mark high-stakes park here for a human. Everything else executes instantly within its caps.
status: pending_approval
- 06
Apply and audit
The write executes and lands in an append-only ledger — each event hash-chained to the last. No role can edit it, including ours.
hash = sha256(prev_hash + event)
Mechanisms
Each claim, with the thing that enforces it
Fail-closed policy engine
Writes require a policy row. No row, no writes. Caps are numeric columns in Postgres — nothing the model supplies is ever used as a limit.
Tenant isolation by row-level security
Every tenant table keys on the request's org GUC. Unset means zero rows — queries fail empty, not open. The gateway's database role is non-owner, so RLS always applies.
KMS-envelope token vault
Platform OAuth tokens are encrypted with a per-row data key, itself wrapped by Cloud KMS. A database dump alone cannot decrypt a single credential.
Hash-chained, append-only audit
Each audit event carries the previous event's hash, per org, from sequence 1. No role has UPDATE or DELETE, and a trigger blocks even the owner.
Selective human approval
Policy scopes approval to the tool classes you flag — budget-touching writes, structural changes. Parked writes surface in an approval inbox with their preview attached.
Least-privilege database roles
The gateway role can read and append audit events but cannot touch billing or membership; the dashboard role can't rewrite history. Two roles, explicit grant matrix.
Failure modes
What happens when something goes wrong
A security page that only lists happy paths isn't one. These are the designed outcomes.
| Scenario | Designed behavior |
|---|---|
| No policy row exists for a platform | All writes blocked — read-only, fail-closed |
| Apply arrives without a matching live preview | Write refused until the exact mutation is previewed |
| Mutation exceeds a cap in the policy row | Blocked server-side; the model cannot raise the cap |
| Tool class is flagged for approval | Write parks as pending; executes only on human approval |
| Org context missing on a request | RLS returns zero rows — no cross-tenant reads |
| Anyone attempts to edit an audit event | Database trigger raises insufficient_privilege — for every role |
Every change your agent makes, in a ledger no one can edit — including us.
Append-only, hash-chained, exportable. Show it to your client, your CFO, or your security team.