Docs›Core›How it works

How it works

Why Pretense mutates secrets rather than redacting them, and how the round trip stays byte-exact.

Mutation, not redaction

Redaction destroys. A redacted secret cannot come back, so the model sees a hole where your code was and the answer degrades with it.

Pretense swaps each secret and PII value for a deterministic synthetic before the request leaves your machine, and renames the code names inside fenced code blocks the same way. What reaches the provider is the synthetic. On the way back the code names are restored byte for byte, so what reaches you reads as your own code. Secrets and PII are never put back into the reply: they stay synthetics, because a reply that echoed one could carry your real key into a link an attacker chose.

text
your code   ──▶  mutate  ──▶  provider sees synthetics
                   │
                   ▼
             reversal map (stays on your machine)
                   │
your result ◀──  reverse  ◀──  provider response

The round trip

Reversal is byte-exact or it is a bug. You do not have to run it yourself: the proxy mutates every request on the way out and restores your code names on the way back, so the round trip happens on each call. Secret and PII synthetics are never put back.

bash
pretense run claude

pretense audit --limit 20

The audit log records what was swapped, request by request, so you can confirm the outbound side rather than take it on trust.

The same engine runs over a single file through mutate and reverse. Both take flags that decide where the file and its map are written, and those are in the CLI reference.

What gets mutated

KindExampleAction
API keys and tokenssk-…, AKIA…, ghp_…Swapped, stays a token in the reply
PIISSN, emailSwapped, stays a token in the reply
Identifiersvariables, functions, classesRenamed in fenced code, restored in the reply
Comments and strings// comments, string literalsPreserved verbatim

Through the proxy, Pretense swaps secret and PII values and also renames the function, class and variable names inside fenced code blocks, so the provider sees neither your credentials nor your real symbol names. Each name gets the same synthetic everywhere in the request, which keeps the structure of your code intact for the model. Text outside a fence keeps its names as written; only its secret and PII values are swapped.

Two paths keep names as written by design. A local runner you declare under air-gap sees real names, because the default air-gap policy, secrets-pii-only, skips the rename for that runner alone. The file-level mutate command swaps secret values only. What init records is a code graph of those names, not a rename of them.

Where the map lives

The way back is a reversal map, encrypted with AES-256-GCM and written owner-only into ~/.pretense/maps/. It is not part of the request, so it is not something the provider receives.

Persisted bytes, persisted map

Mutating a file in place writes that file's map and prints the exact command that restores it, so the way back exists before you need it. A mutation you cannot reverse is a bug, not a mode.