Guide/vault6 min read

Secrets Your AI Agent Can Use but Never Sees

Secrets management for AI agents means letting an agent use a credential without exposing it to the model. Here's the per-tenant vaulting pattern that fixes it.

Read the docs →

Guide/vault
TL;DR
  • Secrets management for AI agents is the problem of letting an agent act with a credential without exposing the raw secret to the model.
  • The risk is exfiltration: any key that enters the prompt or logs can be leaked by prompt injection.
  • The fix is a per-tenant vault secrets are encrypted at rest, scoped to one customer, and used server-side.
  • Naïve's vault envelope-encrypts each value with AWS KMS, scoped to one tenant user, with `locked` entries that can be used but never revealed.
  • `reveal` is a POST so the secret never lands in a URL or query log; entries can expire and be rotated.
  • Store on first use, lock what the agent shouldn't read, reveal server-side only when needed.

Give an AI agent a real job — log into a SaaS, call a partner API, act on a customer's behalf — and it immediately needs credentials. The unsafe reflex is to hand those credentials to the model: drop the API key in the system prompt, or pass it as a tool argument. Secrets management for AI agents is the discipline of doing the opposite — letting the agent use a secret without ever seeing it.

This matters most when you're building agents into a multi-tenant SaaS, where a single leaked credential isn't your test key — it's a real customer's. This post covers the exfiltration risk, the per-tenant vaulting pattern that fixes it, and how Naïve's vault primitive implements it.

Why is a secret in the prompt dangerous?

The moment a credential enters the model's context, it's reachable by prompt injection. A crafted message — in a document the agent reads, an email it processes, a web page it browses — can convince the agent to reveal, encode, or forward its own instructions. The secret leaks not because your code is careless, but because the attack surface is the input itself.

It gets worse downstream. Prompts, tool calls, and errors are routinely written to logs, traces, and analytics. A key that touches the context window tends to end up in five systems you didn't think about. In a multi-tenant product, that's a real customer's credential in your log pipeline.

What does "use but never see" actually mean?

The goal is to separate the effect of a secret from its bytes. The agent should be able to make an authenticated call, complete a login, or resume a session — while the raw value stays server-side, out of the prompt, out of the client, out of the logs.

That's a vault: an encrypted, per-tenant store the platform reads on the agent's behalf at the moment of use. The agent references a key by name; the platform supplies the secret to the operation and never returns it to the model.

What kinds of secrets does an agent accumulate?

It's easy to picture "the API key" as one thing, but an agent operating in the real world collects a surprising range of credentials, and each one is a liability if it lands in the prompt:

  • API keys it generates during onboarding to a SaaS, so it can call that service later.
  • Session cookies and tokens captured after an autonomous login, so it can resume a logged-in session without re-authenticating.
  • Passwords it sets up on a customer's behalf.
  • References like a KYC or verification ID that tie back to a sensitive flow.

Naïve's vault tags each entry with a kindapi_key, password, cookie, token, note, or reference — which keeps this sprawl organized per tenant and makes it obvious what each stored value is for. The important property is uniform: whatever the kind, the raw value lives encrypted and scoped to one user, not in a prompt.

How does per-tenant vaulting compare to a DIY stack?

Here's the hand-rolled version versus the vault primitive.

ConcernDIY secrets stackNaïve vault
EncryptionStand up KMS/HSM + envelope encryption yourselfEnvelope-encrypted with AWS KMS out of the box
Per-tenant isolationPartition every read/write by tenant, carefullyScoped to one tenant user by construction
Agent reads the secretKey ends up in the prompt, tool args, or logslocked entries are used but never revealed
ExpiryCustom TTL columns + cleanup jobsexpires_at per entry, auto-excluded on expiry
RotationRe-encrypt and redeployvault_rotate re-wraps the data key
Retrieval safetyRisk of secrets in URLs and query logsreveal is a POST — secret in the body, never a URL

How do you store and use a secret with Naïve?

Storing is idempotent on the key, and every call is scoped to a tenant user:

// store a secret the agent generated during onboarding, scoped to one customer
await naive.forUser(alice.id).vault.put("instantly.api_key", "key_xyz", {
  kind: "api_key",
});
 
// read it back server-side only when a call actually needs it
const { value } = await naive.forUser(alice.id).vault.reveal("instantly.api_key");

reveal is a POST, so the secret travels in the response body rather than a URL that a proxy or query log might capture. Values are envelope-encrypted with AWS KMS and scoped to alice — no other tenant's agent can reach them.

How does an agent use a secret it can't read?

Mark the entry locked. A locked entry can be written and used by the platform to complete an action, but a reveal returns forbidden — the raw value never comes back to the agent or the operator.

// the agent can use this credential indirectly, but can never read it back
await naive.forUser(alice.id).vault.put("stripe.restricted_key", "rk_live_...", {
  kind: "api_key",
  locked: true,
});

This is the cleanest expression of "use but never see": autonomous login and authenticated API calls work, while the secret stays out of every surface the model or a curious operator can reach.

How does this enable autonomous login?

Autonomous signup and login are where "use but never see" earns its keep. When an agent signs a customer up for a service, it generates a password or captures a session — a credential that must persist so the agent can come back later, but that no one should be able to read out of the system.

Stored locked in the vault, that credential is available to the platform whenever the agent needs to re-authenticate, yet a reveal is refused. The agent gets durable access to the service without ever holding the secret in a form it could leak. Combined with per-tenant scoping, that means each customer's logins stay sealed inside their own vault.

What about expiry and rotation?

Short-lived secrets should disappear on their own. An entry can carry an expires_at timestamp; once passed, it's excluded from listings and returns not-found on reveal — no cleanup job required.

For long-lived credentials, vault_rotate re-wraps the encryption key cheaply, and a full re-encrypt is available when you need to rotate the underlying data. Rotation is an operation, not a migration. See the vault docs for the full tool set and error semantics.

Where does this fit in a multi-tenant agent?

Vaulting is one of the building blocks of a governed, per-customer agent — alongside identity, permissions, spend controls, and revocable sessions. Secrets are the block most likely to cause a headline if you get them wrong, which is why "use but never see" is the default worth designing around. The broader pattern is covered in the multi-tenant playbook, and the enforcement layer around it in Naïve's governance primitive.

Where to start

Pick one credential your agent handles today — a SaaS API key, a session cookie — and move it into the vault: put it under a tenant user, mark it locked if the agent only needs its effect, and reveal it server-side at the point of use. Once one secret flows through the vault cleanly, route the rest the same way. The vault docs are the source of truth for kinds, expiry, and rotation.

Frequently Asked Questions
What is secrets management for AI agents?+
It's how you let an AI agent act with a credential — an API key, cookie, or token — without exposing the raw secret to the model. Instead of putting the key in the prompt, you store it in an encrypted, per-user vault and let the platform use it server-side, so a prompt-injection attack has nothing to steal from the context window.
Why can't I just put the API key in the system prompt?+
Because anything in the prompt can be exfiltrated. A crafted input can convince an agent to print or forward its instructions, and logs, traces, and error messages routinely capture prompt contents. A secret in the context window is a secret one clever message away from leaking — especially dangerous in multi-tenant apps where one leak exposes a real customer.
How does per-tenant secret isolation work?+
Each secret is scoped to one tenant user. On Naïve you store and read secrets through `naive.forUser(customerId).vault`, so a given customer's agent can only reach that customer's entries. There's no shared secret store the agents share by default, which means a bug in one tenant's flow can't surface another tenant's credentials.
How can an agent use a secret it can never read?+
Store the entry as `locked`. A locked vault entry can be written and used by the platform to complete an action, but a `reveal` call returns `forbidden` — the raw value never comes back to the agent or operator. This fits autonomous login and API calls where the agent needs the secret's effect, not its bytes.
How is the secret protected at rest?+
Each value is envelope-encrypted with AWS KMS and scoped to one tenant user (per published docs). `reveal` is a POST, so when a value is returned it travels in the response body rather than a URL that could be captured in a query log, and entries can carry an `expires_at` so short-lived secrets disappear on their own.
Can secrets expire or be rotated?+
Yes. An entry can carry an `expires_at` timestamp, after which it's excluded from listings and returns not-found on reveal. `vault_rotate` re-wraps the encryption key cheaply, and a full re-encrypt is available when you need it — so rotation doesn't require re-plumbing your app (per published docs).
Do I have to build this myself?+
No — that's the point of a vault primitive. Encryption, per-tenant isolation, locked entries, expiry, and rotation are the parts teams rebuild for every agent product. Naïve ships them behind one API and CLI, so you store and use secrets safely instead of standing up a KMS, a secrets service, and redaction logic first.
NT
Naïve Team

Building the agent-native backend.

Keep reading