Security
Security
Access in a workspace is declared, not coded. Every read and write — from a page, the agent, an envoy, an automation, an integration or an API key — is judged by the host against the acting policies, so the rules hold on every path.
Who acts
- Members hold the policies of their source team (
src/access_control/team/+team.ts). An administrator bypasses grants. External members, such as customer and vendor portal users, see only the apps whose audience admits them. - Envoys answer on a conversation instance under their declared
policies; in a direct message, a linked member’s own authority joins them. See Envoys. - Visitors use the pages of a public app and hold exactly its public policies: they may submit forms and read published rows, and never read back a submission.
- Automations act under their declared
runAspolicies. Integrations declare no policies: a feed or outbound run acts as{ kind: 'system', by: { integration, feed | outbound, instance } }and writes only what its definition declares — a feed the non-local fields of itsintocollection, an outbound rule’seventsits own record through the collection’supdateallowlist. Neither has a team, so neither can approve. - API keys hold the policies of the source team they select; a payload can never name its own policies.
How the pieces fit
- The collection bounds what any caller can ever read (
read.fields) and write (its input allowlists). - Policies grant, per collection, which rows (
where), which fields, which queries and actions, and which state moves. The grants of several policies are unioned. - Field masks hide what a read grant does not list: a masked field is absent from rows, filters, sorts, labels and file downloads.
- Approvals hold a write for named teams, declared on the write grant that admits it.
- Limits rate-limit actors, IP addresses and envoy senders, per policy.
Where it is enforced
The host conjoins each grant’s scope into every read and write statement, substitutes masks into every predicate, and routes approvals inside the write statement. A transform reads as the workspace, but queries, actions and automations read as their caller or their runAs, so tenant code never sees rows its caller may not read.