Sanitization Gates: Shipping Skills Safely
Outward-facing skills must not leak — a generation-time gate blocks credentials, PII, infra, and internal names before commit, with CI as the backstop.
The Skill You Ship Is Not the Skill You Wrote
A skill authored for your own machine is allowed to know everything about your machine — your paths, your hosts, your internal project names. The moment that skill is shared, published in a course, or committed to a public repo, every one of those details becomes a leak. The skill that helped you privately becomes a map of your infrastructure for anyone who reads it.
Sanitization is the discipline that lets a skill cross that boundary safely. It is not a one-time cleanup you do before publishing — it is a gate that runs at generation time, every time, and refuses to let a leaking skill be committed at all. This lesson is itself a worked example: it shows you what leaks look like, and the examples are deliberately fake so the gate scanning this very file stays green.
The Gate
The gate sits between generated content and the commit. It runs a set of leak-class scanners over the text, and it has exactly two outcomes:
- Match → BLOCK. Exit non-zero. Print the file and line of every hit so the author can fix it. The commit does not proceed.
- Clean → PASS. Exit zero. The commit proceeds — and the identical gate runs again in CI as the backstop.
The exit code is the whole interface, the same way a PreToolUse hook works: zero means proceed, non-zero means stop. A gate that warns but does not block is not a gate; it is a suggestion, and suggestions lose to deadlines.
The Leak Classes
You cannot scan for what you have not named. A sanitization gate is only as good as its catalog of leak classes, and there are four that cover almost everything that escapes.
Walk them in severity order:
- Credentials (critical). API keys, tokens, secrets,
.envvalues. A pattern likeAKIAEXAMPLE0000000000or ansk--prefixed string. These are the highest-severity leak because they are immediately exploitable. The fix is an env reference:${AWS_ACCESS_KEY}, never the literal. - PII (critical). Personal emails, phone numbers, home directory paths that embed a username. Fix with a placeholder:
<your-email>,~/.claude/skills/<name>. - Infrastructure (high). Internal IP addresses like
10.0.0.1, machine-specific paths, hostnames. These map your network. Fix with a functional label —<runtime-host>instead of an address, "the persistent agent runtime" instead of a specific machine. - Internal codenames (medium). Project and agent codenames that mean nothing to an outsider and everything to an attacker building a picture of your systems. Fix by genericizing to the function: "the semantic memory layer," "the memory store," "the persistent agent runtime."
Every example in the matrix above is intentionally fake — that is the only way to teach the pattern without becoming the thing you are warning against.
Keep the Right Names
Sanitization is not blanket redaction. Some names are meant to ship — they are part of the public story, and stripping them makes the content worse and the writer paranoid. The line is simple:
- Keep the names that are already public and product-facing: Claude Code, the operator's own name, the public products and brands the work is about.
- Genericize the internal-only names: codenames for internal systems, infrastructure identifiers, anything that exists only inside your walls.
The test for any name: would this string help an outsider build a map of systems they should not have? If yes, genericize. If it is already on a public landing page, keep it. Over-sanitizing — black-barring everything — produces unreadable content and trains the author to ignore the gate. Precision is the goal.
Placeholder Discipline: Replace, Don't Redact
When you fix a leak, replace it with something obviously fake and still readable — never a black bar.
A redacted line (API_KEY=████████) tells the reader nothing and leaves them guessing whether the example even works. A placeholder (API_KEY=${MY_SERVICE_KEY} or host = 192.0.2.1 — an address from the block reserved for documentation, which can never name a real machine) keeps the example teachable: the reader sees the shape of what goes there and the fact that it must come from elsewhere. Note the trap: a private address like 10.0.0.1 is NOT a safe placeholder — it is precisely the pattern your own infra scanner flags, so using it as a stand-in trips the gate on the file that contains it. And because a true placeholder is obviously fake, it survives the gate scanning your own file — which is exactly why this lesson can show you AKIAEXAMPLE0000000000 without tripping its own scanner.
The rule: replace, don't redact, and make the replacement unmistakably not-real.
CI Is the Backstop, Not the Gate
The generation-time gate is your first wall and it catches most leaks fast and cheap. But it depends on the author — and authors skip steps, run stale configs, or disable a hook in a hurry. So the identical scan runs again in CI on every push.
This is defense in depth. The author's diligence is the first line, the pipeline is the line that never forgets. A green local run is not the last word; the merge-blocking CI scan is. Wire the same script into both so they cannot drift apart — one script, two trigger points.
Now build the gate itself: a leak-scanner that exits non-zero on a match, reports every hit, and uses obviously-fake examples so the script that scans for leaks does not itself contain one.
What's Next
You can now author a skill that remembers, evolves, and ships safely. The final lesson zooms out from the single skill to the whole library: why a curated collection of phenomenal skills is an authority moat, how curation — pruning, naming, dedup-by-trigger, escalation — keeps it phenomenal, and how each skill you ship makes the next one cheaper to write. That is the capstone, and it is where all four disciplines compound.