Cloak
Register a secret once. Cloak intercepts every tool call and substitutes the real value at execution time, so your API keys and tokens never appear in the model context, conversation transcript, or tool output.
What It Does
When an agent runs curl -H "Authorization: Bearer $STRIPE_KEY", the real key lands in the tool call payload, which gets serialized into the conversation transcript and sent to the model API. Cloak intercepts at the tool boundary and swaps in a placeholder, so the model sees @@SECRET:stripe_key@@ at every step.
Seven hooks cover the different exposure surfaces (the Stop hook is opt-in):
Catches a raw secret the model puts into a command, file write, or MCP argument. The value is vaulted under an auto_xxxx name and the call is denied with the placeholder to use instead.
Denies a built-in Read of a file that holds a registered secret, since Read output has no post-hoc scrub.
Denies reading a dotenv file while it still holds values missing from the vault. The deny reason is the fix: prismor cloak add --env-file .env.
Substitutes @@SECRET:name@@ placeholders with real values before a shell command runs. Once any secret is registered, every Bash command's output is passed through scrub-stream.sh, which masks registered values (not only placeholder-resolved ones) before they reach the transcript.
Scrubs real secret values from MCP tool responses before they are returned to the model, replacing any leaked values with their placeholders.
Scans every prompt you submit for recognizable secret patterns. Detected values are auto-registered and the prompt is soft-blocked, showing you a sanitized version to resubmit.
Runs a dry-run prismor sweep after each session ends to catch any secrets that slipped through despite cloaking.
Cloak is the prevention layer. Sweep is the cleanup layer. Use both: Cloak to stop secrets entering the transcript in real time, Sweep to periodically audit for residue that may have accumulated before Cloak was installed.
How It Works
Secrets are plain files under ~/.prismor/secrets/, one per registered name. The directory is mode 0700, each file is 0600. You only. Never in git, never touched by cloud sync tools.
# 1. You write a command using a placeholder
$ prismor cloak add stripe_key # registers @@SECRET:stripe_key@@
# 2. Agent generates a Bash tool call referencing the placeholder
curl -H "Authorization: Bearer @@SECRET:stripe_key@@" https://api.stripe.com/v1/charges
# 3. decloak.sh (PreToolUse hook) substitutes before execution
# The real key is injected only into the shell subprocess - not the transcript
curl -H "Authorization: Bearer sk_live_..." https://api.stripe.com/v1/charges
# 4. Command output is piped through scrub-stream.sh before the agent sees it
# Any leaked real value in the output is replaced back with the placeholder
Authorization: Bearer @@SECRET:stripe_key@@The hook scripts are pure bash + jq with no Python startup cost. The real value is never serialized into the Claude Code conversation JSON at any point.
Fail-closed by design.
If a @@SECRET:name@@ placeholder references a name that has no matching file in the secrets store, the Bash hook denies the tool call entirely rather than running with a missing substitution.
Setup
Requirements
- bash — hook scripts run as bash (ships with macOS and Linux)
- jq — JSON parsing for hook payloads
- Python 3.8+ — Prismor CLI runtime
- Claude Code or Hermes — the agents with automatic decloak and output scrub (
--agent claude|hermes|all). Codex hooks are block-only, so run commands throughprismor cloak run -- <command>instead
$ brew install jqInstall hooks
Run from your project root. This wires the hooks into the current project's Claude Code settings:
$ prismor cloak installInstalled Claude Code cloaking hooks at /your/project/.claude/settings.json
+ PreToolUse:Bash|Write|Edit|MultiEdit|mcp__.* (secret-guard)
+ PreToolUse:Read (read-guard)
+ PreToolUse:Read|Bash (env-guard)
+ PreToolUse:Bash (decloak + output scrub)
+ PostToolUse:mcp__.* (recloak)
+ UserPromptSubmit (guard)
Secrets directory: /Users/you/.prismor/secrets
Next step: register your first secret with
prismor cloak add <name> (reads the value from stdin)To protect all projects at once, install into your global settings instead:
$ prismor cloak install --scope userInstall flags
| Flag | Description |
|---|---|
| --scope project | Install hooks into the current project's .claude/settings.json (default) |
| --scope user | Install hooks into your global ~/.claude/settings.json - covers all projects (--scope global is equivalent) |
| --agent claude|hermes|all | Which agent to install cloaking for (default: claude). Hermes gets a prismor-cloak plugin under .hermes/plugins/ (project scope) or ~/.hermes/plugins/ (user scope) |
| --no-userprompt-guard | Skip the UserPromptSubmit soft-block hook (opt out of prompt scanning) |
| --no-secret-guard | Skip the PreToolUse hook that detects, vaults, and blocks raw secrets in tool calls |
| --no-read-guard | Skip the PreToolUse hook that denies reading files containing a registered secret |
| --no-env-guard | Skip the PreToolUse hook that denies reading .env-style files whose entries are not yet imported into the vault |
| --sweep-on-stop | Also wire a Stop hook that runs a dry-run sweep after each session ends |
Managing Secrets
Add a secret
The value is read from a hidden prompt or piped from stdin. It never appears in your shell history.
$ prismor cloak add stripe_key
Enter value for @@SECRET:stripe_key@@ (input hidden): ••••••••••••••••••••••••••
Registered @@SECRET:stripe_key@@ (26 bytes) at ~/.prismor/secrets/stripe_keyTo register from a file (useful for long keys or certificates):
$ prismor cloak add my_cert --from-file ./certs/server.pemTo import an existing dotenv file, registering every KEY=VALUE entry as @@SECRET:KEY@@:
$ prismor cloak add --env-file .envOnce registered, reference the secret anywhere in a command or prompt using its placeholder:
List registered secrets
Shows names, sizes, modification times, and whether a secret was auto-registered by the prompt guard. Values are never displayed.
$ prismor cloak list
Registered secrets at /Users/you/.prismor/secrets:
@@SECRET:stripe_key@@ (32 bytes, updated 2026-04-09 11:23)
@@SECRET:openai_key@@ (51 bytes, updated 2026-04-09 09:41)
[auto] @@SECRET:auto_3f2a1b4c@@ (40 bytes, updated 2026-04-09 10:05)Secrets tagged [auto] were created automatically by the UserPromptSubmit guard when it detected a raw secret value in a prompt you submitted.
Remove a secret
$ prismor cloak remove stripe_key
Removed @@SECRET:stripe_key@@Check status
$ prismor cloak status
CLOAKING
──────────────────────────────────────────────────
Claude Code: installed (project)
Config: /your/project/.claude/settings.json
Events: PreToolUse:Bash|Write|Edit|MultiEdit|mcp__.*, PreToolUse:Read, PreToolUse:Read|Bash, PreToolUse:Bash, PostToolUse:mcp__.*, UserPromptSubmit
Hermes Agent: not installed
Codex: block-only; automatic decloak/output scrub unavailable
Codex runner: prismor cloak run -- <command>
Secrets dir: /Users/you/.prismor/secrets
Registered: 3 placeholder(s)Prompt Guard
Every message you send gets scanned before it hits the model. A recognized secret pattern blocks the submission, registers the value under a @@SECRET:auto_xxxxxx@@ placeholder, and stashes the sanitized prompt. Send any follow-up message and Prismor loads it automatically — nothing to copy back.
# You type:
Fix the Stripe integration - the key is sk_live_AbCdEfGhIjKlMnOp1234
# Prompt guard fires before the message is sent:
Prismor cloaking: detected secret(s) in your prompt.
Stored under /Users/you/.prismor/secrets as:
• @@SECRET:auto_3f2a1b4c@@
Your original prompt was NOT sent to the model. The sanitized version
below has been saved — just send any follow-up message (e.g. 'go') and
Prismor will load it automatically. Or paste it yourself:
---
Fix the Stripe integration - the key is @@SECRET:auto_3f2a1b4c@@
---Detected secret patterns
The guard uses conservative, known-prefix patterns to minimize false positives:
| Provider | Patterns |
|---|---|
| Stripe | sk_live_*, sk_test_*, rk_live_* |
| GitHub | github_pat_*, ghp_*, gho_*, ghs_*, ghu_* |
| AWS | AKIA* (access key), ASIA* (temp access key) |
| AIza* (API key) | |
| Slack | xox[bpoar]-* |
| GitLab | glpat-* |
| JWT | eyJ*.eyJ*.* |
Add your org's token formats with prismor cloak pattern add 'mycorp_[0-9a-f]{32}'. Custom patterns are validated on add and apply to both the prompt guard and the tool-call guard. On an enrolled device, patterns an admin pushes in the signed org policy (settings.cloak_patterns) are loaded too, and are read-only on the machine.
Bypassing the guard
If you are intentionally discussing a secret in prose (e.g. explaining a rotation procedure), prefix your message with !!allow to skip detection for that single submission:
The bypass applies only to that one message. The next submission is scanned normally.
Commands
| Command | Description |
|---|---|
| prismor cloak add <name> | Register a new secret interactively (value hidden at prompt) |
| prismor cloak add <name> --from-file <path> | Register a secret by reading its value from a file |
| prismor cloak add --env-file <path> | Import every KEY=VALUE entry from a dotenv file as @@SECRET:KEY@@ (only names and byte counts are printed) |
| prismor cloak list | List registered secret names, sizes, and timestamps (values never shown) |
| prismor cloak remove <name> | Delete a registered secret from the store |
| prismor cloak status | Show hook installation state (Claude Code and Hermes) and registered secret count |
| prismor cloak run -- <command> | Resolve @@SECRET:name@@ placeholders locally, run the command, and scrub its output. The cloaking path for Codex, whose hooks are block-only |
| prismor cloak pattern <list|add|remove> | Manage secret-detection regexes (POSIX ERE) on top of the built-ins; they apply to both the prompt guard and the tool-call guard |
| prismor cloak uninstall | Remove cloaking hooks from settings.json (--agent hermes|all for the Hermes plugin) |
Environment variables
| Variable | Description |
|---|---|
| PRISMOR_SECRETS_DIR | Override the secrets store location (default: $PRISMOR_HOME/secrets, i.e. ~/.prismor/secrets) |
| PRISMOR_PROMPT_STASH_TTL | Seconds a sanitized prompt stashed by the prompt guard stays loadable on your next message (default: 1800) |
Examples
Example 1: Setting up a new project
You're starting a new project that uses Stripe and an OpenAI key. Install Cloak and register your secrets before your first agent session.
# Install hooks into this project
$ prismor cloak install
# Register your secrets (values hidden at prompt)
$ prismor cloak add stripe_key
$ prismor cloak add openai_key
# From now on, use placeholders in your prompts to Claude:
# "Run the Stripe charge using @@SECRET:stripe_key@@"
# Claude will use the real value at execution time - you never see it in the transcript.Example 2: Protecting an existing workflow
You've been using Claude Code with real secrets in your prompts. Install Cloak, register your secrets, and the prompt guard will catch any future paste accidents automatically.
# Install globally so all projects are covered
$ prismor cloak install --scope user
# Register an existing token from a file
$ prismor cloak add github_token --from-file ~/.secrets/github_token
Registered @@SECRET:github_token@@ (40 bytes) at /Users/you/.prismor/secrets/github_token
# Verify
$ prismor cloak list
Registered secrets at /Users/you/.prismor/secrets:
@@SECRET:github_token@@ (40 bytes, updated 2026-04-09 11:30)
# The prompt guard now auto-catches if you ever paste the raw token by mistake.The Secrets Store
Registered secrets live in a flat directory, one file per name:
- Directory permissions: 0700 (owner read/write/execute only)
- File permissions: 0600 (owner read/write only)
- Each file is named after the placeholder (e.g.
stripe_key) - Values are stored in plaintext — the filesystem permissions are the protection layer
- Auto-registered secrets use a deterministic SHA-256 prefix name (
auto_3f2a1b4c), so the same value always maps to the same placeholder across sessions
Do not sync your secrets directory.
If ~/.prismor/secrets/ is inside a directory that is backed up by iCloud, Dropbox, or similar, exclude it explicitly. Cloak is designed for local-only use.
Limitations to be aware of.
- Cloak cannot protect short or hand-typed secrets (e.g. a 6-digit PIN) - the prompt guard uses known-prefix patterns only.
- Freshly generated secrets (created during a session) are not automatically registered — you must run
prismor cloak addafter generation. - Placeholders are only resolved for Bash. A raw secret in a Write, Edit, or MCP call is denied and vaulted by the secret guard, and MCP responses are scrubbed by the re-cloak hook, but those tools never receive the real value in place of a placeholder.
Frequently asked questions
How does Prismor Cloak keep secrets out of the model context?
Cloak replaces real secret values with placeholders before the agent ever sees them. When a tool call actually executes, Cloak substitutes the real value at execution time and scrubs it back out of the captured output, so the real secret never enters the transcript or the model context.
What is the difference between Cloak and Sweep?
Cloak is preventive: it stops secrets from reaching the model context in the first place. Sweep is remediative: it finds and redacts secrets that already leaked into AI tool caches and configuration files.
Where does Cloak store the real secret values?
Real values live in the Prismor secrets store rather than in your prompts or agent config. The agent references a placeholder name, and only the execution layer resolves it to the real value.
Does Cloak protect against prompt injection that tries to exfiltrate secrets?
Yes. Because the model only ever sees placeholders, an injected instruction telling the agent to print or send a secret returns the placeholder rather than the real credential. Cloak also includes a prompt guard for these attempts.