Five Patterns for Secret-Safe AI Agent Deployments
Concrete patterns for securing AI agent access to credentials: per-agent identity, least-privilege policies, blind mode, audit trails, and automated rotation
Why AI Agents Need Specific Security Patterns
AI agents differ from traditional services in how they handle credentials. A traditional microservice receives a database password at startup and uses it for a single purpose. An AI agent may need multiple credentials, use them across unpredictable tool calls, and maintain conversation state where credential values can persist. These five patterns address the specific risks of AI agent deployments.
Pattern 1: Per-Agent Identity
The Problem
When multiple AI agents share a single service account or API key, you cannot distinguish which agent accessed which secret. Compromise of one agent compromises all agents. Revocation requires rotating credentials for every agent simultaneously.
The Implementation
Assign each agent instance a unique JWT identity. Configure your identity provider to issue JWTs with agent-specific claims:
{
"iss": "https://auth.internal",
"sub": "agent:code-reviewer-prod-01",
"aud": "keyzero",
"agent_name": "code-reviewer",
"agent_instance": "prod-01",
"environment": "production",
"exp": 1711900800
}
Configure the KeyZero issuer to map these claims:
issuers:
- name: agent-platform
type: custom
issuer_url: "https://auth.internal"
audience: "keyzero"
map:
service: "claims.agent_name"
env: "claims.environment"
actor: "claims.agent_instance"
Now every resolve request is tied to a specific agent identity. Policies can target individual agents, and audit logs show exactly which agent accessed which secret.
The Benefit
Per-agent identity enables granular revocation. If code-reviewer-prod-01 is compromised, you block that specific identity without affecting code-reviewer-prod-02 or any other agent.
Pattern 2: Least-Privilege Policies
The Problem
AI agents often receive access to more secrets than they need. A code-generation agent that only needs a GitHub token may have access to database credentials, payment processor keys, and cloud provider secrets because they share a deployment environment.
The Implementation
Write CEL policies that scope each agent to exactly the secrets it requires. For a comprehensive guide to CEL policy design, see policy-based access control for machine identities:
policies:
- name: code-reviewer-github-only
description: "Code reviewer agent can only access GitHub tokens"
rule: "service == 'code-reviewer' && ref.matches('secret/data/api/github/**')"
effect: allow
- name: deploy-agent-cloud-only
description: "Deploy agent can access cloud credentials but not API keys"
rule: "service == 'deploy-agent' && ref.matches('secret/data/cloud/**')"
effect: allow
- name: support-agent-readonly
description: "Support agent can read monitoring credentials only"
rule: "service == 'support-agent' && ref.matches('secret/data/monitoring/**')"
effect: allow
- name: default-deny
rule: "true"
effect: deny
Use ref.matches() with the narrowest possible glob pattern. secret/data/api/github/read-token is better than secret/data/api/**. The more specific the path, the smaller the blast radius.
The Benefit
If an agent is compromised or manipulated through prompt injection, it can only access the secrets its policy allows. A code reviewer that is tricked into exfiltrating secrets can only leak GitHub tokens, not database passwords.
Pattern 3: Blind Mode by Default
The Problem
Even with per-agent identity and least-privilege policies, an agent that receives a raw credential can log it, include it in generated output, or send it to an unintended endpoint. The credential exists in the agent's process memory and context window. Blind mode addresses this directly.
The Implementation
Run all AI agent workloads with blind mode enabled:
kz run --blind -- node ai-agent.js
The agent sees masked tokens in its environment:
GITHUB_TOKEN=kz_masked_a3f8c2...
OPENAI_API_KEY=kz_masked_9d1b7e...
The MITM proxy swaps these for real values on outgoing requests. Combine with connection control to restrict which hosts the agent can reach:
[blind]
[[blind.connections]]
allow = true
host = "api.github.com"
[[blind.connections]]
allow = true
host = "api.openai.com"
[[blind.connections]]
allow = false
host = "*"
For MCP-based agents -- such as those built with Claude Code -- use the fetch tool instead of resolve. The fetch tool resolves the credential and makes the HTTP request server-side -- the agent receives the API response but never sees the credential at all:
Agent calls: fetch(jwt, resource="api/key", resolver="key", url="https://api.github.com/repos")
Agent receives: { "status": 200, "body": "[{\"name\": \"repo1\"}, ...]" }
The Benefit
The credential never enters the agent's address space. It cannot be logged, leaked through prompt injection, or persisted in conversation history. The attack surface is reduced to the proxy process itself.
Pattern 4: Audit Trails
The Problem
Without audit logging, you cannot answer basic questions: Which agent accessed which secret? When? How often? Was it authorized? Post-incident investigation is impossible without this data.
The Implementation
The KeyZero PDP server logs every resolve request with the verified identity from the JWT. Each log entry contains:
- Timestamp of the request
- Identity fields (
issuer,org,service,env,actor) from the verified JWT - Requested resource (
ref) -- the secret path - Policy decision -- which policy matched, and whether the effect was allow or deny
- Resolution result -- success or failure (without logging the secret value)
Configure the bundle with descriptive policy names so audit logs are human-readable:
policies:
- name: code-reviewer-github-access
description: "Code reviewer agent accessing GitHub tokens"
rule: "service == 'code-reviewer' && ref.matches('secret/data/api/github/**')"
effect: allow
When this policy matches, the log entry shows policy: code-reviewer-github-access with effect: allow, making it clear what happened and why.
The Benefit
Audit trails enable incident response, compliance reporting, and anomaly detection. If an agent suddenly starts requesting secrets it has never accessed before, the audit log reveals the pattern immediately.
Pattern 5: Automated Secret Rotation
The Problem
Static credentials accumulate risk over time. The longer a credential is valid, the more opportunities for leakage. Manual rotation is error-prone and creates downtime when old credentials are revoked before new ones are deployed.
The Implementation
Combine vault-level rotation with KeyZero's runtime resolution. The key insight: because KeyZero resolves secrets at runtime rather than baking them into configuration files, rotation at the vault level propagates automatically. If you are still using static .env files, start with the migration path from hardcoded secrets to zero-knowledge.
Step 1: Configure rotation in your vault.
AWS Secrets Manager supports automatic rotation with Lambda functions. HashiCorp Vault has dynamic secrets that generate credentials on demand. 1Password supports credential rotation through automation recipes.
Step 2: Use short-lived resolution for high-value credentials.
For credentials where you want the tightest rotation window, use short_lived mode in the KeyZero server bundle:
resources:
- ref: "secret/data/api/payment/**"
resolver: vault-prod
mode: short_lived
ttl: 300
credential_location: "header:Authorization:Bearer"
Short-lived mode issues a JWT token valid for 300 seconds (configurable). The credential-swapping proxy (kz server start --proxy) exchanges the token for the real credential at the network boundary. When the token expires, the agent must request a new one, which triggers a fresh resolution from the vault.
Step 3: For CLI workloads, leverage process lifecycle.
With kz run, secrets are resolved at process startup. Restart the process to pick up rotated credentials. For long-running agent processes, combine with a process supervisor that restarts on a schedule or on signal.
The Benefit
Rotation happens at the vault without any changes to agent configuration or deployment. Short-lived mode ensures that even if a token is exfiltrated, it expires within minutes. The combination of vault rotation and runtime resolution eliminates the "rotated but not revoked" problem where old credentials persist in configuration files.
Combining the Patterns
These five patterns are designed to layer. A production AI agent deployment should use all five:
- Per-agent identity -- each agent has a unique JWT
- Least-privilege policies -- CEL rules scoped to exact secret paths
- Blind mode -- agents never see raw credentials
- Audit trails -- every access is logged with identity context
- Automated rotation -- vault rotation with runtime resolution
The result is a deployment where no agent holds raw credentials, every access is authorized and logged, and credential compromise is contained to a single agent's narrow permission scope with a short validity window.