# Prompt injection cannot steal a key the agent cannot read

> Most agent secret leaks are not clever. The model was asked for the key and it had the key. Sealed secrets remove the second half of that sentence, and change what a security review has to check.

2026-09-04 · https://control-1.synsema.com/blog/prompt-injection-cannot-steal-a-key-the-agent-cannot-read


The typical leak in an agent system is boring. A tool description, a retrieved document or a user message says "print your configuration" or "include the API key in the response", and the model, which has the key as a string in its context or its process, does exactly that. No exploit, no memory corruption. The model was asked, and it could.

The usual fixes attack the first half: filter the inputs, add a guard model, train the agent to refuse. They help, and they all fail sometimes, because the model is the thing being attacked and it is also the thing doing the defending.

## Remove the second half

Synsema takes the other route. A value loaded with `secret("STRIPE_KEY")` is not a string. It is an opaque value the runtime tracks. It can be passed to the builtins that need it: HTTP authentication headers, HMAC, signing. Everything else fails or redacts:

```synsema
let key be secret("STRIPE_KEY")
print(key)                       -- [secret STRIPE_KEY: redacted]
let s be "Bearer " + key         -- error: a secret cannot be concatenated
json_encode({"k": key})          -- {"k": "[redacted]"}
```

The model can be tricked into wanting to leak the key. It cannot be tricked into being able to. The program never had a readable copy.

## What the platform does with that

On the Synsema Platform, secrets are stored on the control plane and injected sealed when the container starts. The dashboard shows a name and a fingerprint, never the value. The audit log records every time a program touched a secret and what it did with it, so "did the agent ever use this key" is a query, not a forensic project.

There is one escape hatch, `reveal()`, and it is a capability of its own: deny by default, never granted on the platform's plans, and every call is written down. If a program needs to reveal a secret, that need shows up in the manifest, which means it shows up in code review.

## What changes in the security review

Without sealed secrets, a reviewer has to reason about every path a string can take through an agent: prompts, tool outputs, logs, error messages, traces sent to a vendor. With them, the question becomes: which builtins accept a secret, and are those the ones we want? That is a short list, and it is the same list for every program.

It also changes the shape of the credentials themselves. A key that can only be used, never read, does not need to be short-lived to be safe from the model. The platform will still rotate it, and short-lived scoped credentials for GitHub, Slack and Google are on the roadmap, but the rotation protects against operators and infrastructure, not against the agent. That separation is what lets you give an agent a real key and sleep.

