Open source · by Mandu Security

Your agent sees the dumpling, never the filling.

Mandu keeps your secrets for you, and keeps your AI agent useful. Run Claude Code, Codex and other coding agents next to your real keys and secret files. The AI sees your secrets masked. You, and the programs that need them, still get the real values.

Mandu ~/proj/.env STRIPE_KEY=sk_live… Your agentClaude Code, Codex… The AI seesSTRIPE_KEY=<MDU:masked> 2The AI writes curl api.stripe.com-u<MDU:masked> api.stripe.com sk_live… 1 3 Mandu ~/proj/.env STRIPE_KEY=sk_live… Your agentClaude Code, Codex… The AI seesSTRIPE_KEY=<MDU:masked> 2The AI writes curl api.stripe.com-u<MDU:masked> api.stripe.com sk_live… 1 3
  1. Masked at the border. On its way in, Mandu swaps your key, sk_live…, for <MDU:masked>. The AI never sees the real value.

  2. Keep working. The AI puts <MDU:masked> in a command without knowing the real value.

  3. Checked at the border. On its way out, Mandu swaps the same real key back only for a program or host its rule allows, like api.stripe.com, or with your OK. Otherwise it stays masked or is blocked.

Get started

Five commands.

Mandu uses your agent's own login, Claude Pro and Max included, and needs no API key of its own. No account, no Mandu server and no telemetry: everything Mandu stores stays in ~/.mandu.

Mandu is in an invited beta. The first command uses the GitHub CLI, gh, signed in to an invited account. Without it, download the files of the latest release and run sh install.sh in that folder. To join the beta, write to us (see Company).

# install: needs an invited GitHub account, no sudo (after the launch: npm install -g @mandusec/mandu)
$ gh release download -R ManduSec/mandu -p '*' --clobber && sh install.sh

# check this computer: Node, git, your agent, your secrets
$ mandu doctor

# your agent, protected (or: mandu codex)
$ mandu claude
mandu: mode secrets (14 credential files masked, 3 env vars masked)
mandu: dashboard http://127.0.0.1:38121/

# what was protected, and what waits for your OK
$ mandu dashboard

# make it the default
$ alias claude="mandu claude"

The same file, read two ways.

The AI gets a stand-in it can pass around and put in a command. The file on your disk keeps the real value, and Mandu hands it only to the programs and hosts you allow, like api.stripe.com. Only secrets change.

~/proj/.env

The AI sees

STRIPE_KEY=<MDU:masked>
GH_TOKEN=<MDU:masked>
DEBUG=1

On your disk, and for the programs and hosts you allow

STRIPE_KEY=sk_live_51Hq8xT2…
GH_TOKEN=ghp_7Qx2Lm9a…
DEBUG=1

The problem

Your agent is leaking your secrets. Every request.

Coding agents read your files as they work, .env included. Once a key is read, it rides along in every request to the AI company. And a key the agent holds goes to any server a command names.

YOUR COMPUTER OUTSIDE You“How much did we chargeon Stripe last week?” Your question has no key in it. ~/proj/.envSTRIPE_KEY=sk_live_51Hq… AI agentClaude CodeCodexsk_live_51Hq… sk_live_51Hq… ✓ sk_live… sk_live_51Hq… AI providerAnthropicOpenAIgets it with every request api.stripe.comthe one place it had to go Other destinationsa gist, a webhook, a tunnel 1 ✓ 2 YOUR COMPUTER OUTSIDE You“How much did wecharge on Stripelast week?” no key in it ~/proj/.envSTRIPE_KEY=sk_live_51Hq… AI agentClaude CodeCodexsk_live_51Hq… sk_live_51Hq… AI providerAnthropicOpenAI api.stripe.comthe one place it had to go Other destinationsa gist, a webhook, a tunnel 1 ✓ 2
Only api.stripe.com needed your key. The AI company got it anyway, and so can any server a command names.

Two ways your secrets leak

1In every request to the AIOnce read, the key sits in the agent's context and goes out with every call.the AI company
2In a command the agent runscurl with the key, to whatever host the command names.any server: a gist, a webhook, a tunnel

What it looks like

> how much did we charge on Stripe last week? ● Read(.env) STRIPE_KEY=sk_live_51Hq8xT2… ● Bash(curl api.stripe.com/v1/charges \ -u sk_live_51Hq8xT2…:) ● Last week: $12,480 across 37 payments.

Not only .env

~/.ssh/id_ed25519~/.aws/credentials~/Documents/health/lab-results.md~/work/board-memo-q4.md

SSH keys and cloud logins, and any document you would not hand to an outside company: health records, a contract, a board memo. Whatever the agent opens goes to the AI company too.

The obvious fix: block the agent from reading .env. Then the key stays home, and the agent can't use it either.

Why not a sandbox or a vault? Read the post.

How it works

Masked for the AI. Ready for the task.

For a secret whose rule says “No AI”, Mandu keeps the real value out of the AI’s conversation while letting approved tools use it. The figure at the top shows the three steps: masked at the border, keep working, checked at the border.

You choose which AI may read a secret and where it may be sent. Your OK for a send never overrides the rule about which AI may read it.

Inside a placeholder and a rule Format, model access, destinations, and configuration

Only Mandu’s local table turns a placeholder back into the real value; the agent can neither read nor change that table. Other illustrations on this page use <MDU:masked> as shorthand.

$MDU:PrefixMarks a Mandu placeholder
nomodel_Model accessNo AI may read this secret
ReadToolThe tool that read the secret
[9bbd]IDA reference in the local table
.STRIPE_KEYSecret nameHelps the AI choose the right placeholder

Where the real value goes

curl api.stripe.com/v1/charges \
  -u $MDU:nomodel_Read[9bbd].STRIPE_KEY:
✓A host its rule lists. The request to api.stripe.com gets the real key, and only that request.
✓A program its rule names. ssh may open your key, but only when git started it. Your deploy tool opens its token on your word. Every other program is refused or gets the masked copy.
✓You. The agent's replies show you the real values; the AI keeps the placeholders.
# ~/.mandu/policy.yaml
paths:
  - path: ~/proj/.env
    level: no-model             # 1: No AI
    bindings: [api.stripe.com]  # 2: Listed hosts
  - path: ~/.ssh/id_ed25519
    level: no-model
    readers: [{ app: ssh, from: git }]
  - path: ~/work/board-memo-q4.md
    level: local-model          # 1: Your own AI only
models:
  - { name: ollama, local: true, … }

A rule answers two questions

1Which AI may read it?

no-modelNo AI. Every AI sees it masked, Mandu's own helper included.
local-modelYour own AI only. Only an AI you run and list, such as Ollama, may read it, when it asks. It can be your agent's main AI too.
any-modelAny AI, when it asks. The AI first sees it masked. Mandu's helper reads it for the AI, or the AI reads it into the conversation. The read is logged, and your AI provider gets the text.
publicNot secret. An ordinary file, even if it looks like a secret.

2Where may it be sent?

(default)Needs your OK. Every send of the real value waits on the Approvals page. Allow onceAlways allowBlock
bindingsListed hosts without asking. The hosts you list get the real value, so list only hosts you trust.
valuesPer value. In a file of several secrets, such as a .env, each value has its own hosts.

Your OK never lets an AI past the first question; only changing the rule does. Answer both on the dashboard's Rules page, one menu each, or in ~/.mandu/policy.yaml.

See it for yourself

Mandu with Claude Code

Simulation coming soon

Mandu with Codex

Simulation coming soon

Mandu with OpenCode

Simulation coming soon

Mandu with OpenClaw

Simulation coming soon

Mandu with Goose

Simulation coming soon

mandu demo runs the real Mandu components on your computer against a stand-in AI, offline, with no API key. It plays Claude Code's and Codex's requests today.

Evidence

Kept the secret and got the task done in 17 of 18 sessions.

Six everyday tasks with a secret in them: file a bug report, go through the inbox, summarise a blood test, scrub a key from git history, debug a payment error, add notes to Notion. Claude Code with Claude Sonnet, live, three takes of each: 18 sessions per setting. The other protections keep the secret by blocking the task, or finish the task and let the secret go. The next best did both in 12.

Secret kept and task done

Sessions out of 18 in which both held.

Mandu17/18
Claude Code credential mask12/18
PrivAiTe gateway†8/18
Claude Code deny rules6/18
No protection3/18
fencepost plugin†0/18

Safe, useful, or both

Each setting is one point. Higher kept the secret more often; further right got the task done more often.

All the numbers
SettingSecret keptTask doneBoth
Mandu18/1817/1817/18
Claude Code credential mask12/1816/18*12/18
PrivAiTe gateway†9/1817/18*8/18
Claude Code deny rules16/188/186/18
No protection4/1817/18*3/18
fencepost plugin†1/1817/18*0/18

Claude Code 2.1.295 with Claude Sonnet 5.5, live in a disposable virtual machine, 17 October 2026. Mandu with Protection on (secrets mode). Each other protection was set up for the task's own secret files, as its documentation directs, and ran without Mandu. A secret counts as kept when it reached neither the AI vendor nor a site it was not meant for, checked verbatim and in base64, hex, reversed and rot13; the other settings were graded from a neutral recording of their traffic. Mandu's one miss: the AI asked a question instead of running the report script, and nothing left the machine. Everyday tasks, no attacks. Method and raw numbers are in the repository's design docs.

* An estimate, for the blood-test task only: its stored screen captures were trimmed, so whether the cloud AI gave the summary was read from the recorded traffic. Every one of these sessions sent the report to the cloud, so “both” is not affected.

† These numbers may understate these two tools, which find a secret by its pattern. The test API keys in this run did not have the format real Stripe and Notion keys have, and the scanner behind the fencepost plugin matched none of them. The test keys now have the real format, and both tools are being measured again.

Research

Built on two papers

Prompt Flow Integrity (arXiv:2503.15547) isolates an LLM agent from untrusted content and guards against privilege escalation. DualView (arXiv:2607.03821) follows it with a defense that keeps two views of an agent's data and routes every action to one of them.

Mandu implements DualView at the operating-system level instead of inside one agent framework, and uses its placeholder machinery to protect secrets. It carries forward DualView's placeholder table, policy engine and two-view workspace.

github.com/compsec-snu/dualview

Cite

@misc{dualview2026,
  title  = {DualView: Preventing Indirect Prompt
            Injection in Personal AI Agents},
  author = {Kim, Juhee and Choi, Woohyuk and
            Kang, Taehyun and Kim, Youngmin and
            Lee, Byoungyoung},
  year   = {2026},
  eprint = {2607.03821},
  archivePrefix = {arXiv},
  primaryClass  = {cs.CR}
}

@misc{pfi2025,
  title  = {Prompt Flow Integrity to Prevent
            Privilege Escalation in LLM Agents},
  author = {Kim, Juhee and Choi, Woohyuk and
            Lee, Byoungyoung},
  year   = {2025},
  eprint = {2503.15547},
  archivePrefix = {arXiv},
  primaryClass  = {cs.CR}
}

Mandu Security

The company behind Mandu.

Mandu Security builds Mandu and keeps it open. We started from systems-security research on keeping AI agents away from data they should not hold, and we are turning that research into something you can install next to the agent you already use.

A mandu is a Korean dumpling. The agent handles the wrapper; the filling, your secrets, stays with you.

<MDU:masked>

Open source · free

Mandu

Everything on this page, on your own computers.

Now · beta

Design partners

We are working with a small group of early teams whose agents run next to real credentials. If that is you, write to us.