| Layer | What it stops | Mechanism |
|---|---|---|
| Authentication | Unauthenticated requests | JWT validation at the middleware |
| Authorization | Authorized but out-of-scope requests | RBAC scopes on every endpoint |
| Request isolation | Cross-user and cross-session leakage | Sessions and memory scoped per user_id |
| Schema enforcement | The agent writing where it shouldn’t | Read-only roles, schema-scoped engines |
| Tool sandboxing | Tools doing damage outside their lane | Sandboxes, allowlists, path scoping |
| Human approval | Irreversible actions running unsupervised | requires_confirmation, @approval (see Human Approval) |
Authentication
A production AgentOS sits behind JWT-validating middleware. Every request carries a token signed by the control plane; your service verifies it with a public key.authorization=True, every endpoint except /health and /openapi.json requires a valid JWT.
Setup flow
- Deploy AgentOS with
authorization=TrueandJWT_VERIFICATION_KEYset. The app crash-loops until the key is valid. That’s expected. - Open os.agno.com → Add OS → Live → paste your URL.
- Go to Settings → Generate key pair.
- Copy the public key. Paste into your env (
JWT_VERIFICATION_KEY). - Redeploy.
Custom JWT configuration
If you’re issuing JWTs from your own auth provider, pass anAuthorizationConfig:
verification_keys is a list, so you can rotate keys without downtime. Old tokens validate against the old key, new tokens against the new one. Drop the old key once tokens have aged out.
Authorization (RBAC scopes)
Every JWT carries ascopes claim. Endpoints are gated on scopes.
| Scope | Grants |
|---|---|
agents:read | List agents and read their config |
agents:<id>:run | Run a specific agent |
agents:*:run | Run any agent |
teams:<id>:run | Run a specific team |
teams:*:run | Run any team |
workflows:<id>:run | Run a specific workflow |
workflows:*:run | Run any workflow |
agent_os:admin | Full access including session, memory, and trace queries |
agents:my-agent:run. An internal service gets agent_os:admin. The control plane mints both with appropriate scopes. See RBAC for the full setup.
Request isolation
Every request runs in its own context. State doesn’t bleed between users, agents, or sessions.| Guarantee | How |
|---|---|
| No cross-user state | Sessions and memory are scoped per user_id. The JWT’s user_id_claim decides who’s asking. |
| No cross-session state | History is scoped per (user_id, session_id). Two open sessions for the same user stay independent. |
| No tool side-effect leakage | Tool calls run in-process within a single request’s lifecycle. No shared mutable state. |
| No model-level mixing | Each LLM call carries only the messages for the current run. No global prompt caching across users. |
agent_os:admin for service-level callers, it gives you per-user data isolation without a separate per-tenant runtime.
Schema enforcement
For agents that touch databases, API-level auth isn’t enough. A model can decide to writeDROP TABLE users regardless of what its instructions say. Stop it at the engine, not the prompt.
- Read-only roles. PostgreSQL connections opened with
default_transaction_read_only=onreject any write attempt no matter what SQL the model generates. This is what Dash uses on the Analyst agent. - Schema-scoped writes. A SQLAlchemy event listener that blocks DDL/DML targeting forbidden schemas. Dash uses this on the Engineer agent: writes go to the
dashschema, neverpublic.
Tool sandboxing
Most tools are pure functions. The ones that aren’t (HTTP calls, shell commands, code execution, file I/O) need their own guardrails.| Tool category | Recommended guard |
|---|---|
| Database queries | Read-only role, schema scoping |
| HTTP requests | Allowlist of hosts, no localhost |
| Shell or code execution | Sandbox (subprocess in container, separate worktree, strict ulimits) |
| File I/O | Path scoping (one root directory, no ..) |
| Money or irreversible actions | requires_confirmation=True + @approval(type="required") (see Human Approval) |
Human approval
The last layer. Some actions should never run unsupervised — refunds, deletes, prod deploys, anything irreversible. AgentOS pauses the run, surfaces the pending approval, resumes when a human signs off. Two patterns:requires_confirmation for the user asking the agent, @approval for a designated admin with policy authority. Full details on the Human Approval page.
Defaults
| Concern | Default behavior |
|---|---|
| Auth in production | RUNTIME_ENV=prd enables JWT validation |
| Auth in dev | RUNTIME_ENV=dev skips JWT validation so you can iterate |
| User isolation | Sessions and memory keyed on user_id from the JWT |
| Trace tampering | Traces are append-only; no API to mutate past spans |
| Schedule abuse | Scheduler tools require agent_os:admin scope to create system-level schedules |