Security & Zero Trust¶
Agentomatic ships with a layered security model that protects your agent platform from the network edge all the way down to individual tool invocations. Each layer is independently configurable and designed to degrade gracefully when optional dependencies are absent.
graph LR
A["Client Request"] --> B["Rate Limiting"]
B --> C["API Key / JWT Auth"]
C --> D["CORS"]
D --> E["Zero-Trust Enforcer"]
E --> F["Agent Security Policy"]
F --> G["Agent Execution"]
| Layer | Component | Purpose |
|---|---|---|
| 1 – Transport | Rate Limiter | Sliding-window throttle per client IP |
| 2 – Identity | API Key / JWT Middleware | Authenticate callers |
| 3 – Origin | CORS Middleware | Restrict browser origins |
| 4 – Authorisation | Zero-Trust Enforcer | Per-agent RBAC, delegation, and audit |
| 5 – Execution | Agent Security Policy | Tool allow/block lists, token budgets |
API Key Authentication¶
The simplest authentication method — a shared secret verified on every request via an HTTP header or query parameter.
Enabling API Key Auth¶
How It Works¶
- The middleware reads the key from the
X-API-Keyheader first, then falls back to theapi_keyquery parameter. - If neither matches, a
401 UnauthorizedJSON response is returned. - The following paths are skipped by default and never require a key:
| Path | Purpose |
|---|---|
/health |
Liveness probe |
/healthz |
Kubernetes liveness |
/readiness |
Kubernetes readiness |
/docs |
Swagger UI |
/openapi.json |
OpenAPI schema |
/redoc |
ReDoc UI |
/ |
Root / landing page |
Use headers, not query params
Query parameters are logged by proxies and browsers. Always prefer the
X-API-Key header in production.
JWT Token Authentication¶
For production deployments, Agentomatic supports JSON Web Token (JWT) authentication with JWKS key rotation — the industry standard for microservice-to-microservice auth.
JWTConfig Reference¶
from agentomatic.security import JWTConfig
config = JWTConfig(
enabled=True,
jwks_url="https://your-idp.example.com/.well-known/jwks.json",
issuer="https://your-idp.example.com/",
audience="agentomatic-api",
algorithms=["RS256"],
header_name="Authorization", # default
header_prefix="Bearer", # default
)
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool |
False |
Master switch for JWT validation |
jwks_url |
str |
"" |
URL to the JWKS endpoint for public key discovery |
issuer |
str |
"" |
Expected iss claim value |
audience |
str |
"" |
Expected aud claim value |
algorithms |
list[str] |
["RS256"] |
Accepted signing algorithms |
header_name |
str |
"Authorization" |
HTTP header carrying the token |
header_prefix |
str |
"Bearer" |
Prefix stripped before decoding |
skip_paths |
set[str] |
Health & docs paths | Paths that bypass JWT checks |
Registering the Middleware¶
from agentomatic.security import JWTAuthMiddleware, JWTConfig
config = JWTConfig(
enabled=True,
jwks_url="https://idp.example.com/.well-known/jwks.json",
issuer="https://idp.example.com/",
audience="agentomatic",
)
app.add_middleware(JWTAuthMiddleware, config=config)
On successful validation the middleware populates two attributes on the request state:
request.state.jwt_claims— the full decoded JWT payload.request.state.user_id— thesub(subject) claim value.
Optional dependency
JWT support requires the PyJWT[crypto] extra. If the package is not
installed, the middleware logs a warning and allows all requests through
— ensuring graceful degradation during development.
Dev-mode bypass
When jwks_url is left empty, tokens are decoded without
signature verification. This is convenient for local development but
must never be used in production.
Agent-Level Permissions (RBAC)¶
Each agent can be assigned an AgentSecurityPolicy that governs what it
is allowed to do at runtime.
from agentomatic.security import AgentSecurityPolicy
policy = AgentSecurityPolicy(
require_auth=True,
allowed_roles=["admin", "operator"],
allowed_scopes=["agents:execute", "tools:read"],
max_execution_time=60.0,
max_tokens_per_request=4096,
allowed_tools=["web_search", "calculator"],
blocked_tools=["shell_exec"],
env_vars_whitelist=["OPENAI_API_KEY"],
allowed_delegation_targets=["summariser", "researcher"],
rate_limit_requests=100,
rate_limit_window=60,
)
Full Field Reference¶
| Field | Type | Default | Description |
|---|---|---|---|
require_auth |
bool |
False |
Reject unauthenticated requests |
allowed_roles |
list[str] |
[] |
JWT roles that may invoke this agent (empty = any) |
allowed_scopes |
list[str] |
[] |
OAuth scopes required (empty = any) |
max_execution_time |
float |
30.0 |
Hard timeout in seconds per request |
max_tokens_per_request |
int |
8192 |
Maximum LLM tokens the agent may consume |
allowed_tools |
list[str] \| None |
None |
Explicit allow-list (None = all tools allowed) |
blocked_tools |
list[str] |
[] |
Tools that are always denied |
env_vars_whitelist |
list[str] \| None |
None |
Environment variables the agent may read |
allowed_delegation_targets |
list[str] |
[] |
Agents this agent may delegate work to |
rate_limit_requests |
int \| None |
None |
Per-agent request cap (overrides global) |
rate_limit_window |
int \| None |
None |
Window in seconds for the per-agent cap |
Tool Access Resolution Order¶
The policy evaluates tool access in the following order:
- Blocked tools — if the tool appears in
blocked_tools, access is denied immediately. - Allowed tools — if
allowed_toolsis a list, the tool must appear in it. Ifallowed_toolsisNone, all tools are permitted. - Default deny — if neither condition is met, access is denied.
assert policy.is_tool_allowed("calculator") is True
assert policy.is_tool_allowed("shell_exec") is False
Zero-Trust Enforcement¶
The ZeroTrustEnforcer is the central authority that ties policies to
agents and provides three verification primitives:
from agentomatic.security import ZeroTrustEnforcer, AgentSecurityPolicy
enforcer = ZeroTrustEnforcer(require_auth_globally=True)
# Register per-agent policies
enforcer.register_policy("researcher", AgentSecurityPolicy(
allowed_tools=["web_search"],
allowed_delegation_targets=["summariser"],
))
enforcer.register_policy("summariser", AgentSecurityPolicy(
allowed_tools=["text_summarise"],
max_tokens_per_request=2048,
))
Verification Methods¶
Checks authentication presence, role claims, and scope claims against the agent's policy.
ok, reason = enforcer.verify_request(request, agent_name="researcher")
if not ok:
raise HTTPException(status_code=403, detail=reason)
Verification steps performed:
- Is authentication present when
require_authisTrue? - Does the JWT contain a permitted role claim?
- Does the JWT contain a permitted scope claim?
Confirms that one agent is permitted to delegate work to another.
Audit Logging¶
Every verification call is recorded via loguru in a structured format:
enforcer.audit_log(
event="tool_access_denied",
agent_name="researcher",
context={"tool": "shell_exec"},
)
Centralised audit trail
Pipe loguru output to a JSON sink (file or stdout) and forward it to your SIEM for real-time alerting on denied events.
Rate Limiting Configuration¶
Agentomatic includes a sliding-window rate limiter that operates per client IP.
Enabling Rate Limiting¶
from agentomatic.middleware.rate_limit import RateLimitMiddleware
app.add_middleware(
RateLimitMiddleware,
max_requests=100, # requests per window
window_seconds=60, # window duration
)
Client Identification¶
The middleware resolves the client IP using:
- The
X-Forwarded-Forheader (first entry), when behind a reverse proxy. - Falls back to
request.client.hostotherwise.
Response Headers¶
Every response includes rate-limit metadata:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the window |
X-RateLimit-Remaining |
Requests remaining in the current window |
When the limit is exceeded, a 429 Too Many Requests response is
returned with a Retry-After header indicating when the client may
retry.
CORS Configuration¶
Cross-Origin Resource Sharing is configured at the platform level via
FastAPI's built-in CORSMiddleware.
from agentomatic import AgentPlatform
platform = AgentPlatform(
cors_origins=["https://studio.example.com", "http://localhost:3000"],
)
The middleware is registered with the following defaults:
| Setting | Value |
|---|---|
allow_credentials |
True |
allow_methods |
["*"] |
allow_headers |
["*"] |
expose_headers |
["X-Studio-Run-Id"] |
Restrict origins in production
Never use ["*"] for cors_origins in production. Always enumerate
the exact domains that should be allowed to make cross-origin requests.
Input Validation¶
All request and configuration models in Agentomatic are built on Pydantic v2, which provides automatic input validation and serialisation.
Best practices
- Use
AgentSecurityPolicyfields likemax_tokens_per_requestandmax_execution_timeto bound agent resource consumption. - Validate user-supplied prompts and tool arguments at the application layer before they reach the agent runtime.
- Prefer allow-lists (
allowed_tools) over block-lists (blocked_tools) for defence in depth.
Environment Variable Security¶
Keep secrets out of source control
- Store all secrets (
AUTH__API_KEY, database URIs, LLM provider keys) in a.envfile or a secrets manager — never commit them to Git. - Ensure
.envis listed in your.gitignore. - Use
env_vars_whitelistinAgentSecurityPolicyto restrict which variables each agent can access at runtime. - Rotate API keys and JWT signing keys on a regular schedule.
Production Security Checklist¶
Review before deploying to production
- API Key Auth enabled (
FEATURES__ENABLE_AUTH=true) - JWT validation enabled with a real
jwks_url— dev-mode bypass is disabled - CORS origins restricted to known domains (no
["*"]) - Rate limiting enabled (
FEATURES__ENABLE_RATE_LIMIT=true) - Secrets stored in
.envor a vault — never in source code -
allowed_toolsset per agent — no agent has unrestricted tool access -
blocked_toolsincludes high-risk tools likeshell_exec -
max_execution_timeandmax_tokens_per_requesttuned per agent - Delegation targets explicitly listed — no open delegation
- Audit logging piped to a persistent sink / SIEM
- TLS termination handled by a reverse proxy (nginx, Caddy, cloud LB)
- Dependency audit —
pip auditrun in CI
Troubleshooting¶
I get 401 even though I'm sending the API key
Make sure the key is in the X-API-Key header (case-sensitive) or
the api_key query parameter. Confirm that
FEATURES__ENABLE_AUTH=true and AUTH__API_KEY are set in the
same environment that the server process reads.
JWT middleware silently allows all requests
This usually means PyJWT[crypto] is not installed. The middleware
degrades gracefully and logs a warning. Install the dependency:
Rate limiter counts are wrong behind a load balancer
The middleware reads X-Forwarded-For to identify clients. Ensure
your reverse proxy sets this header correctly and that only trusted
proxies can override it — otherwise clients can spoof their IP.
Related Documentation¶
| Topic | Link |
|---|---|
| Quick Start | Quick Start |
| Configuration Reference | configuration.md |
| Agent Manifest | Agent Structure |
| Multi-Agent Orchestration | Swarm Delegation |
| Studio Integration | studio.md |
| API Reference | API Reference |