> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frostwolf.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Claude Code

> Cloak personal data and secrets before Claude sees them, and block prompt injection, in every Claude Code session.

The FrostWolf plugin for Claude Code brings Guard and Cloak into a coding session. Personal data and secrets in files, command output, and tool results are replaced with tokens before Claude sees them, and put back when Claude writes a file or runs a command. Injected instructions are taken out of what Claude reads, and the calls that reach outside your project are validated before they run.

The plugin runs as Claude Code [hooks](https://code.claude.com/docs/en/hooks). It changes no base URL and runs no proxy. Detection runs on the control plane, as it does for the SDKs.

<Note>
  The plugin is in beta. It is tested on macOS. Linux runs the same code, and Windows is untested.
</Note>

## Requirements

* Claude Code 2.1.295 or newer
* Node.js 18 or newer on your `PATH`
* A FrostWolf API key from [frostwolf.app](https://frostwolf.app)

## Install

Run one command in your shell, with your API key:

```bash theme={null}
claude plugin install frostwolf --marketplace FrostWolfAI/frostwolf-skills --config api_key=YOUR_API_KEY
```

This adds the FrostWolf marketplace, installs the plugin, and keeps your key in your system's secure credential store, not in `settings.json`. Start a new session to load the hooks.

<Tip>
  The console's **Integrations** page shows this command with a key filled in. Choose **Create a key for Claude Code** and copy the command.
</Tip>

A key typed into a shell command stays in your shell history. To be prompted for the key instead, install from a Claude Code session:

```text theme={null}
/plugin install frostwolf --marketplace FrostWolfAI/frostwolf-skills
```

## How a value travels

<Steps>
  <Step title="Claude reads a file">
    `customers.csv` holds `ada,ada@example.com`. Before the result reaches Claude, FrostWolf replaces the address. Claude sees `ada,[[email:e64094f527c3]]`.
  </Step>

  <Step title="Claude uses the token">
    Claude writes `copy.csv` with the content `ada,[[email:e64094f527c3]]`.
  </Step>

  <Step title="FrostWolf restores it">
    Before the write runs, FrostWolf puts the address back. The file on disk holds `ada,ada@example.com`.
  </Step>

  <Step title="You see the real value">
    Your terminal shows the address where Claude wrote the token. Claude and the session transcript keep the token.
  </Step>
</Steps>

The same value always gets the same token within a session, so Claude can tell that two mentions are one person. A token belongs to the session that made it. Another session's token is refused, not restored.

## What each hook does

| Hook | What FrostWolf does |
| - | - |
| `PostToolUse` | Cloaks personal data and secrets in every tool result, and takes injected instructions out, before Claude sees the result. |
| `PreToolUse` | Puts real values back into a tool call. Validates shell, web fetch, and MCP calls before they run. |
| `PostToolBatch` | Checks each result exactly as Claude will receive it, and stops the turn before the next model call if one was not cloaked. |
| `UserPromptSubmit` | Blocks prompt injection. Refuses a prompt that carries personal data, or attaches a file with `@` that does, and offers a version to send instead. |
| `MessageDisplay` | Shows real values on your screen. |
| `SessionStart`, `SessionEnd` | Tells Claude how tokens work, and erases the session's values when it ends. |

## Tool output

Every tool result is checked, whichever tool produced it: file reads, searches, shell output, web fetches, and MCP tools.

Personal data and secrets become tokens. Every category is tokenized, including credentials and card codes, which the SDKs redact by default. In a coding session Claude writes files from what it read, and a redacted secret would be written back into your file as the redaction label. A token comes back as the original.

An injected instruction is taken out and replaced with `[FROSTWOLF_REMOVED]`, and Claude is told the rest of the result is untrusted. A write that contains the marker is refused, so Claude cannot overwrite your original text with it.

## Tool calls

Before a tool runs, FrostWolf restores every token in its arguments. The call then goes through your permission rules as usual, evaluated against the restored arguments. Restoring a value never approves a call.

A call that uses a token FrostWolf does not hold is refused, rather than written into a file as literal text. That happens when Claude alters a token, or uses one from a session that has ended. Claude is told to read the source again. A token-shaped string that was already in your files, such as an example in documentation, is recognised and let through.

Shell, PowerShell, web fetch, and MCP calls are validated before they run, the same check as [`validateToolCalls`](/guard/tool-calls). They are validated as Claude wrote them, with tokens, so the values never travel for validation. File edits are not validated: code under test legitimately contains strings an argument check would flag.

## Prompts

A hook can refuse a prompt but cannot rewrite it. A prompt with a prompt injection is blocked.

A prompt that carries personal data is refused by default, and the refusal offers the cloaked prompt to send instead. Its tokens are already held, so they restore like any other:

```text theme={null}
FrostWolf did not send this prompt:
- The prompt carries personal data: email (1).

A hook cannot rewrite a prompt.

Submit this instead. FrostWolf cloaks a file when Claude reads it with a tool,
and restores each value wherever Claude uses it in a tool call:

Send a welcome note to [[email:b9407ee406d3]] please
```

Set `prompt_personal_data` to `warn` to send such a prompt as typed and be told about it instead.

A username inside your working directory or home directory path does not refuse a prompt. Claude Code gives Claude the working directory in every session, so it is not new exposure.

### Files attached with @

Claude Code attaches a file you mention with `@` to the prompt itself, without a tool call, so no tool hook sees it. FrostWolf reads the file when you submit the prompt. If it carries personal data, or an injected instruction, the prompt is refused, and the version offered names the file as a plain path, so Claude reads it with a tool and FrostWolf cloaks it:

```text theme={null}
FrostWolf did not send this prompt:
- customers.csv, attached with @, carries personal data: email (1).

Claude Code attaches a file mentioned with @ without a tool call, so FrostWolf
cannot cloak it.

Submit this instead. ...

Summarize customers.csv
```

An attached file with an injection is refused even in `warn` mode. A file larger than 256 KB is not checked, and is refused unless `on_unavailable` is `allow`.

## The batch check

`PostToolBatch` runs after every tool in a turn has finished and before Claude Code sends the results to the model. It sees each result exactly as Claude will, and it stops the turn if one should not go through:

* **A rewrite was dropped.** Claude Code discards a rewritten result whose shape does not match the tool's. FrostWolf keeps the shape by construction, and this check confirms it.
* **A result was not checked.** FrostWolf could not reach the control plane, or the hook failed.
* **A failed call printed personal data or an injection.** A command that exits non-zero goes to a different hook, which cannot rewrite its output. The batch check reads it instead.

When the turn stops, the result is still in the conversation and would be sent with your next message. Run `/rewind` and go back to before that step to remove it. The stop message names the tool and the types found, never a value.

## Settings

Set these in `/plugin` or `/config`. When the hooks are installed without the plugin, use the environment variable.

| Option | Environment variable | Default | Values |
| - | - | - | - |
| `api_key` | `FROSTWOLF_API_KEY` | none | Your API key. Required. |
| `endpoint` | `FROSTWOLF_ENDPOINT` | `https://api.frostwolf.app` | Control plane base URL. |
| `prompt_personal_data` | `FROSTWOLF_PROMPT_PERSONAL_DATA` | `block` | `block` refuses and offers a cloaked version. `warn` sends as typed and tells you. |
| `on_unavailable` | `FROSTWOLF_ON_UNAVAILABLE` | `block` | `block` refuses what FrostWolf could not check. `allow` lets it through. |
| `display` | `FROSTWOLF_DISPLAY` | `restore` | `restore` shows real values on screen. `tokens` shows tokens, as Claude sees them. |

## Failure handling

The plugin fails closed by default, like the SDKs. See [Failure handling](/guard/failure-handling).

| When FrostWolf cannot check | `on_unavailable: block` (default) | `on_unavailable: allow` |
| - | - | - |
| A prompt | Refused | Sent, with a notice |
| A tool call | Refused | Runs. If its tokens cannot be restored, it is refused instead, so no token reaches a file |
| A tool result | The turn stops at the batch check | Reaches Claude unchecked |

The gate hooks are registered with `onFailure: "block"`, so a hook that crashes, cannot start, or times out refuses the action rather than letting it through. Without an API key, every prompt is refused with a message saying how to set one.

## Privacy

<CardGroup cols={2}>
  <Card title="Values stay on your machine" icon="laptop">
    Tokenized values are held in memory by a local service, behind a socket only your account can open. They never touch disk.
  </Card>

  <Card title="Values are never echoed" icon="eye-slash">
    The control plane returns types and ranges. Block messages, stop messages, and logs name types and counts only.
  </Card>

  <Card title="Erased when you are done" icon="trash">
    A session's values are erased when it ends. The service exits after two hours without activity, and values expire after twelve hours.
  </Card>

  <Card title="Telemetry is counts only" icon="chart-simple">
    The console sees how many values of each type were cloaked, never the values or the tokens.
  </Card>
</CardGroup>

### What a device reports

When a session starts, the plugin checks in with this device's name, operating system and architecture, and the plugin, Claude Code, and Node.js versions, under a random installation id it generates once. The console's **Integrations** page lists each device with its session count and when it was last seen. The person behind a device comes from the console member who created its key, not from the device. A check-in carries no prompt, file, or value, and a failed check-in never delays or blocks a session.

### What reaches the control plane

To be checked, text has to reach the control plane: tool results, prompts, and files attached with `@` are sent for detection, as they are when you use an SDK. See [Security and privacy](/operations/security-and-privacy).

Claude Code keeps its own session transcripts and prompt history on disk. They hold what Claude saw, with tokens. Output the batch check stopped, and a prompt FrostWolf refused, are kept there as they were.

## Limits

<AccordionGroup>
  <Accordion title="Content loaded without a tool call" icon="file-circle-question">
    `CLAUDE.md` and other memory files are loaded into Claude's context directly, and no hook sees them. Keep personal data and secrets out of them. Files attached with `@` are the exception: FrostWolf reads those itself.
  </Accordion>

  <Accordion title="Prompts are refused, not rewritten" icon="message-slash">
    A hook cannot change a prompt. FrostWolf refuses one that carries personal data and offers the cloaked version, which you send yourself.
  </Accordion>

  <Accordion title="Resumed sessions" icon="clock-rotate-left">
    Tokens live only as long as the local service and the session. In a session resumed after it ended, or after the service restarted, earlier tokens cannot be restored. Claude is told to read the source again, which gives it current tokens.
  </Accordion>

  <Accordion title="Latency and usage" icon="gauge">
    Each tool result is a control-plane request, so expect a short delay after each tool runs. Text over 12,000 characters is split across requests. Text read again unchanged within ten minutes, such as a file read twice, reuses the earlier answer. Requests count toward your plan like any other scan.
  </Accordion>

  <Accordion title="Detection accuracy" icon="bullseye">
    Detection is a model, so it has false positives and false negatives. See [Cloak limits](/cloak/limits).
  </Accordion>
</AccordionGroup>

## Without the plugin

Clone [frostwolf-skills](https://github.com/FrostWolfAI/frostwolf-skills) and point your own hooks at its `claude-code/dist/hook.mjs`. Every event uses the same command:

```json theme={null}
{
  "type": "command",
  "command": "node",
  "args": ["/path/to/frostwolf-skills/claude-code/dist/hook.mjs"]
}
```

Register it for the events in the repository's `hooks/hooks.json`, with the same timeouts and `onFailure` settings, and set `FROSTWOLF_API_KEY` in the environment.

## Troubleshooting

| What you see | What to do |
| - | - |
| `FrostWolf has no API key` | Run `/plugin`, open frostwolf, and set the API key. |
| `FrostWolf's local service is not responding` | Check that `node` is on your `PATH`. The next hook starts the service again. |
| `This call uses [[…]], which is not a value FrostWolf holds` | Claude altered a token, or the session's values were erased. Ask Claude to read the source again. |
| The turn stops with `FrostWolf stopped before this output reached Claude` | Run `/rewind` and go back to before that step. See [The batch check](#the-batch-check). |

The local service writes errors, never values, to `service.log` in `~/.claude/plugins/data/` under the plugin's directory.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.