Policy engine
Policy operates at two layers. The runtime policy in policy.toml controls caller identity → role → capability mapping — who may call the gateway and with what ceiling. On top of that, each registered agent is governed by a layered policy chain (org ceiling → zone ceiling → agent leaf), covered below.
Edit policy.toml to change runtime policy behaviour without recompiling.
Fail-closed by default
Section titled “Fail-closed by default”The role used when no token is provided — or the token maps to no role — is set by default_role:
# Production posture: unauthenticated callers get nothing.default_role = "deny_all"deny_all is a built-in role; no [roles.deny_all] entry is required. Setting the default to admin is for development only and emits a startup warning.
Tokens map to roles
Section titled “Tokens map to roles”[tokens]"stratus-dev-token-change-me" = "admin""console-bff-token" = "console_operator"
# Regulated profiles"fintech-teller-token" = "fintech_teller""fintech-manager-token" = "fintech_manager""regulator-token" = "regulator" # read-only ledger accessCallers present the token as Authorization: Bearer <token> or X-Imara-Token.
Roles list capabilities
Section titled “Roles list capabilities”A role is a capability ceiling. Anything not listed is blocked and written to the ledger:
[roles.admin]capabilities = [ "CAP_VFS_READ", "CAP_VFS_WRITE", "CAP_VFS_DELETE", "CAP_VFS_SEARCH", "CAP_NETWORK", "CAP_SUBAGENTS", "CAP_MESSAGING_OUT", "CAP_X402_OUT", "CAP_PAYMENTS_OUT", "CAP_PAYMENT_APPROVE",]Capabilities are deliberately narrow. For example, CAP_PAYMENT_APPROVE lets a role answer a payment escalation but grants no ability to spend — which is why an operator role can hold it while still denying ACTION_PAYMENT_INITIATE.
The agent policy chain
Section titled “The agent policy chain”Registered agents carry a second, layered policy resolution — structured like IAM:
- an org ceiling caps everything in the organisation,
- a zone ceiling caps the department,
- each agent carries a compiled leaf derived from its own grant.
A request must clear all three. Starter templates render the ceiling documents from a handful of parameters; the results are ordinary, editable policy documents, content-addressed by hash:
| Method | Path | Description |
|---|---|---|
GET |
/v1/policy-templates |
Browse the template catalog |
POST |
/v1/policy-templates/{id}/apply |
Render + attach org and zone ceilings (dry_run: true previews without saving) |
GET |
/v1/agents/{id}/policy-chain |
The effective chain governing one agent, with document hashes |
The chain endpoint returns the same resolution the enforcement gate performs, so “denied by policy” is always inspectable by hash. Re-applying a template with identical parameters is harmless — content addressing re-attaches the same hashes. See the integration guide for a worked example.
Managing runtime policy over the API
Section titled “Managing runtime policy over the API”Policy mutations are control-plane operations gated by [admin]:
| Method | Path | Description |
|---|---|---|
GET |
/admin/policy |
Current snapshot (roles, tokens, default role) |
POST/DELETE |
/admin/policy/roles · /admin/policy/tokens |
Manage roles and token mappings |
POST |
/v1/policies/evaluate |
Dry-run a policy document against a hypothetical action |
POST |
/admin/policy/observe |
Observation mode: record freely, then… |
GET |
/admin/policy/build/{sessionID} |
…synthesise a least-privilege role from the trace |
Observation mode is the fastest path to least privilege: run the agent against a permissive role while the kernel records what it actually does, then build a role containing exactly those capabilities.