Meta's research team recently published how they secure Muse. It is the most concrete write-up of agent security anyone has shipped this year, and reading it was a strange experience: two teams who have never spoken arrived at the same first principle, and in places the same mechanism. So here is ours, at the same level of detail — what each control actually does, and where the gaps still are.
The premise is one sentence. An agent that holds a credential has already lost it. It sits in the context window, it gets logged, it lands in memory and debug traces, and every prompt injection the agent meets on the open web becomes an attack on that key. Everything below follows from refusing to put one there.
Credential surrogation
Meta calls it credential surrogation: the agent gets a surrogate, and the real token is swapped in at the network boundary. We call the same idea placeholder rehydration, and it is the centre of the product.
A secret in a prompt becomes a typed placeholder — a tag that encodes which vault path it stands for, plus a checksum. The model sees only the tag. The plaintext is substituted inside an AMD SEV-SNP enclave, and only when four things hold at once:
- the placeholder resolves to a secret this org actually owns;
- a human has bound that secret to this tool, by name;
- bound it to this argument, by JSON path — a key authorised for an
Authorizationheader cannot be substituted into a request body; - and the destination host is on that binding's allowlist.
A bad checksum, an unknown tag, a wrong destination, or the same placeholder repeated more times than a real call would need — each is a refusal, not a warning. The agent never receives the value in any of those paths.
Inspection lives outside the agent
Meta makes a point of running their safety classifiers outside the runtime cell, so an attacker who lands inside cannot switch them off. We reached the same conclusion by a different route: Shroud, our inspection proxy, is a separate service in a separate trust domain. The agent cannot disable it, because the agent cannot reach it as anything other than a client.
Streaming responses are inspected frame by frame, which matters more than it sounds. A secret that arrives split across three chunks is still a secret; a rolling window holds back just enough of the tail to catch one spanning a boundary, and redacts it before a byte reaches the client. Injection is scored in both directions — on the way to the model and on the way back — because a tool result carrying instructions is the same attack as a prompt carrying them.
Tainted arguments
This one we took from Muse directly. Their tainted egress tracks data flow in the kernel with eBPF: a process that has touched user data needs approval to send anything out. We run on managed containers and cannot do that. But the property worth having survives the translation — content that came from outside and is trying to issue instructions does not get a live credential.
Before any placeholder becomes plaintext, the enclave scores the tool arguments it is about to write into. Above a threshold, the substitution is refused and the call fails with a reason. Two details make it work without kernel support or session state: the arguments leaving the enclave are the content, so the check is local to one request; and the enclave scores them itself rather than accepting a caller-supplied verdict. A "not tainted" flag on the request would be worth nothing, because the caller is the thing being defended against.
Untrusted input is cleaned before the agent reads it
Muse strips one-time tokens, password-reset links and magic links out of connector content. We now do the same on inbound channel messages — Discord, SMS, WhatsApp, Telegram. An agent with a mailbox is the cheapest route an attacker has to a live credential, and unlike an API key these arrive as ordinary prose that no secret scanner recognises.
Matching runs on a copy with invisible characters normalised, so a code fragmented with Unicode tag characters — which reads as a code to a human and as nothing to a matcher — is still caught. And because these credentials are single-use, redacting them costs the agent nothing it could legitimately have acted on. The human still got the real message.
Approvals are capabilities, not suggestions
The line in Meta's post worth stealing outright: "Approvals granted via the human in the loop system are strict capabilities, not conversational suggestions. They're bound to the particular connector/destination and use case."
Our payment path already works this way. When an agent pays an x402 paywall, the client sends the raw challenge bytes; the vault computes the digest and decides. The digest binds the actual transfer value, not the challenge's ceiling, so what a human approved and what gets signed cannot drift. A null allowlist means the agent pays nobody.
Signing follows the same shape. Address allowlists, per-transaction caps, daily limits and simulation before broadcast — and widening a guardrail does not take effect when you click save. It queues behind a policy-change approval, and shadow mode lets you measure a rule against real traffic before enforcing it. Risk tier is derived server-side from policy and payload, so an agent cannot talk its way into the weakest approval channel.
Short-lived everything
Hosted agent runtimes get a short-lived JWT, never a durable key. That is a deliberate trade and it has a cost: a credential that expires is a credential that can expire at the wrong moment, and keeping one fresh across a scale-to-zero container is real work rather than a checkbox. We think it is the right trade anyway, for the same reason surrogation is: the thing most likely to leak is the thing that lives longest.
Underneath, secrets are envelope-encrypted with a per-secret data key, the key-encryption key lives in a hardware security module, and the encryption is bound to the secret's vault and path — so a ciphertext moved to another path does not decrypt. Row-level security fails closed: no org context, no rows. The audit log is an HMAC hash chain, so an entry cannot be altered without breaking every entry after it.
What we have not built yet
A security page that lists only wins is marketing. Three things in Meta's design we do not have, and want:
- A single egress authority. Their Sentinel is the sole permission point for all network egress, evaluating hostname, resolved IP, port, method and decoded body. Ours is enforced per executor. That is a worse shape, and it is where our own audits keep finding bugs.
- Kernel-level data-flow tracking. Our taint check is per-request and pattern-based. Theirs follows data through processes. Ours is weaker and we know it.
- Privilege separation inside the runtime. Their connector logic runs in a separate sandboxed worker with its own credential allowlist. In our runtimes the credential-holding sidecar and the agent process share a container.
Why convergence is the interesting part
Meta has an enormous security organisation and a different product, and they landed on surrogate credentials swapped in at the boundary, classifiers that run where the agent cannot switch them off, and approvals that are capabilities bound to a destination. We landed on the same three from a small team and a different starting point — watching people paste Stripe keys into prompts.
When two independent designs agree on the load-bearing parts, that is the closest thing this field currently has to evidence. The agentic economy will grow exactly as fast as the security layer allows, and the shape of that layer is becoming legible.
Ours is live, it has a free tier, and you can put an agent behind it in one command at 1claw.co.