Open Source · Apache 2.0

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.

View on GitHub

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):

PreToolUse: Bash|Write|Edit|MultiEdit|mcp__.*
Secret Guard

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.

PreToolUse: Read
Read Guard

Denies a built-in Read of a file that holds a registered secret, since Read output has no post-hoc scrub.

PreToolUse: Read|Bash
Env Guard

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.

PreToolUse: Bash
Decloak

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.

PostToolUse: mcp__.*
Re-cloak MCP

Scrubs real secret values from MCP tool responses before they are returned to the model, replacing any leaked values with their placeholders.

UserPromptSubmit
Prompt Guard

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.

Stop (optional)
Residue Sweep

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.

Placeholder flow
# 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 through prismor cloak run -- <command> instead
Terminal
$ brew install jq

Install hooks

Run from your project root. This wires the hooks into the current project's Claude Code settings:

Terminal
$ prismor cloak install
Output
Installed 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:

Terminal
$ prismor cloak install --scope user

Install flags

FlagDescription
--scope projectInstall hooks into the current project's .claude/settings.json (default)
--scope userInstall hooks into your global ~/.claude/settings.json - covers all projects (--scope global is equivalent)
--agent claude|hermes|allWhich 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-guardSkip the UserPromptSubmit soft-block hook (opt out of prompt scanning)
--no-secret-guardSkip the PreToolUse hook that detects, vaults, and blocks raw secrets in tool calls
--no-read-guardSkip the PreToolUse hook that denies reading files containing a registered secret
--no-env-guardSkip the PreToolUse hook that denies reading .env-style files whose entries are not yet imported into the vault
--sweep-on-stopAlso 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.

Terminal
$ prismor cloak add stripe_key
Enter value for @@SECRET:stripe_key@@ (input hidden): ••••••••••••••••••••••••••
Registered @@SECRET:stripe_key@@ (26 bytes) at ~/.prismor/secrets/stripe_key

To register from a file (useful for long keys or certificates):

Terminal
$ prismor cloak add my_cert --from-file ./certs/server.pem

To import an existing dotenv file, registering every KEY=VALUE entry as @@SECRET:KEY@@:

Terminal
$ prismor cloak add --env-file .env

Once registered, reference the secret anywhere in a command or prompt using its placeholder:

curl -H "Authorization: Bearer @@SECRET:stripe_key@@" https://api.stripe.com/v1/charges

List registered secrets

Shows names, sizes, modification times, and whether a secret was auto-registered by the prompt guard. Values are never displayed.

Terminal
$ 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

Terminal
$ prismor cloak remove stripe_key
Removed @@SECRET:stripe_key@@

Check status

Terminal
$ 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.

Example: detected secret in prompt
# 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:

ProviderPatterns
Stripesk_live_*, sk_test_*, rk_live_*
GitHubgithub_pat_*, ghp_*, gho_*, ghs_*, ghu_*
AWSAKIA* (access key), ASIA* (temp access key)
GoogleAIza* (API key)
Slackxox[bpoar]-*
GitLabglpat-*
JWTeyJ*.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:

!!allow The old key sk_live_... has been rotated. Please update the docs to reflect the new format.

The bypass applies only to that one message. The next submission is scanned normally.

Commands

CommandDescription
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 listList registered secret names, sizes, and timestamps (values never shown)
prismor cloak remove <name>Delete a registered secret from the store
prismor cloak statusShow 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 uninstallRemove cloaking hooks from settings.json (--agent hermes|all for the Hermes plugin)

Environment variables

VariableDescription
PRISMOR_SECRETS_DIROverride the secrets store location (default: $PRISMOR_HOME/secrets, i.e. ~/.prismor/secrets)
PRISMOR_PROMPT_STASH_TTLSeconds 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.

Terminal
# 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.

Terminal
# 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:

~/.prismor/secrets/
  • 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 add after 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.