Built by the Nano Collective, a community collective building AI tooling not for profit, but for the community.
This is the first public release of prompt-scrub, a small Node.js utility that runs entirely on your machine. It detects identifying content inside a prompt (emails, paths, secrets, phone numbers, URLs, postal addresses, and a couple of opt-in categories), replaces each finding with a stable placeholder like Email_1 or Path_2, and lets you rehydrate the model's response back to the original values locally after it comes back.
The motivation is simple: most accidental identifier leakage to a cloud LLM lives in the text of the prompt and the text of its response. Stripping it there, deterministically, before the prompt leaves your machine is a useful layer in a privacy posture, and one that does not need a network round-trip, a new account, or a hosted service to work.
What it actually does
The package exposes two functions and a CLI.
scrub() takes either a plain string or an array of { role, content } messages, runs the configured detectors over the text, replaces each finding with a category-namespaced placeholder, and returns the scrubbed content plus a session id:
import { scrub, rehydrate } from '@nanocollective/prompt-scrub';
const prompt = "My key is sk-12345 and my email is [email protected]";<br>const { scrubbedContent, sessionId } = scrub({ content: prompt });<br>// scrubbedContent: "My key is Secret_1 and my email is Email_1"
You send scrubbedContent to whatever LLM provider you already use. When the response comes back, rehydrate() walks the text, looks up each placeholder in the session map, and swaps them back. Unknown placeholders (placeholder text the model hallucinated, or placeholders from a previous session) are passed through unchanged and surfaced as warnings so you can decide whether to trust them:
const response = "Your email Email_1 looks correct and your key Secret_1 is fine.";<br>const { content, warnings } = rehydrate({ content: response, sessionId });<br>// content: "Your email [email protected] looks correct and your key sk-12345 is fine."
The session id is the link between the two steps. It points at a small JSON file under your OS config directory that holds the placeholder-to-original mapping. The file is written atomically, with restrictive permissions, and includes a corrupt-file quarantine path so a half-written map does not silently disable rehydration. The location is overridable via the PROMPT_SCRUB_CONFIG_DIR environment variable.
Detectors
Eight detectors ship in the box.
On by default:
phone
postal address
path (Unix-style and Windows-style)
secret (tuned for common API key and credential shapes, since missing a credential is worse than missing a name)
url
Off by default, opt-in:
name (proper-noun detector, with a stricter allowlist mode)
code-tell (user-enumerated private identifiers, for things like internal project codenames)
URL detection also accepts a trusted-host allowlist with subdomain matching, so internal services you control can pass through without a placeholder if that is what you want. Overlapping detector findings (an email that looks like a URL fragment, say) resolve by a documented priority order, with longer span winning on ties. Determinism is deliberate: the same input and session always produce the same scrubbed output, which keeps provider prompt-cache prefixes byte-stable.
The CLI
A small command-line wrapper around the same logic. The recommended workflow is inspect first, then scrub:
# See exactly what would change without writing anything<br>echo "My email is [email protected]" | prompt-scrub inspect
# Scrub stdin (or a file), prints the session id to stderr<br>echo "My email is [email protected]" | prompt-scrub scrub
# Rehydrate a response using --session-id<br>echo "Contact Email_1 for details." | prompt-scrub rehydrate --session-id
# Inspect a session map<br>prompt-scrub sessions list<br>prompt-scrub sessions show
# See the active detector set, including any rule-pack additions<br>prompt-scrub rules list
inspect is the part we want people to actually use. It does not write a session file and it prints a SHA-256 hash of the scrubbed output so you can verify byte-stable cache prefixes across runs:
Detected entities:<br>[Email] [email protected] → Email_1 (chars 12-29)
No session written.<br>Hash: 41beda4af0b83488fdf6eea9347775450a1c7c887a6ef377212340f36c445132
Extensibility
Two extension points:
Custom detectors can be passed in via the library API (customDetectors in ScrubOptions). Each one returns matches in the same shape the built-ins do, so they slot into the same priority / span logic.
Rule packs are separate npm packages that contribute additional detectors. They are declared in your config or in package.json, merged into the active set, and visible in rules list. This is the path for sharing detectors across projects without forking the package.
What this is, and what it is not
prompt-scrub reduces identity...