The security model
Where each decision is made, what is server-enforced, and the rule behind all of it.
One rule explains most of the design: no security decision is made from a value the browser can edit. Everything below follows from it.
| Decision | Made | Not made |
|---|---|---|
| Are you signed in? | Server-side, revalidating against the auth server | From the cookie's contents |
| What is your role? | Server-side from account entitlements and workspace membership, on every request | From browser storage |
| May this workspace use this connection? | Server-side, from the workspace's own selection | From what the interface offers |
| May this agent touch this system? | Server-side, from the tool list | From the brief's wording |
| Does this key have this scope? | Server-side, against the issued scopes | From the request |
| Is this webhook genuine? | Server-side, by signature before the body is parsed | From the sender's claim |
Fail closed
Where something cannot be determined, the answer is no. An unverifiable webhook is dropped rather than accepted with a warning. A provider whose signing scheme is not implemented is refused rather than trusted. If the rate limiter itself is unavailable in production, requests are refused rather than let through unmetered. Each of these produces a support conversation instead of an incident, which is the trade being made.
Isolation
- Workspaces share nothing by default. Cross-workspace work requires an explicit delegation.
- Provider credentials sit against the account, encrypted, and are never copied into a workspace record.
- Files live in a private store with row-level rules and are reached by signed URL.
- A developer key reads one workspace. Any other identifier is a 404, whether or not it exists.