Docs · Privacy masking
Privacy masking
magpie can mask what your agents send before it leaves this computer. API keys, passwords and tokens, and when you ask, emails, phone numbers and words of your own, go to the vendor as placeholders such as {{API_KEY_es6eu6jk}}. In the reply each placeholder turns back into its value, so your agent and your files keep the real thing and the vendor never sees it. It runs on this computer, in magpie’s gateway, and is off by default.
Turn it on
- Open the pageGo to Settings → Privacy.
- Mask secretsUnder What to mask, set Mask secrets to On: API keys, private keys, tokens and passwords.
- Personal data, if you want itSet Mask personal data to On for emails, phone numbers, ID and bank card numbers. Each further kind of personal data then gets its own switch below it.
- Your own wordsIn Masked words, list names, codenames or hosts to keep from vendors, separated by commas. Masked words in any case makes Nightjar mask NIGHTJAR too. A secret magpie doesn’t know, such as your gateway’s own keys, goes under Masking rules: what it starts with, or a regular expression.
- Try itPaste a text into Try it to see exactly what the vendor would get. What you paste there isn’t kept.
It applies from the next request, with no restart and nothing to change in your agent. The work is done by the built-in plugin Privacy masking (redact, in Settings → Plugins), which is on whenever any masking is asked for.
There is no CLI command for it. Without the window (magpie serve, Docker), open the same page with magpie web, or set it in ~/.config/magpie/settings.json (/config/magpie/settings.json in Docker). The masker reads the file on every request:
{
"redact": true,
"redactPersonal": true,
"redactWords": ["Nightjar", "db.acme-corp.cn"]
}
What is masked
| Switch | What it finds |
|---|---|
| Mask secrets | Private key blocks (-----BEGIN … PRIVATE KEY-----). API keys by their shape: sk-… (OpenAI, Anthropic, OpenRouter), ghp_/github_pat_, glpat-, AIza…, xox…-, sk_live_, AKIA/ASIA, hf_, gsk_, xai-, npm_, pypi-, dop_v1_, SG., and relay keys like oc_sk_…. JWTs. The password in user:password@host. The value of a field named for a secret, as .env files and configs have them: DB_PASSWORD=…, api_key: …, client_secret, token, credential. Your own Masking rules. |
| Mask personal data | Emails, mainland China mobile numbers, Chinese ID numbers and bank card numbers whose checksum adds up. Also on by default: US Social Security numbers, passport numbers (after passport, 护照 or パスポート), IBANs, birthdays (next to birthday, DOB or 生日), MAC addresses and serial numbers (after S/N). Off until you turn them on, since agents send them in nearly every request: the user name in home folders, user and computer names (user@host), public IP addresses and cloud storage buckets. |
| Masked words | Each word you listed, exactly, as {{TERM_…}}. |
| Find with a local model | Under How it’s found. A model on this computer (OpenAI’s Privacy Filter, a 1.6 GB download) finds what no rule can: a name or an address in free text. It needs Mask secrets or Mask personal data on, and finds under the same switches. |
A placeholder is the kind plus eight characters of a keyed hash. The same value gets the same placeholder every time, so the conversation reads the same turn after turn and the vendor’s prompt cache stays valid. The values are held in memory only.
An example
With Mask secrets and Mask personal data on and Nightjar in Masked words, your agent sends:
Deploy Nightjar with OPENAI_API_KEY=sk-proj-8fK2mQx7Lp4Rt9Vw3Yz6Ab1Cd, then mail li.wei@acme-corp.cn or call 13800138000.
Deploy {{TERM_5hmsrhdc}} with OPENAI_API_KEY={{API_KEY_srhjgvja}}, then mail {{EMAIL_eh5um6mb}} or call {{PHONE_uf7iy2qq}}.
When the model answers “I set OPENAI_API_KEY to {{API_KEY_srhjgvja}} in .env”, your agent reads the real key there, in streamed text and tool-call arguments alike, so the file it writes has the real key. A placeholder the model rewrote a little (spaces inside the braces, another case) is still turned back.
Check that it works
- Before: Try it on Settings → Privacy shows the text as the vendor would get it, with each value’s kind.
- Each request: a shield badge with a count appears on the request in the Gateway page’s recent calls and in the Routing log. Point at it for the kinds; click it to open the request’s record.
- Over time: Insights → Masking (also What was masked on the Privacy page) shows the values masked over 7 or 30 days by kind, agent and provider, and each request’s text as the vendor saw it, placeholders marked.
The records are kept in ~/.config/magpie/masking for 30 days: the agent, provider, model, and each placeholder’s kind, length and first characters, never the value.
When a value is found
Under When a value is found, choose per kind:
- Mask (the default): sent as a placeholder.
- Warn: sent as written, and the badge counts it in amber.
- Block: the request is held back with a 400, “magpie held this request back: Settings › Privacy blocks API_KEY, and it has one (sk-…, 33 characters). Nothing was sent.” The message never has the value.
A value the task needs as it is, such as a team address or a test key, goes in Never mask: it is sent as written wherever it is found whole.
What it leaves alone, and limits
- Thinking stays as the vendor wrote it. A thinking block with its signature, Gemini’s thoughts and Responses’ encrypted reasoning are sealed by the vendor and go back unchanged, so the signature still matches. Only values already masked elsewhere in the request are swapped in them.
- Ids and tool definitions go as written: model ids, message and call ids, UUIDs, tool names, MCP server names, and tool schemas (their descriptions are masked), so a vendor or a tool can still check them.
- Images and file data are sent as they are. Text inside a screenshot is not masked.
- It fails closed. If masking is on and can’t run (the Privacy masking plugin turned off by hand, or the local model is on and can’t answer), the request gets a 503 that says nothing was sent. It is never sent unmasked instead.
- Local models can skip it. Send requests unmasked on a provider whose every address is on this computer or your local network (Ollama, LM Studio) sends its requests as written;
magpie provider set ollama unmasked=yesdoes the same. The logs keep the masked body. - The model sees placeholders. It can’t read or reason about a masked value; it can only pass it on. Put a value it must read under Never mask.
- Rules find shapes. A phone number in another country’s format or a name in free text needs Find with a local model, or your own Masked words and Masking rules. Try your own text in Try it first.
See also Privacy for everything magpie sends by itself. The code is internal/redact and internal/masking.