Documentation Index
Fetch the complete documentation index at: https://docs.agno.com/llms.txt
Use this file to discover all available pages before exploring further.
Securing an agent is a game of layers. Each layer stops a different class of attack, from unauthenticated requests at the edge down to protected tool calls at the agent level.
| Layer | What it stops | Mechanism |
|---|
| Authentication | Unauthenticated requests | JWT validation at the middleware |
| Authorization | Authenticated but out-of-scope requests | RBAC scopes on every endpoint |
| Request isolation | Concurrent requests reading each other’s data | Fresh, isolated object per request |
| User isolation | Cross-user data leakage on shared endpoints | Opt-in using AuthorizationConfig(user_isolation=True) |
| Human approval | Protected actions running unsupervised | requires_confirmation, @approval (see Human Approval) |
AgentOS gives you good defaults at every layer.
Network-layer controls (rate limiting, WAF, IP allowlists, mTLS) live at your reverse proxy or API gateway layer.
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.
from agno.os import AgentOS
agent_os = AgentOS(
agents=[agent],
db=db,
authorization=True, # reads JWT_VERIFICATION_KEY from env
)
When authorization=True, every endpoint requires a valid JWT except a small set of public routes: /, /health, /info, /docs, /openapi.json
Get a JWT_VERIFICATION_KEY
- 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).
- Deploy AgentOS with
authorization=True and JWT_VERIFICATION_KEY set.
The control plane keeps the private key. Your service only ever sees the public key. JWTs are signed by the control plane and verified by your service.
Custom JWT configuration
If you’re issuing JWTs from your own auth provider, pass an AuthorizationConfig:
from agno.os import AgentOS
from agno.os.config import AuthorizationConfig
agent_os = AgentOS(
agents=[agent],
db=db,
authorization=True,
authorization_config=AuthorizationConfig(
verification_keys=["public-key-1", "public-key-2"], # rotation
algorithm="RS256",
audience="agno-os",
verify_audience=True,
admin_scope="agent_os:admin",
user_isolation=True, # see below
),
)
verification_keys is a list, so you can rotate keys without downtime. Old tokens validate against the old key, new tokens against the new. Drop the old key once tokens have expired.
JWT claim names (scopes, sub) are configured on the JWT middleware itself, not on AuthorizationConfig. The defaults — scopes for the scopes claim, sub for the user id claim — match the tokens minted by the control plane.
Authorization (RBAC scopes)
Every JWT carries a scopes 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 |
A typical end-user gets 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 gets a fresh copy of the agent, team, or workflow it’s hitting. AgentOS calls deep_copy() on the registered component per request, so mutable state — session-scoped variables, tool execution context, run metadata — never bleeds between concurrent calls.
Heavy resources (the DB connection, the model client, MCP tool handles) are shared by reference; only the mutable per-run state is isolated. You get cheap concurrency without the footgun of two requests racing on the same in-memory agent instance.
This is on by default for every run endpoint. There’s nothing to configure.
User isolation
Per-user data isolation is opt-in. JWT and RBAC stay in force without it, but routes operate on the unscoped DB — a caller with agents:my-agent:run could read another user’s sessions if they know the IDs. For multi-tenant deployments, turn it on:
from agno.os.config import AuthorizationConfig
agent_os = AgentOS(
agents=[agent],
db=db,
authorization=True,
authorization_config=AuthorizationConfig(
verification_keys=[public_key],
user_isolation=True,
),
)
With user_isolation=True, every non-admin caller gets:
| Guarantee | How |
|---|
| No cross-user reads | The JWT sub is threaded as user_id on every user-scoped read (sessions, memory, traces). Callers only see their own rows. |
| No cross-user writes | user_id is coerced on every write — a non-admin can’t persist a session, memory, or trace attributed to another user. |
| Run ownership | Cancel, resume, and continue routes require session_id and verify the run belongs to the caller’s session. |
| WebSocket reconnect | Reconnecting to a workflow run requires workflow_id and an ownership check. |
Admin callers (whoever holds the configured admin_scope, defaulting to agent_os:admin) bypass all of the above and see the full unscoped view — service accounts, internal tooling, the control plane.
Adding a new router endpoint that touches user-owned data? It must call get_scoped_user_id(request) (or one of its siblings in agno.os.middleware.user_scope) and thread the resulting user_id into every DB read/write. Missing that call silently bypasses isolation with no runtime error.
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 |
|---|
| Authentication | authorization=False. Opt in with authorization=True and JWT_VERIFICATION_KEY for production. |
| Request isolation | On — every run endpoint deep-copies the component. |
| User isolation | Off. Opt in via AuthorizationConfig(user_isolation=True) for multi-tenant. |
| Trace tampering | Traces are append-only; no API to mutate past spans. |
| Schedule abuse | Scheduler write endpoints require the schedules:write scope. |