> For the complete documentation index, see [llms.txt](https://docs.interactive.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.interactive.ai/agents/operations/security.md).

# Security

> **Context** — Everything security-relevant about running an agent, in one place: who can call what, how each credential flows, and what to lock down. Assumes the [network surface](/agents/concepts/architecture.md#network-surface) overview.

## Authentication surfaces

### Inbound: bearer token (everything except health, webhooks & login)

All API routes except the health probes, `/webhooks/*`, and the `/auth/login` flow require:

```
Authorization: Bearer <agent api key>
```

The token is the manifest's `runtime.api_key` (resolved from `${AGENT_API_KEY}` or your chosen variable). Comparison is constant-time.

For the built-in chat UI only, the same key can be presented as an `HttpOnly` `agent_auth` cookie, set by typing the key into `POST /auth/login` (browsers can't attach bearer headers to the UI's own requests) and cleared by `POST /auth/logout`. The cookie carries the same secret with the same powers — everything below applies to it equally.

Properties to design around:

* **One shared token per agent.** There is no per-client identity, no scopes, no expiry. Anyone holding the token has the full API: read any session, post messages, trigger autonomous routines.
* Therefore: the token belongs to your **integration tier only**. Browsers and end-user devices must never see it — your integration authenticates end users its own way and holds the agent token server-side (the [SDK guide](/agents/guides/integrating-the-sdk.md)'s architecture).
* Treat it like a database password: secret store, rotation procedure, never in URLs or logs.

### Inbound: HMAC signatures (`/webhooks/*`)

Third-party webhook entry points bypass bearer auth — the provider's signature over the raw body *is* the authentication:

* Signature = `prefix + hex(HMAC(secret, raw_body))`, algorithm `sha256` (or `sha1`/`sha512` where a provider requires it), carried in the configured header. Verified constant-time; any failure → 401 with no detail.
* Unknown webhook names and routines without webhook config return an identical 404 — the public surface doesn't reveal what exists.
* **Rotation without restart:** the secret env var is re-read on every request. Update the secret in the platform's secret store and both old and new traffic windows behave predictably (one secret is valid at a time — coordinate the provider-side switch).
* Replay safety: identical deliveries dedupe to the same run via a body-hash idempotency key — a replayed webhook cannot double-fire a routine.

### Outbound credentials

| Destination                                               | Credential                                     | Header                             |
| --------------------------------------------------------- | ---------------------------------------------- | ---------------------------------- |
| LLM router                                                | `llms.api_key`                                 | Bearer                             |
| InteractiveAI platform (boot fetch)                       | `interactive_platform.{public_key,secret_key}` | platform auth                      |
| MCP servers                                               | per-server `mcps[].api_key`                    | Bearer                             |
| Knowledge base (pgvector)                                 | `search.password`                              | Postgres auth                      |
| External search                                           | `search.api_key`                               | Bearer                             |
| Traces backend                                            | `traces.backend.api_key`                       | Bearer or Basic (`api_key_scheme`) |
| Autonomous callbacks & event webhooks to your integration | the agent api key                              | Bearer                             |

Your callback/webhook receivers **must verify** that bearer header — otherwise anyone who can reach them can forge "agent results".

## Secret handling rules

The manifest cannot contain a secret value — credential fields only accept `${VAR_NAME}` references, enforced by schema validation:

* **Resolution at boot, fail-fast:** missing variables abort startup with the variable name. No fallback path, no partial boots.
* **Exception — webhook secrets** are read per request (rotation support, above).
* You declare a secret bundle in the manifest's `secrets:` list (by its name in Interactive Secrets); the platform injects that bundle's key/value pairs as environment variables before boot. The value exists only in the agent's environment, never in the manifest.
* **Rotation procedure** (all except webhook secrets): update the secret in the platform's secret store; the platform rolls the agent to pick it up. The old credential must stay valid until the rollout completes.
* Logs and config dumps redact credential fields; Postgres URIs are logged with credentials masked.

## Autonomous-surface hardening

* **`callback_url_allowlist`** on every autonomous routine in production. Without it, any caller holding the bearer token can point results at any URL. With it, only listed hostnames (a leading `.` allows subdomains) receive callbacks.
* **Operator timeout bounds** are a backstop against runaway runs: a routine's `timeout_seconds` is capped by `AUTONOMOUS_MAX_TIMEOUT_SECONDS` (default 600s) regardless of what the YAML asks for.
* **Input schemas are a security control** — strict `input_schema`s reject malformed payloads at the door (HTTP 400), before any model sees them.

## Prompt-level exposure

What reaches the model is what's in the session: messages, tool results, variables, retrieved snippets. Practical consequences:

* **Anything a tool returns can end up in a reply.** Don't return fields the customer must never see; filter server-side in the MCP tool, not in instructions.
* **Injected tool events** (`/sessions/{id}/tool_events`) render verbatim into prompts (control characters are stripped). The endpoint is bearer-gated, but treat injected payloads with the same trust you'd give tool results.
* **Variables are agent-visible by definition** — never put secrets in variables. Integration-private data goes in metadata, which never reaches a prompt.
* **Traces capture conversations, tool results, and variables** and the trace metadata snapshot is not redacted. Apply access control on the traces backend accordingly, and point `traces.backend` at your own collector if platform-default storage doesn't meet your data policy.

## Network posture checklist

* [ ] Agent reached only through the platform-managed endpoint; `endpoint: true` only when a public URL is genuinely required
* [ ] Bearer token held exclusively by the integration tier; rotation procedure documented
* [ ] Third-party providers reach only `/webhooks/*`; every webhook HMAC-verified
* [ ] MCP servers not publicly reachable beyond what the agent needs; per-server `api_key` set when network reachability alone isn't enough
* [ ] Postgres (sessions + KB) reachable from the agent only; `sslmode: require` or stricter
* [ ] Callback receivers verify the bearer header and dedupe by `run_id`
* [ ] `callback_url_allowlist` set on every production autonomous routine
* [ ] Trace backend access restricted to operators who may read conversations


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.interactive.ai/agents/operations/security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
