{
    "componentChunkName": "component---src-pages-blog-markdown-remark-fields-slug-js",
    "path": "/blog/auth-for-ai-agents",
    "result": {"data":{"markdownRemark":{"html":"<p>AI agents are no longer experimental. Production systems today delegate real action, such as sending emails, committing code, and modifying records, to autonomous runtimes that chain tool calls without pausing for confirmation. That shift introduces authentication challenges that classic OAuth and session models were never designed to handle.</p>\n<p>This post maps the full authentication blueprint for AI agent systems: who the principals are, how tokens flow across trust boundaries, where policy enforcement must sit, and when a human must approve before execution continues.</p>\n<h2 id=\"what-changes-when-the-user-is-an-agent\" style=\"position:relative;\"><a href=\"#what-changes-when-the-user-is-an-agent\" aria-label=\"what changes when the user is an agent permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>What Changes When the “User” Is an Agent?</h2>\n<h3 id=\"principles-and-boundaries\" style=\"position:relative;\"><a href=\"#principles-and-boundaries\" aria-label=\"principles and boundaries permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Principles and Boundaries</strong></h3>\n<p>Four distinct actors exist in a typical agentic system:</p>\n<ul>\n<li><strong>End-user</strong>: The human who initiates a task.</li>\n<li><strong>Application backend</strong>: The service that authenticates the user and spawns the agent.</li>\n<li><strong>Agent runtime</strong>: The LLM-backed process that plans and executes steps.</li>\n<li><strong>Tool servers</strong>: APIs or microservices the agent calls to perform real-world actions.</li>\n</ul>\n<p>Each actor has its own identity, and each boundary between them is a potential trust gap.</p>\n<h3 id=\"risk-deltas-versus-classic-applications\" style=\"position:relative;\"><a href=\"#risk-deltas-versus-classic-applications\" aria-label=\"risk deltas versus classic applications permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Risk Deltas Versus Classic Applications</strong></h3>\n<p>Classical web applications execute deterministic code paths. Agents do not. The primary new risks are:</p>\n<ul>\n<li><strong>Tool chaining</strong>: An agent may call ten tools in sequence; a compromise at step three poisons all downstream steps.</li>\n<li><strong>Unbounded actions</strong>: Without explicit scope limits, an agent granted “write” access may write far more than intended.</li>\n<li><strong>Prompt injection</strong>: Malicious content in tool outputs can hijack agent behavior mid-session.</li>\n<li><strong>Data exfiltration</strong>: An agent with read access to sensitive data and outbound tool access can leak it with trivial ease.</li>\n</ul>\n<h3 id=\"blast-radius-and-least-privilege\" style=\"position:relative;\"><a href=\"#blast-radius-and-least-privilege\" aria-label=\"blast radius and least privilege permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Blast Radius and Least-Privilege</strong></h3>\n<p>Because agents act autonomously, the blast radius of a misconfigured permission is orders of magnitude larger than a misconfigured human-facing role. Scope narrowing is not optional: it is the primary control.</p>\n<p><strong>Minimal sequence diagram:</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"41062797709582385000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`End-User → App Backend: authenticate (OIDC/OAuth)\nApp Backend → Agent Runtime: spawn(session_token, scopes=[narrow])\nAgent Runtime → Tool Server A: call(capability_token_A)\nAgent Runtime → Tool Server B: call(capability_token_B)\nTool Server → Policy Engine: evaluate(actor, action, resource)\nPolicy Engine → Tool Server: allow | deny`, `41062797709582385000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\">End<span class=\"token operator\">-</span>User → App Backend<span class=\"token operator\">:</span> <span class=\"token function\">authenticate</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">OIDC</span><span class=\"token operator\">/</span>OAuth<span class=\"token punctuation\">)</span>\nApp Backend → Agent Runtime<span class=\"token operator\">:</span> <span class=\"token function\">spawn</span><span class=\"token punctuation\">(</span>session_token<span class=\"token punctuation\">,</span> scopes<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>narrow<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\nAgent Runtime → Tool Server <span class=\"token constant\">A</span><span class=\"token operator\">:</span> <span class=\"token function\">call</span><span class=\"token punctuation\">(</span>capability_token_A<span class=\"token punctuation\">)</span>\nAgent Runtime → Tool Server <span class=\"token constant\">B</span><span class=\"token operator\">:</span> <span class=\"token function\">call</span><span class=\"token punctuation\">(</span>capability_token_B<span class=\"token punctuation\">)</span>\nTool Server → Policy Engine<span class=\"token operator\">:</span> <span class=\"token function\">evaluate</span><span class=\"token punctuation\">(</span>actor<span class=\"token punctuation\">,</span> action<span class=\"token punctuation\">,</span> resource<span class=\"token punctuation\">)</span>\nPolicy Engine → Tool Server<span class=\"token operator\">:</span> allow <span class=\"token operator\">|</span> deny</code></pre></div>\n<h2 id=\"core-architecture-identity-sessions-and-scopes\" style=\"position:relative;\"><a href=\"#core-architecture-identity-sessions-and-scopes\" aria-label=\"core architecture identity sessions and scopes permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Core Architecture: Identity, Sessions, and Scopes</h2>\n<h3 id=\"workspace-and-tenant-model\" style=\"position:relative;\"><a href=\"#workspace-and-tenant-model\" aria-label=\"workspace and tenant model permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Workspace and Tenant Model</strong></h3>\n<p>Each agent identity should be scoped to a single tenant or organization. An agent operating inside Org A must never present credentials that are valid inside Org B. Org IDs must be encoded into every token, every log line, and every policy evaluation. Cross-tenant isolation is not a nice-to-have; it is the foundational invariant.</p>\n<h3 id=\"session-tiers\" style=\"position:relative;\"><a href=\"#session-tiers\" aria-label=\"session tiers permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Session Tiers</strong></h3>\n<p>A healthy architecture separates session lifetimes into two tiers:</p>\n<ol>\n<li><strong>Long-lived user session</strong>: Issued at login; backed by refresh tokens; hours to days in TTL.</li>\n<li><strong>Short-lived agent session</strong>: Derived from the user session at task start; minutes in TTL; non-refreshable by design.</li>\n</ol>\n<p>Agent sessions should expire when the task completes or when the wall-clock TTL expires, whichever comes first.</p>\n<h3 id=\"scope-inheritance\" style=\"position:relative;\"><a href=\"#scope-inheritance\" aria-label=\"scope inheritance permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Scope Inheritance</strong></h3>\n<p>Scopes flow strictly downward. A user token carrying <code class=\"language-text\">repo:read</code> can produce an agent token carrying <code class=\"language-text\">repo:read</code>, but never <code class=\"language-text\">repo:write.</code> An agent token can produce a per-tool capability token carrying an even narrower scope. The rule is: <strong>only narrow, never widen.</strong></p>\n<h2 id=\"token-strategy-that-actually-works\" style=\"position:relative;\"><a href=\"#token-strategy-that-actually-works\" aria-label=\"token strategy that actually works permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Token Strategy That Actually Works</h2>\n<h3 id=\"token-types\" style=\"position:relative;\"><a href=\"#token-types\" aria-label=\"token types permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Token Types</strong></h3>\n<table>\n<thead>\n<tr>\n<th>Token Type</th>\n<th>Issued To</th>\n<th>Audience</th>\n<th>Rotation</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>User access token</td>\n<td>End-user</td>\n<td>App backend</td>\n<td>On refresh</td>\n</tr>\n<tr>\n<td>Agent session token</td>\n<td>Agent runtime</td>\n<td>Agent runtime</td>\n<td>Per task</td>\n</tr>\n<tr>\n<td>Capability token</td>\n<td>Agent → Tool</td>\n<td>Specific tool server</td>\n<td>Per call or per minute</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"sender-constrained-tokens\" style=\"position:relative;\"><a href=\"#sender-constrained-tokens\" aria-label=\"sender constrained tokens permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Sender-Constrained Tokens</strong></h3>\n<p>Standard bearer tokens can be replayed if intercepted. Sender-constrained tokens bind a token to the entity presenting it. Two\nstandard mechanisms exist:</p>\n<ul>\n<li><strong>DPoP (Demonstrating Proof-of-Possession)</strong>: The agent signs each request with a private key; the token is only valid when accompanied by a matching proof JWT.</li>\n<li><strong>MTLS (Mutual TLS)</strong>: The client certificate is bound to the token; the server verifies the certificate at the transport layer.</li>\n</ul>\n<p>DPoP is generally preferred for agent-to-tool communication because it works over standard HTTPS without requiring certificate infrastructure at every tool server.</p>\n<h3 id=\"token-exchange-and-downgrades\" style=\"position:relative;\"><a href=\"#token-exchange-and-downgrades\" aria-label=\"token exchange and downgrades permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Token Exchange and Downgrades</strong></h3>\n<p>Use RFC 8693 (OAuth 2.0 Token Exchange) to derive capability tokens from agent session tokens. Key rules:</p>\n<ul>\n<li><strong>Audience restriction</strong>: Each capability token is valid for exactly one tool server.</li>\n<li><strong>TTL</strong>: Capability tokens should expire in 60—300 seconds.</li>\n<li><strong>Break-glass flows</strong>: Emergency elevated access should require explicit re-authentication, not scope widening on existing tokens.</li>\n</ul>\n<h2 id=\"tool-permissions-as-policy-rbacfga\" style=\"position:relative;\"><a href=\"#tool-permissions-as-policy-rbacfga\" aria-label=\"tool permissions as policy rbacfga permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Tool Permissions as Policy (RBAC/FGA)</h2>\n<h3 id=\"modeling-tools-as-resources\" style=\"position:relative;\"><a href=\"#modeling-tools-as-resources\" aria-label=\"modeling tools as resources permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Modeling Tools as Resources</strong></h3>\n<p>Every tool call maps to a structured permission check:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"33654297214999773000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`subject: agent-id / tenant-id                                         \naction: github.issues.label                                           \nresource: repo:acme-org/payments-service                              `, `33654297214999773000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token literal-property property\">subject</span><span class=\"token operator\">:</span> agent<span class=\"token operator\">-</span>id <span class=\"token operator\">/</span> tenant<span class=\"token operator\">-</span>id                                         \n<span class=\"token literal-property property\">action</span><span class=\"token operator\">:</span> github<span class=\"token punctuation\">.</span>issues<span class=\"token punctuation\">.</span>label                                           \n<span class=\"token literal-property property\">resource</span><span class=\"token operator\">:</span> repo<span class=\"token operator\">:</span>acme<span class=\"token operator\">-</span>org<span class=\"token operator\">/</span>payments<span class=\"token operator\">-</span>service                              </code></pre></div>\n<p>Verbs, resource IDs, and any contextual constraints (time of day, rate limits, spend thresholds) are all inputs to the policy engine.</p>\n<h3 id=\"rbac-versus-relationship-based-access-control\" style=\"position:relative;\"><a href=\"#rbac-versus-relationship-based-access-control\" aria-label=\"rbac versus relationship based access control permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>RBAC Versus Relationship-Based Access Control</strong></h3>\n<p>RBAC (role-based access control) is sufficient when tool access maps cleanly to a small number of roles. It fails when:</p>\n<ul>\n<li>Access depends on the relationship between the agent and a specific resource instance.</li>\n<li>Permissions vary per tenant, per data owner, or per environmental condition.</li>\n</ul>\n<p>In those cases, fine-grained authorization (FGA) systems such as OpenFGA or AWS Cedar evaluate relationship graphs rather than flat role assignments.</p>\n<h3 id=\"evaluate-at-the-tool-server\" style=\"position:relative;\"><a href=\"#evaluate-at-the-tool-server\" aria-label=\"evaluate at the tool server permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Evaluate at the Tool Server</strong></h3>\n<p>Policy must be evaluated by the tool server or a sidecar policy engine immediately adjacent to it. Evaluating policy inside the LLM prompt is not a security control; it is advisory text that a sufficiently crafted prompt can override. The tool server must return a structured <code class=\"language-text\">allow</code> or <code class=\"language-text\">deny</code> response with a reason code, regardless of what the agent was told to do.</p>\n<p><strong>Minimal policy schema:</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"68789202679348404000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\n&quot;subject&quot;: &quot;agent:a1b2/tenant:acme&quot;,\n&quot;action&quot;: &quot;github.issues.label&quot;,\n&quot;resource&quot;: &quot;repo:acme/payments&quot;,\n&quot;decision&quot;: &quot;allow&quot;,\n&quot;reason&quot;: &quot;policy:triage-agent-v2&quot;\n}`, `68789202679348404000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token punctuation\">{</span>\n<span class=\"token string-property property\">\"subject\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"agent:a1b2/tenant:acme\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token string-property property\">\"action\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"github.issues.label\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token string-property property\">\"resource\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"repo:acme/payments\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token string-property property\">\"decision\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"allow\"</span><span class=\"token punctuation\">,</span>\n<span class=\"token string-property property\">\"reason\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"policy:triage-agent-v2\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"human-in-the-loop-where-it-matters\" style=\"position:relative;\"><a href=\"#human-in-the-loop-where-it-matters\" aria-label=\"human in the loop where it matters permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Human-in-the-Loop Where It Matters</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/4e756bb8e7020cede7773de1fcc67fbe/280a1/Human-in-the-Loop.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 45.56962025316456%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAByUlEQVQoz63QzYrUQBQF4Er95K+TSlKppKvSSWe6Z1AH6aadZtDFgAM6LmUQF/oSbsTX9g1yriSjrly6OFy4HD4ul7FIUqATcKuJNwXJzlA8NpRfOqr2a+ihIe0Nla6k1he09RqbJqVaCzIrRiZl1OWctoVEX0hiTAmwSJEaDMzjCfbzGfr+GSJfQXlDb99s8OXDiK8PI14fLOIioV2r8e3mgO/nI37cHPGyzpFFjKpEgDEegElOed9gf3/C1bsz/O1zqDqHLFe4fXWB93fXeLi7xuGFh4gluUrj4+mAx9MRn05H7OsSPGAUyQCMMQYmAspKjW2/xX53idY7iCyBSkLYzmOzu0I37mFsDaE4pWmCwtZY9z1KWyNfpTNEwWwtYMAoK3LUdo3aelTWQEQhpBSwtkRtHWzjYSq9XJLFCWpj0DY96qpCmWULyP6CjJEuNPrNgGG4QOtaCCUheYDGGvTDiM1mi6p8AtM4hvMew7BD13XQ/wChqwLeucl7N7VuPS2gCNBaM43jOPV9P5mqmEHMFzrnJrf0/VQ8gX+yyMSVIBFK4kqSCBWxIJh/QnEoKFKcIiWWzF3BOcVKUSjkMpV42v8O+/k/8wuTBNKF5xzZbAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Human-in-the-Loop\"\n        title=\"Human-in-the-Loop\"\n        src=\"/static/4e756bb8e7020cede7773de1fcc67fbe/f058b/Human-in-the-Loop.png\"\n        srcset=\"/static/4e756bb8e7020cede7773de1fcc67fbe/c26ae/Human-in-the-Loop.png 158w,\n/static/4e756bb8e7020cede7773de1fcc67fbe/6bdcf/Human-in-the-Loop.png 315w,\n/static/4e756bb8e7020cede7773de1fcc67fbe/f058b/Human-in-the-Loop.png 630w,\n/static/4e756bb8e7020cede7773de1fcc67fbe/40601/Human-in-the-Loop.png 945w,\n/static/4e756bb8e7020cede7773de1fcc67fbe/78612/Human-in-the-Loop.png 1260w,\n/static/4e756bb8e7020cede7773de1fcc67fbe/280a1/Human-in-the-Loop.png 1545w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h3 id=\"approval-triggers\" style=\"position:relative;\"><a href=\"#approval-triggers\" aria-label=\"approval triggers permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Approval Triggers</strong></h3>\n<p>Not every action requires human approval. A risk-scoring layer should route requests into one of three buckets:</p>\n<table>\n<thead>\n<tr>\n<th><strong>Ruleset</strong></th>\n<th><strong>Example Condition</strong></th>\n<th><strong>Outcome</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Auto-allow</td>\n<td>Read-only ops under rate limit</td>\n<td>Execute immediately</td>\n</tr>\n<tr>\n<td>Soft-hold</td>\n<td>Spend > $10, first run of new tool</td>\n<td>Queue for async approval</td>\n</tr>\n<tr>\n<td>Must-approve</td>\n<td>Delete op, cross-tenant data access</td>\n<td>Block until explicit approval</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"ux-patterns\" style=\"position:relative;\"><a href=\"#ux-patterns\" aria-label=\"ux patterns permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>UX Patterns</strong></h3>\n<ul>\n<li><strong>Inline approve/deny:</strong> Push a notification to the authorizing user with full context; single-click decision.</li>\n<li><strong>Time-boxed holds:</strong> If no approval arrives within N minutes, the agent abandons the task and notifies the user.</li>\n<li><strong>Bulk approvals:</strong> For high-frequency agents, allow pre-approval of a category of actions within a session.</li>\n</ul>\n<h3 id=\"evidence-and-audit-capture\" style=\"position:relative;\"><a href=\"#evidence-and-audit-capture\" aria-label=\"evidence and audit capture permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Evidence and Audit Capture</strong></h3>\n<p>Every approval or denial must be stored with: agent ID, tenant ID, action, resource, a hash of the input parameters, the approver identity, and a timestamp. This record is the chain of custody if an action is disputed later.</p>\n<h2 id=\"secrets-data-boundaries-and-prompt-safe-design\" style=\"position:relative;\"><a href=\"#secrets-data-boundaries-and-prompt-safe-design\" aria-label=\"secrets data boundaries and prompt safe design permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Secrets, Data Boundaries, and Prompt-Safe Design</h2>\n<h3 id=\"no-secrets-in-prompts\" style=\"position:relative;\"><a href=\"#no-secrets-in-prompts\" aria-label=\"no secrets in prompts permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>No Secrets in Prompts</strong></h3>\n<p>API keys, database credentials, and tokens must never appear in the agent’s context window. The correct pattern is: the agent calls a tool; the tool server fetches the secret from a vault and uses it server-side. The agent receives only the result.</p>\n<h3 id=\"minimize-data-in-context\" style=\"position:relative;\"><a href=\"#minimize-data-in-context\" aria-label=\"minimize data in context permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Minimize Data in Context</strong></h3>\n<ul>\n<li>Apply field-level masking before injecting query results into the prompt.</li>\n<li>Redact PII (emails, phone numbers, account numbers) unless the task explicitly requires them.</li>\n<li>Use allow-list field filters at the tool server, not inside the prompt.</li>\n</ul>\n<h3 id=\"output-validation\" style=\"position:relative;\"><a href=\"#output-validation\" aria-label=\"output validation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Output Validation</strong></h3>\n<p>Before any agent-generated content is committed to a downstream system, validate it against a strict schema. Reject outputs that contain unexpected fields, executable content, or values outside allowed ranges.</p>\n<p><strong>Anti-patterns to avoid:</strong></p>\n<ul>\n<li>Passing raw database rows into the prompt without redaction.</li>\n<li>Using the LLM’s own output as a policy decision.</li>\n<li>Granting an agent a “super” token and filtering in-prompt.</li>\n<li>Logging full prompt content, including injected secrets.</li>\n</ul>\n<h2 id=\"observability-and-audit-youll-actually-use\" style=\"position:relative;\"><a href=\"#observability-and-audit-youll-actually-use\" aria-label=\"observability and audit youll actually use permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Observability and Audit You’ll Actually Use</h2>\n<h3 id=\"per-tool-logs\" style=\"position:relative;\"><a href=\"#per-tool-logs\" aria-label=\"per tool logs permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Per-Tool Logs</strong></h3>\n<p>Every tool invocation should produce a structured log event containing:</p>\n<ul>\n<li><code class=\"language-text\">actor</code> (agent ID + tenant ID)</li>\n<li><code class=\"language-text\">scope</code> presented</li>\n<li><code class=\"language-text\">action</code> and <code class=\"language-text\">resource</code></li>\n<li>SHA-256 hash of input parameters</li>\n<li>SHA-256 hash of output</li>\n<li>Policy decision + reason code</li>\n<li>Latency and status</li>\n</ul>\n<h3 id=\"distributed-tracing\" style=\"position:relative;\"><a href=\"#distributed-tracing\" aria-label=\"distributed tracing permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Distributed Tracing</strong></h3>\n<p>Propagate a shared <code class=\"language-text\">trace-id</code> and <code class=\"language-text\">span-id</code> from the frontend through the backend, into the agent runtime, and into every tool server call. This makes it possible to reconstruct the full execution chain of a multi-step task in a single trace view. OpenTelemetry is the standard instrumentation layer for this.</p>\n<h3 id=\"alerting\" style=\"position:relative;\"><a href=\"#alerting\" aria-label=\"alerting permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Alerting</strong></h3>\n<p>Configure alerts on:</p>\n<ul>\n<li>Deny rate spike (>X denials/minute per tenant)</li>\n<li>Approval backlog (holds older than N minutes)</li>\n<li>Spend anomaly (agent cost >Y per hour)</li>\n<li>Unusual tool access patterns (new tool called for the first time in production)</li>\n</ul>\n<h2 id=\"threat-models-and-practical-mitigations\" style=\"position:relative;\"><a href=\"#threat-models-and-practical-mitigations\" aria-label=\"threat models and practical mitigations permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Threat Models and Practical Mitigations</h2>\n<table>\n<thead>\n<tr>\n<th><strong>Threat</strong></th>\n<th><strong>Control</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Prompt injection via tool output</td>\n<td>Sanitize tool outputs; treat them as untrusted data, not instructions.</td>\n</tr>\n<tr>\n<td>Tool abuse / scope escalation</td>\n<td>Allow-list tools per agent role; enforce at tool server.</td>\n</tr>\n<tr>\n<td>Supply chain compromise</td>\n<td>Sign dependencies; maintain SBOMs; use runtime isolation (containers/VMs).</td>\n</tr>\n<tr>\n<td>SSRF via agent-initiated HTTP calls</td>\n<td>Egress allow-list on agent runtime network; block RFC 1918 ranges.</td>\n</tr>\n<tr>\n<td>Data exfiltration</td>\n<td>Content filters on outbound tool calls; canary data in sensitive datasets.</td>\n</tr>\n<tr>\n<td>Token replay</td>\n<td>DPoP or MTLS on all agent-to-tool calls.</td>\n</tr>\n<tr>\n<td>Cross-tenant data access</td>\n<td>Tenant ID in every token claim; policy engine enforces isolation.</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"reference-implementation-with-supertokens\" style=\"position:relative;\"><a href=\"#reference-implementation-with-supertokens\" aria-label=\"reference implementation with supertokens permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Reference Implementation with SuperTokens</h2>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/9e2ba8940c8f1123465e76174989af40/d0c2f/Supertokens.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 47.46835443037975%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqElEQVQoz02PbW/aMBSFMy0kvo6d2InjkDgJBEJJeX8tpGzVoIxO2mhFK6H9LH7yBJGqPTrf7nmke7RR5n8r5Pdxp+x3eqHXj8Q0U+MsnOTJXRK24jAQnu8L3xdSSiGE53lFUVwul81mow1iPlRsrthK8bVy18qdKa/sNleDYpGn4+Iuz3Pf9wHwJwCg6zoAaJ3AHtbtpWKl4quQz+p81lBP8+nDbLaZjlaLxXA8aWYZIQT+w7qhxS7pSrsv7YGgA+G0GZnk7fPp/eP942m7/3N8fTm87LbbKKzr+leEACFU+QghzaVW5JCUWS1mtV163G1/7/fPP3avb6fz+e/pdHo7Hn8+734dDo9liTE2b1BKGGOahcGxsCA4oDi0rbaK+t1u7763XC4f16vyYb6YjsbDXt5qSF+gG7VaTcowa91rAIABCCAHg4OBAhIuD4MgjaMsVa0kakQyFJzbxDSMSjYMw+NuohpXGd0GQBUAauiSs0YcN5NrsjRhGByzVtUQArOmUz+V2UD7XF9hXo/G9RFicZtyhzJqWcgE0zCrBoCpf6HJKBge/gGUPjjwL5U6kAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"SuperTokens\"\n        title=\"SuperTokens\"\n        src=\"/static/9e2ba8940c8f1123465e76174989af40/f058b/Supertokens.png\"\n        srcset=\"/static/9e2ba8940c8f1123465e76174989af40/c26ae/Supertokens.png 158w,\n/static/9e2ba8940c8f1123465e76174989af40/6bdcf/Supertokens.png 315w,\n/static/9e2ba8940c8f1123465e76174989af40/f058b/Supertokens.png 630w,\n/static/9e2ba8940c8f1123465e76174989af40/40601/Supertokens.png 945w,\n/static/9e2ba8940c8f1123465e76174989af40/78612/Supertokens.png 1260w,\n/static/9e2ba8940c8f1123465e76174989af40/d0c2f/Supertokens.png 1362w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h3 id=\"sessions-and-claims\" style=\"position:relative;\"><a href=\"#sessions-and-claims\" aria-label=\"sessions and claims permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Sessions and Claims</strong></h3>\n<p><a href=\"https://supertokens.com/\" target=\"_blank\" rel=\"nofollow\">SuperTokens</a> supports custom session claims. Encode the following into the agent session payload:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"63576749199959080000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\n &quot;sub&quot;: &quot;user:u123&quot;,\n  &quot;agent_id&quot;: &quot;agent:a456&quot;,\n  &quot;tenant_id&quot;: &quot;acme&quot;,\n  &quot;scopes&quot;: [&quot;github.issues.read&quot;, &quot;github.issues.label&quot;],\n  &quot;task_id&quot;: &quot;task:t789&quot;,\n  &quot;exp&quot;: 1700000300\n}`, `63576749199959080000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n <span class=\"token property\">\"sub\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"user:u123\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"agent_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"agent:a456\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"tenant_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"acme\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"scopes\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"github.issues.read\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"github.issues.label\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"task_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"task:t789\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"exp\"</span><span class=\"token operator\">:</span> <span class=\"token number\">1700000300</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"capability-token-issuance-pseudocode\" style=\"position:relative;\"><a href=\"#capability-token-issuance-pseudocode\" aria-label=\"capability token issuance pseudocode permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Capability Token Issuance (Pseudocode)</strong></h3>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"65923687541305480000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`# Issue a short-lived capability token for one tool\ndef issue_capability_token(agent_session, tool_audience):\n    claims = {\n        &quot;sub&quot;: agent_session[&quot;agent_id&quot;],\n        &quot;tenant&quot;: agent_session[&quot;tenant_id&quot;],\n        &quot;aud&quot;: tool_audience,  # e.g. &quot;tool:github-triage&quot;\n        &quot;scopes&quot;: narrow_scopes(  # only scopes valid for this tool\n            agent_session[&quot;scopes&quot;], tool_audience\n        ),\n        &quot;exp&quot;: now() + 120  # 2-minute TTL\n    }\n    return sign_jwt(claims, private_key)`, `65923687541305480000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token comment\"># Issue a short-lived capability token for one tool</span>\n<span class=\"token keyword\">def</span> <span class=\"token function\">issue_capability_token</span><span class=\"token punctuation\">(</span>agent_session<span class=\"token punctuation\">,</span> tool_audience<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    claims <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">\"sub\"</span><span class=\"token punctuation\">:</span> agent_session<span class=\"token punctuation\">[</span><span class=\"token string\">\"agent_id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"tenant\"</span><span class=\"token punctuation\">:</span> agent_session<span class=\"token punctuation\">[</span><span class=\"token string\">\"tenant_id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"aud\"</span><span class=\"token punctuation\">:</span> tool_audience<span class=\"token punctuation\">,</span>  <span class=\"token comment\"># e.g. \"tool:github-triage\"</span>\n        <span class=\"token string\">\"scopes\"</span><span class=\"token punctuation\">:</span> narrow_scopes<span class=\"token punctuation\">(</span>  <span class=\"token comment\"># only scopes valid for this tool</span>\n            agent_session<span class=\"token punctuation\">[</span><span class=\"token string\">\"scopes\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> tool_audience\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"exp\"</span><span class=\"token punctuation\">:</span> now<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token number\">120</span>  <span class=\"token comment\"># 2-minute TTL</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> sign_jwt<span class=\"token punctuation\">(</span>claims<span class=\"token punctuation\">,</span> private_key<span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"dpop-validation-at-the-tool-server-pseudocode\" style=\"position:relative;\"><a href=\"#dpop-validation-at-the-tool-server-pseudocode\" aria-label=\"dpop validation at the tool server pseudocode permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>DPoP Validation at the Tool Server (Pseudocode)</strong></h3>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"84482929961206870000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`def validate_dpop_request(http_request):\n    proof = parse_dpop_proof(http_request.headers[&quot;DPoP&quot;])\n    token = parse_bearer_token(http_request.headers[&quot;Authorization&quot;])\n\n    assert proof[&quot;htu&quot;] == http_request.url\n    assert proof[&quot;htm&quot;] == http_request.method\n    assert token[&quot;cnf&quot;][&quot;jkt&quot;] == thumbprint(proof[&quot;jwk&quot;])\n\n    assert not replay_cache.seen(proof[&quot;jti&quot;])\n    replay_cache.add(proof[&quot;jti&quot;], ttl=120)\n\n    return evaluate_policy(token)`, `84482929961206870000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def</span> <span class=\"token function\">validate_dpop_request</span><span class=\"token punctuation\">(</span>http_request<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    proof <span class=\"token operator\">=</span> parse_dpop_proof<span class=\"token punctuation\">(</span>http_request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span><span class=\"token string\">\"DPoP\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    token <span class=\"token operator\">=</span> parse_bearer_token<span class=\"token punctuation\">(</span>http_request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span><span class=\"token string\">\"Authorization\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">assert</span> proof<span class=\"token punctuation\">[</span><span class=\"token string\">\"htu\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> http_request<span class=\"token punctuation\">.</span>url\n    <span class=\"token keyword\">assert</span> proof<span class=\"token punctuation\">[</span><span class=\"token string\">\"htm\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> http_request<span class=\"token punctuation\">.</span>method\n    <span class=\"token keyword\">assert</span> token<span class=\"token punctuation\">[</span><span class=\"token string\">\"cnf\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"jkt\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> thumbprint<span class=\"token punctuation\">(</span>proof<span class=\"token punctuation\">[</span><span class=\"token string\">\"jwk\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">assert</span> <span class=\"token keyword\">not</span> replay_cache<span class=\"token punctuation\">.</span>seen<span class=\"token punctuation\">(</span>proof<span class=\"token punctuation\">[</span><span class=\"token string\">\"jti\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    replay_cache<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">(</span>proof<span class=\"token punctuation\">[</span><span class=\"token string\">\"jti\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> ttl<span class=\"token operator\">=</span><span class=\"token number\">120</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">return</span> evaluate_policy<span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"policy-evaluation-pseudocode\" style=\"position:relative;\"><a href=\"#policy-evaluation-pseudocode\" aria-label=\"policy evaluation pseudocode permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Policy Evaluation (Pseudocode)</strong></h3>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"45434212907816130000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`def evaluate_policy(token):\n    decision = policy_engine.check(\n        subject=token[&quot;sub&quot;],\n        tenant=token[&quot;tenant&quot;],\n        action=current_action(),\n        resource=current_resource(),\n        scopes=token[&quot;scopes&quot;]\n    )\n\n    audit_log(token, decision)\n\n    return decision  # allow | deny + reason`, `45434212907816130000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def</span> <span class=\"token function\">evaluate_policy</span><span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    decision <span class=\"token operator\">=</span> policy_engine<span class=\"token punctuation\">.</span>check<span class=\"token punctuation\">(</span>\n        subject<span class=\"token operator\">=</span>token<span class=\"token punctuation\">[</span><span class=\"token string\">\"sub\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        tenant<span class=\"token operator\">=</span>token<span class=\"token punctuation\">[</span><span class=\"token string\">\"tenant\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        action<span class=\"token operator\">=</span>current_action<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        resource<span class=\"token operator\">=</span>current_resource<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        scopes<span class=\"token operator\">=</span>token<span class=\"token punctuation\">[</span><span class=\"token string\">\"scopes\"</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span>\n\n    audit_log<span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">,</span> decision<span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">return</span> decision  <span class=\"token comment\"># allow | deny + reason</span></code></pre></div>\n<h2 id=\"example-build-github-triage-agent-end-to-end\" style=\"position:relative;\"><a href=\"#example-build-github-triage-agent-end-to-end\" aria-label=\"example build github triage agent end to end permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Example Build: “GitHub Triage” Agent (End-to-End)</h2>\n<h3 id=\"allowed-actions\" style=\"position:relative;\"><a href=\"#allowed-actions\" aria-label=\"allowed actions permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Allowed Actions</strong></h3>\n<p>The triage agent may apply labels, assign issues, and post comments. It may not delete issues, push code, or modify repository settings. These boundaries are encoded in the capability token scopes, not in the system prompt.</p>\n<h3 id=\"policy-configuration\" style=\"position:relative;\"><a href=\"#policy-configuration\" aria-label=\"policy configuration permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Policy Configuration</strong></h3>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"29007993931648790000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`# triage-agent-policy.yaml\nagent: github-triage\ntenant_scope: per_org\n\nallowed_actions:\n  - github.issues.label\n  - github.issues.assign\n  - github.issues.comment\n\nrate_limits:\n  per_hour: 200\n  business_hours_only: false\n\nhitl_triggers:\n  - action: github.issues.move_repo\n    ruleset: must-approve\n  - cost_usd_per_task: 5.00\n    ruleset: soft-hold`, `29007993931648790000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token comment\"># triage-agent-policy.yaml</span>\n<span class=\"token key atrule\">agent</span><span class=\"token punctuation\">:</span> github<span class=\"token punctuation\">-</span>triage\n<span class=\"token key atrule\">tenant_scope</span><span class=\"token punctuation\">:</span> per_org\n\n<span class=\"token key atrule\">allowed_actions</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">-</span> github.issues.label\n  <span class=\"token punctuation\">-</span> github.issues.assign\n  <span class=\"token punctuation\">-</span> github.issues.comment\n\n<span class=\"token key atrule\">rate_limits</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">per_hour</span><span class=\"token punctuation\">:</span> <span class=\"token number\">200</span>\n  <span class=\"token key atrule\">business_hours_only</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">false</span>\n\n<span class=\"token key atrule\">hitl_triggers</span><span class=\"token punctuation\">:</span>\n  <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">action</span><span class=\"token punctuation\">:</span> github.issues.move_repo\n    <span class=\"token key atrule\">ruleset</span><span class=\"token punctuation\">:</span> must<span class=\"token punctuation\">-</span>approve\n  <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">cost_usd_per_task</span><span class=\"token punctuation\">:</span> <span class=\"token number\">5.00</span>\n    <span class=\"token key atrule\">ruleset</span><span class=\"token punctuation\">:</span> soft<span class=\"token punctuation\">-</span>hold</code></pre></div>\n<h3 id=\"sample-audit-log-deny-event\" style=\"position:relative;\"><a href=\"#sample-audit-log-deny-event\" aria-label=\"sample audit log deny event permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Sample Audit Log (Deny Event)</strong></h3>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"77202868260585780000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\n  &quot;event&quot;: &quot;tool_call_denied&quot;,\n  &quot;agent_id&quot;: &quot;agent:triage-01&quot;,\n  &quot;tenant_id&quot;: &quot;acme&quot;,\n  &quot;action&quot;: &quot;github.issues.delete&quot;,\n  &quot;resource&quot;: &quot;repo:acme/payments#441&quot;,\n  &quot;reason&quot;: &quot;action_not_in_allow_list&quot;,\n  &quot;trace_id&quot;: &quot;4bf92f3577b34da6&quot;,\n  &quot;timestamp&quot;: &quot;2025-11-01T14:23:07Z&quot;\n}`, `77202868260585780000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"event\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"tool_call_denied\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"agent_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"agent:triage-01\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"tenant_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"acme\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"action\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"github.issues.delete\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"resource\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"repo:acme/payments#441\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"reason\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"action_not_in_allow_list\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"trace_id\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"4bf92f3577b34da6\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"timestamp\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"2025-11-01T14:23:07Z\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"rollout-plan-and-guardrails\" style=\"position:relative;\"><a href=\"#rollout-plan-and-guardrails\" aria-label=\"rollout plan and guardrails permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Rollout Plan and Guardrails</h2>\n<h3 id=\"promotion-phases\" style=\"position:relative;\"><a href=\"#promotion-phases\" aria-label=\"promotion phases permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Promotion Phases</strong></h3>\n<table>\n<thead>\n<tr>\n<th><strong>Phase</strong></th>\n<th><strong>Capability</strong></th>\n<th><strong>Exit Criteria</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Dev</td>\n<td>Full access in an isolated sandbox</td>\n<td>Zero policy violations in 48h</td>\n</tr>\n<tr>\n<td>Shadow</td>\n<td>Live traffic; all actions logged but not executed</td>\n<td>Deny rate &#x3C; 1%</td>\n</tr>\n<tr>\n<td>Read-only</td>\n<td>Read operations only in production</td>\n<td>Stable for 5 business days</td>\n</tr>\n<tr>\n<td>Partial write</td>\n<td>Writes to low-risk resources; HITL on all others</td>\n<td>Approval rate &#x3C; 20%</td>\n</tr>\n<tr>\n<td>Full write</td>\n<td>Full-scoped write access; HITL on high-risk only</td>\n<td>Deny rate stable; no anomalies</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"metrics-to-track\" style=\"position:relative;\"><a href=\"#metrics-to-track\" aria-label=\"metrics to track permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Metrics to Track</strong></h3>\n<ul>\n<li>Approval rate (target: trending toward zero for routine ops)</li>\n<li>Deny rate (spikes indicate policy gaps or attack activity)</li>\n<li>MTTR for stuck approvals (SLA: under 30 minutes during business hours)</li>\n</ul>\n<h3 id=\"kill-switches\" style=\"position:relative;\"><a href=\"#kill-switches\" aria-label=\"kill switches permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Kill Switches</strong></h3>\n<p>Every agent deployment should have a per-tenant circuit breaker (disable one tenant’s agent without affecting others), a feature flag (disable globally in under 30 seconds), and a hard cost cap (agent halts if cumulative spend exceeds threshold within a rolling window).</p>\n<h2 id=\"compliance-and-data-residency\" style=\"position:relative;\"><a href=\"#compliance-and-data-residency\" aria-label=\"compliance and data residency permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Compliance and Data Residency</h2>\n<h3 id=\"pii-in-prompts-and-outputs\" style=\"position:relative;\"><a href=\"#pii-in-prompts-and-outputs\" aria-label=\"pii in prompts and outputs permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>PII in Prompts and Outputs</strong></h3>\n<p>Log the minimum necessary. Hash or truncate PII in audit logs. Define retention windows per data class, for example, 30 days for tool invocation metadata, 7 days for prompt/response hashes, and immediate deletion for any inadvertently logged credentials.</p>\n<h3 id=\"residency-tags\" style=\"position:relative;\"><a href=\"#residency-tags\" aria-label=\"residency tags permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Residency Tags</strong></h3>\n<p>Attach a <code class=\"language-text\">data_region</code> claim to every agent session. Route token issuance, key storage, and audit log writes to region-specific infrastructure. Encryption keys should never leave their designated region.</p>\n<h3 id=\"access-reviews\" style=\"position:relative;\"><a href=\"#access-reviews\" aria-label=\"access reviews permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Access Reviews</strong></h3>\n<p>Conduct quarterly reviews of all agent scopes and capability token definitions. Flag any scope that has not been exercised in 90 days as a candidate for removal. Automated scope-usage reporting from audit logs makes this tractable.</p>\n<h2 id=\"implementation-checklist\" style=\"position:relative;\"><a href=\"#implementation-checklist\" aria-label=\"implementation checklist permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Implementation Checklist</h2>\n<p><strong>Identity and Tokens</strong></p>\n<ul>\n<li>Tenant-scoped identity + short-lived tokens</li>\n<li>Capability tokens per tool (audience-restricted)</li>\n<li>DPoP/MTLS for secure calls + RFC 8693 exchange</li>\n<li>Re-auth required for break-glass</li>\n</ul>\n<p><strong>Policy and HITL</strong></p>\n<ul>\n<li>Tools as resources; policy enforced at tool server</li>\n<li>RBAC/FGA for access control + risk-based routing</li>\n<li>Approval flow with context, timeouts, and audit logs</li>\n</ul>\n<p><strong>Observability</strong></p>\n<ul>\n<li>Structured logs + end-to-end trace IDs</li>\n<li>Alerts (denies, backlog, anomalies) + key dashboards</li>\n</ul>\n<p><strong>Threats and Controls</strong></p>\n<ul>\n<li>Sanitize tool outputs + enforce allow-lists</li>\n<li>Signed dependencies + restricted network egress</li>\n<li>Content filters + replay protection</li>\n</ul>\n<h2 id=\"faqs\" style=\"position:relative;\"><a href=\"#faqs\" aria-label=\"faqs permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>FAQs</h2>\n<ul>\n<li><strong>Do I need DPoP if I’m already using MTLS?</strong> No. MTLS is enough if you already have a certificate infrastructure. DPoP is a simpler alternative without full PKI. Both solve token replay and theft.</li>\n<li><strong>Should agents share user tokens or get their own?</strong> Agents must have their own tokens. Sharing user tokens breaks security, auditability, and revocation control.</li>\n<li><strong>How do I sandbox tools that call the internet?</strong> Use a network allow-list. Block all outbound traffic by default, allow only required domains, and filter requests/responses to prevent SSRF and data leaks.</li>\n<li><strong>What is the simplest HITL pattern to start with?</strong> Use a cost threshold. If a task exceeds a set limit, pause and ask for user approval.</li>\n</ul>\n<h2 id=\"cta\" style=\"position:relative;\"><a href=\"#cta\" aria-label=\"cta permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>CTA</h2>\n<p><strong>Try the sample app:</strong> A reference implementation of agent-safe authentication by using SuperTokens, complete with scoped capability tokens, DPoP proof validation, HITL approval hooks, and a structured audit trail is available as a runnable repository. It demonstrates every pattern covered in this post in a single deployable stack.</p>\n<p><strong>Book 30-minute office hours:</strong> Teams designing agent authentication architectures from scratch can schedule a design review session to walk through token strategy, policy modeling, and HITL trigger configuration for their specific use case.</p>\n<p>The patterns described in this post, scoped capability tokens, sender-constrained transport, tool-server policy enforcement, and evidence-backed approvals, are the minimum viable security posture for any AI agent operating in a production environment with real data and real consequences.</p>","frontmatter":{"date":"March 22, 2026","title":"Authentication for AI Agents: Tokens, Tool Calls, and Human-in-the-Loop","cover":"auth-for-ai-agents.png","author":"Mostafa Ibrahim","description":"Secure AI agents with scoped tokens, DPoP, tool-level policies, and human approvals."},"fields":{"slug":"/auth-for-ai-agents/"}},"site":{"siteMetadata":{"title":"SuperTokens Blog"}}},"pageContext":{"id":"179a44ca-0f4c-58e3-865e-2a56629ae7f2","fields__slug":"/auth-for-ai-agents/","__params":{"fields__slug":"auth-for-ai-agents"}}},
    "staticQueryHashes": []}