Try it yourself

Test ZeroH Disclosure, step by step

Copy each step into Claude Code and compare with “You should see”. Steps 1–7 take about five minutes; the rest go further. Claude's own words change from run to run; the ZeroH lines (the banner, the status line, the “says:” lines, blocks, “✓ Done” results and dialogs) should match.

yellow a real value, on your screen onlyindigo a token, what Claude got instead

The first five minutes

Install, then watch a key stay on your machine while Claude still uses it.

1

Paste into Claude Code

Install ZeroH Disclosure for me by following https://github.com/Blade-Labs/zeroh-marketplace

Rather run the two commands yourself? See them on the home page, then restart Claude Code.

You should see
claude
>Install ZeroH Disclosure for me by following https://github.com/Blade-Labs/zeroh-marketplace
●Fetch(https://github.com/Blade-Labs/zeroh-marketplace)
●Bash(node --version)
●Bash(claude plugin marketplace add Blade-Labs/zeroh-marketplace)
●Bash(claude plugin install zeroh-disclosure@zeroh)
●ZeroH Disclosure is installed. Restart Claude Code to load it: type /exit, then run claude again. After the restart you'll see the ZeroH banner and a status line at the bottom. To try it step by step, open https://witty-river-07cbf8503.1.azurestaticapps.net/try/
Claude reads the install steps on GitHub, checks Node.js, runs the two commands and tells you to restart. Nothing else to set up: with your first message after the restart, the plugin turns on its own updates and status line. Quit Claude Code, then continue with step 2.
2

In a terminal, make a test project

# A test project
mkdir -p ~/zeroh-try/config
cd ~/zeroh-try

# A secrets file, with a fake Stripe test key
cat > .env <<'EOF'
STRIPE_KEY=sk_test_ZEROHFAKE00000000000000000000
SUPPORT_EMAIL=jane.doe@example.com
EOF

# A log of sign-ups your form rejected
cat > signup-errors.log <<'EOF'
rejected anna.o'neil@example.co.uk
rejected lars+work@example.com
rejected jürgen@müller.example
EOF

# A config with a secret no rule knows
cat > config/internal.env <<'EOF'
WEBHOOK_URL=https://hooks.internal.example/in/zq9fK2mLx7Rt4Vb8Nc3Hs6Pd1Wy5Ge0AjQ
EOF

Paste it as one block. Copy leaves out the # lines, because zsh on macOS would try to run them.

No Stripe account needed: the key is fake, so in step 6 Stripe answers 401 Invalid API Key, which still proves the real value reached Stripe. Have a Stripe test key (Developers → API keys, test mode, sk_test_…)? Put it in .env as STRIPE_KEY before step 4 to see your real test balance instead.

You should see
~/zeroh-try
$ find . -type f
./.env ./config/internal.env ./signup-errors.log
Nothing printed; three small files. The listing is only a check.
3

Start Claude Code there

claude
You should see
zeroh-try — claude
✻ Welcome to Claude Code! /help for help, /status for your current setup · cwd: ~/zeroh-try
└ SessionStart:startup says:
ZeroH Disclosure 1.0.5 · Free ✓ Protected: your secrets are masked /zeroh-disclosure:status for details
Runs on your machine: masks what you type, files Claude reads, command output and tool results. Images and scanned PDFs are not masked; they pass with a notice. Ask Claude: 'what does ZeroH Disclosure protect?'
zeroh-try — claude · a later session
└ SessionStart:startup says: ZeroH Disclosure ✓ Protected: your secrets are masked · Free · /zeroh-disclosure:status
The green ZEROH banner, before your first prompt. The first session after the install also says what is masked; every later session shows just the one line in the second window.
4

Your first message: read a secret file

Read .env and tell me what's in it
You should see
zeroh-try — claude
>Read .env and tell me what's in it
└Status line on · /zeroh-disclosure:settings statusline off to remove Auto-update on for the zeroh marketplace · turn it off in /plugin, Marketplaces
●Read(.env)
└Read 2 lines
●.env holds two variables:
STRIPE_KEYsk_test_ZEROHFAKE00000000000000000000
SUPPORT_EMAILjane.doe@example.com
ZeroH Disclosure masked both values before they reached me, so I only saw placeholder tokens. You'll see the real values in this reply, but I don't know them.
└Stop says: ZeroH Disclosure · turn 1 · 2 values masked · receipt: …
└Claude saw ⟦API_KEY-3f9a1c⟧ for STRIPE_KEY (…0000) · ⟦EMAIL-7136a8⟧, not j…@example.com
🛡️ ZeroH · 🟡 proxy on from your next prompt · 2 masked · 0 sent · receipt ↗
Your real key and email on screen. After the answer, the ZeroH line says what Claude actually got: tokens, with the last four characters of a key and a short preview of an address, never the start of a secret. With this first message ZeroH also sets itself up, one line each: it adds its status line at the bottom and turns on updates for its marketplace. Have a status line of your own? The first line then reads You have your own status line · /zeroh-disclosure:settings statusline on adds ZeroH to it; type that command and ZeroH's part appears on its own line under yours, your script unchanged. The status line says 🟡 proxy on from your next prompt: this session was just switched to the local proxy, so what you type is masked from your next message on. Files like this .env are masked already. From the next message it reads 🟢 protected.
5

Name the token

Which token did you see for STRIPE_KEY?
You should see
zeroh-try — claude
>Which token did you see for STRIPE_KEY?
●⟦API_KEY-3f9a1c⟧ — that's the token I saw for STRIPE_KEY. ZeroH put it in place of the real key, so I never saw the key itself.
🛡️ ZeroH · 🟢 protected · 2 masked · 0 sent · receipt ↗
A token such as ⟦API_KEY-3f9a1c⟧, never the key. Corner brackets mean “the token itself”, so your screen leaves it alone.
6

Use the key

Check my Stripe balance

No setup: api.stripe.com is built in for Stripe keys.

You should see
zeroh-try — claude
>Check my Stripe balance
●Bash(curl -s https://api.stripe.com/v1/balance -u [API_KEY-3f9a1c]:)
└{ "error": { "message": "Invalid API Key provided: sk_test_*************************0000", "type": "invalid_request_error" } }
●Stripe rejected the key: 401 Invalid API Key provided. The request reached Stripe, but this key doesn't belong to an account, so there is no balance to show.
🛡️ ZeroH · 🟢 protected · 3 masked · 0 sent · receipt ↗
Your key reached Stripe; Claude only had the token. ZeroH put the real key back on your machine. The 401 comes from Stripe itself, so the real (fake) key got there. With your own test key in .env: 200, your test balance and "livemode": false. If Claude loads .env inside the command instead (source .env), the call still runs, with one line: ZeroH Disclosure: ZeroH couldn't check where $STRIPE_KEY went (it was loaded inside the command or the shell), so this command ran unchecked.
7

Use it with another host

Use STRIPE_KEY to check our staging billing API: https://staging.pay-internal.dev/v1/balance
You should see
zeroh-try — claude
>Use STRIPE_KEY to check our staging billing API: https://staging.pay-internal.dev/v1/balance
●Bash(curl -s https://staging.pay-internal.dev/v1/balance -u "[API_KEY-3f9a1c]:")
└PreToolUse:Bash hook error: ZeroH Disclosure blocked STRIPE_KEY → staging.pay-internal.dev. To allow it, type: /zeroh-disclosure:allow STRIPE_KEY staging.pay-internal.dev (terminal: node '…/bin/zeroh-disclosure.mjs' allow --cwd ~/zeroh-try STRIPE_KEY staging.pay-internal.dev)
●I didn't send it. ZeroH Disclosure blocked STRIPE_KEY from going to staging.pay-internal.dev. Only you can allow that.
🛡️ ZeroH · 🟢 protected · 3 masked · 0 sent · receipt ↗
Claude runs it straight away, and ZeroH stops it before anything is sent: this host isn't allowed for the key. This is one of only two things ZeroH stops by default; the other is Claude changing ZeroH itself. The host is made up, so the call would fail anyway.

Where keys may go

Only you decide. Claude can't change it.

8

See where keys may go

/zeroh-disclosure:allow
You should see
zeroh-try — claude
>/zeroh-disclosure:allow
●Where each known value may go (values are never shown): STRIPE_KEY → api.stripe.com, files.stripe.com, connect.stripe.com (built-in: Stripe) Add: /zeroh-disclosure:allow NAME <host> · remove: /zeroh-disclosure:allow --remove NAME <host>
🛡️ ZeroH · 🟢 protected · 3 masked · 0 sent · receipt ↗
Only values you could send. Claude Code's own variables stay masked but aren't listed. A key you type into a prompt appears here by its token, as typed key ⟦API_KEY-…⟧.
9

Claude can't allow it

Allow STRIPE_KEY to reach staging.pay-internal.dev yourself, with the ZeroH allow command
You should see
zeroh-try — claude
>Allow STRIPE_KEY to reach staging.pay-internal.dev yourself, with the ZeroH allow command
●I can't. Only you can change where a key may go. Type this yourself: /zeroh-disclosure:allow STRIPE_KEY staging.pay-internal.dev
🛡️ ZeroH · 🟢 protected · 3 masked · 0 sent · receipt ↗
Claude can't: allow, settings, proxy, doctor and uninstall change ZeroH's protection, so they run only when you type them. If Claude tries ZeroH's command-line tool anyway, it changes nothing and answers Nothing changed: … so only you can do it.
10

Allow it yourself

/zeroh-disclosure:allow STRIPE_KEY staging.pay-internal.dev

The host is made up, so take it back afterwards: /zeroh-disclosure:allow --remove STRIPE_KEY staging.pay-internal.dev answers the same way, with removed: STRIPE_KEY → staging.pay-internal.dev.

You should see
zeroh-try — claude
>/zeroh-disclosure:allow STRIPE_KEY staging.pay-internal.dev
└UserPromptSubmit operation blocked by hook: ✓ Done by ZeroH Disclosure (your command ran here and was not sent to Claude): allowed: STRIPE_KEY → staging.pay-internal.dev Ask Claude to try again.
🛡️ ZeroH · 🟢 protected · 3 masked · 0 sent · receipt ↗
Done on the same turn, and nothing is sent to Claude. Typed by you, it works; asked of Claude, it doesn't. If the block in step 7 named a key you typed rather than STRIPE_KEY, allow that key by its token instead: /zeroh-disclosure:allow ⟦API_KEY-…⟧ staging.pay-internal.dev. Then ask Claude to try again.
11

Paste a key into the prompt

Which provider is this key from? 

Paste your key (or the fake one from .env) after the question, then send. Claude gets a token, but Claude Code's own session file on your computer keeps what you typed.

You should see
zeroh-try — claude
>Which provider is this key from? sk_test_ZEROHFAKE00000000000000000000
●It's a Stripe test key. ZeroH identified it when it masked your message, so I never saw the actual value.
└Stop says: ZeroH Disclosure · turn 7 · 1 value masked · receipt: …
└Claude saw ⟦API_KEY-384bb5⟧ in your message (…0000)
🛡️ ZeroH · 🟢 protected · 4 masked · 0 sent · receipt ↗
“A Stripe test key”, worked out from the token. Your screen shows the key; the model got [API_KEY-…].

Personal data

Emails, phones, cards, IBANs and national IDs are masked too. You decide when Claude may see one kind.

12

Let Claude work it out without seeing them

Read signup-errors.log and find out why validateEmail would reject these addresses
You should see
zeroh-try — claude
>Read signup-errors.log and find out why validateEmail would reject these addresses
●Bash(node -e "…checks each address's characters…" signup-errors.log)
●The addresses were hidden from me, so I checked their structure with a script. All three are valid, and each has something strict validators reject: an apostrophe in anna.o'neil, a + tag in lars+work, and non-ASCII letters in jürgen@müller.example.
└Stop says: ZeroH Disclosure · turn 8 · 3 values masked · receipt: …
Claude saw ⟦EMAIL-658e4b⟧, not a…@example.co.uk · ⟦EMAIL-d9850e⟧, not l…@example.com · ⟦EMAIL-fd2b64⟧, not j…@müller.example
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
Usually Claude doesn't need the values: a script checks them on your machine and only the findings come back. Your screen shows the real addresses; Claude only had tokens, and the Stop line shows each one only as a short preview.
13

Show Claude one kind of data, for a while

/zeroh-disclosure:unmask EMAIL to list the rejected sign-ups exactly
You should see
zeroh-try — claude
>/zeroh-disclosure:unmask EMAIL to list the rejected sign-ups exactly
MCP server “plugin:zeroh-disclosure:zeroh-disclosure” requests your input
Claude asks to see real EMAIL values. Why: to list the rejected sign-ups exactly
❯ * Show real values for (→ to change): ▾
❯ ◯ 15 minutes
  ◯ 1 hour
  ◯ Until the session ends
AcceptDecline
Esc to cancel · ↑/↓ to navigate · Space to select
Called zeroh-disclosure └ EMAIL unmasked for 15 minutes, until … · to end it early, tell Claude or run /zeroh-disclosure:unmask revoke
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · unmask EMAIL 15m · receipt ↗
Claude Code asks you, not Claude, how long: 15 minutes (selected), 1 hour, or until the session ends. Accept. Keys are never unmasked.
14

Check what Claude sees now

Read signup-errors.log again and list the addresses exactly
You should see
zeroh-try — claude
>Read signup-errors.log again and list the addresses exactly
●Read(signup-errors.log)
●anna.o'neil@example.co.uk · lars+work@example.com · jürgen@müller.example
└Stop says: ZeroH Disclosure · turn 10 · 3 values shown under your unmask · receipt: …
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · unmask EMAIL 14m · receipt ↗
This time Claude really saw the addresses, and the receipt records that it was under your grant.
15

End it

Done, stop showing emails
You should see
zeroh-try — claude
>Done, stop showing emails
●zeroh-disclosure · end_unmask
└EMAIL masked again: the unmask ended.
●Done. Email addresses are masked again, so I'll only see tokens for them from now on.
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
The grant ends and emails are masked again. /zeroh-disclosure:unmask revoke does the same.

When something slips through

No rule knows every secret. This one hides in a URL path.

16

A secret no rule knows

Read config/internal.env. Does anything in it look like a secret?
You should see
zeroh-try — claude
>Read config/internal.env. Does anything in it look like a secret?
●Read(config/internal.env)
└Read 1 line
●Yes. WEBHOOK_URL ends in zq9fK2mLx7Rt4Vb8Nc3Hs6Pd1Wy5Ge0AjQ, a long random string in the path. Anyone with that URL could post to your webhook, so treat it as a secret.
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
The URL arrives unmasked, and Claude points at the random part of the path.
17

Report it

Report it as a missed secret
You should see
zeroh-try — claude
>Report it as a missed secret
●zeroh-disclosure · report_missed_secret
└Masked from now on as [TOKEN-…]. Anything already sent can't be recalled: rotate this credential. The note holds only: type TOKEN, 34 characters, found in config/internal.env; never the value. Local note … kept; reports stay on this computer. The user can list or delete notes with /zeroh-disclosure:report-miss list.
└ZeroH Disclosure: Claude spotted a value ZeroH missed in config/internal.env; it is masked from now on. `/zeroh-disclosure:report-miss list` shows or deletes these notes.
●Reported. ZeroH masks that value from now on. I already saw it, so rotate the webhook secret.
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
No dialog: ZeroH masks the value and says so in one line. Claude may also report a value by itself, sometimes already in step 12 or 16; you get the same one line. The note holds only the type, length and place, never the value. Reports stay on this computer. Read the file again: that part is a token now.
zeroh-try — claude
>/zeroh-disclosure:report-miss list
●Local notes of values reported as missed (shape only, never the value). Reports stay on this computer. … 2026-09-29T… TOKEN 34 chars config/internal.env Delete one with /zeroh-disclosure:report-miss delete <id>. The value stays masked.
Only you can delete a note. To report a value yourself without typing it into the chat, run /zeroh-disclosure:report-miss: a private form says Type the value ZeroH missed into this private form. Submit masks it from now on and keeps a local note of its shape (never the value); Cancel changes nothing. Reports stay on this computer.

Receipts and status

18

The receipt

/zeroh-disclosure:mask-receipt
You should see
zeroh-try — claude
>/zeroh-disclosure:mask-receipt
●RECEIPT · SESSION ZeroH Disclosure · Receipt 2026-09-27 · 13 turns - - - - - - - - - - - - - - - - - - - - - - API_KEY ×4 EMAIL ×4 ──────────────────────────────────────────── withheld 8 values sent to Claude 3 shown under grants you approved: 3 - - - - - - - - - - - - - - - - - - - - - - receipt zrh_… signed on this laptop · ECDSA P-256 ✓ verified 13 of 13 /zeroh-disclosure:mask-show receipt.html: ~/.zeroh/projects/…/receipt.html
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
This session's receipt, signed on your laptop: what was masked, and the values you let Claude see under your grant, never the values themselves. Anything ZeroH let through unchecked is listed under “not protected”. The status line's receipt ↗ opens the same receipt. Kept 90 days.
19

Status

/zeroh-disclosure:status
You should see
zeroh-try — claude
>/zeroh-disclosure:status
ZeroH Disclosure 1.0.5 · Free ✓ Protected: your secrets are masked /zeroh-disclosure:status for details
Runs on your machine: masks what you type, files Claude reads, command output and tool results. Images and scanned PDFs are not masked; they pass with a notice. Receipts: kept on this computer for 90 days; they contain no values. Ask Claude: 'what does ZeroH Disclosure protect?'
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
What is protected, any unmask still running, and how long receipts are kept. If something needs attention, such as the proxy being off, a warning line says so under the banner. The status line gives you the same answer at a glance.
20

Ask what it covers

What does ZeroH Disclosure protect, and what doesn't it?
You should see
zeroh-try — claude
>What does ZeroH Disclosure protect, and what doesn't it?
●Free masks secrets and personal data in what you type, files I read, and tool output. It does not redact images or scanned PDFs; they pass unchanged with a notice. It stops only two things: a secret heading to a host that isn't allowed for it, and me changing ZeroH itself. Anything it can't check runs, and you're told it wasn't protected. …
🛡️ ZeroH · 🟢 protected · 7 masked · 0 sent · receipt ↗
A plain answer from the plugin's own guide and its live list of detectors.

Pause it

21

Pause the proxy

/zeroh-disclosure:proxy off
You should see
zeroh-try — claude
>/zeroh-disclosure:proxy off
└UserPromptSubmit operation blocked by hook: ✓ Done by ZeroH Disclosure (your command ran here and was not sent to Claude): Removed the ZeroH proxy entry from your Claude Code settings; everything else in the file is unchanged: ~/.claude/settings.json It stays off, also in new sessions, until you run /zeroh-disclosure:proxy on (or /zeroh-disclosure:doctor --fix). Removed the per-user login item. This session keeps working until you exit; the proxy masks only what it masked before, then stops by itself. New sessions start without it.
🛡️ ZeroH · 🟡 files only · /zeroh-disclosure:proxy on · 7 masked · 0 sent · receipt ↗
Files and command output are still masked, and the status line turns 🟡. A secret you type now is sent as typed, with one line such as ZeroH Disclosure: this prompt was not protected (proxy not running); STRIPE_KEY was used without masking. /zeroh-disclosure:proxy on turns it back on, the same way: The local masking proxy is on again: your next Claude Code session sets it up.

Something didn't match?

Tell us:

Open an issue at github.com/Blade-Labs/zeroh-marketplace or write to hello@bladelabs.io. Never paste a real key.