Skip to main content

Parry Guard Parry-guard

ci audit codecov Mentioned in Awesome Claude Code

A prompt injection scanner for AI coding tool hooks. It catches injection attacks, leaked secrets, and data exfiltration in tool inputs and outputs. It's built for Claude Code, and also works with Codex or any tool that runs command hooks with the same JSON input and output.

Early development: expect bugs and false positives. Tested on Linux and macOS.

Prerequisites

The ML models are gated on HuggingFace. Before installing:

  1. Create an account at huggingface.co.
  2. Accept the DeBERTa v3 license. All modes need it.
  3. For full mode, also accept the Llama Prompt Guard 2 license. Meta has to approve it.
  4. Create an access token at huggingface.co/settings/tokens.

Usage

Claude Code

Add to ~/.claude/settings.json:

With uvx:

{
  "hooks": {
    "PreToolUse": [{ "command": "uvx parry-guard hook", "timeout": 1000 }],
    "PostToolUse": [{ "command": "uvx parry-guard hook", "timeout": 5000 }],
    "UserPromptSubmit": [{ "command": "uvx parry-guard hook", "timeout": 2000 }]
  }
}

With rvx:

{
  "hooks": {
    "PreToolUse": [{ "command": "rvx parry-guard hook", "timeout": 1000 }],
    "PostToolUse": [{ "command": "rvx parry-guard hook", "timeout": 5000 }],
    "UserPromptSubmit": [{ "command": "rvx parry-guard hook", "timeout": 2000 }]
  }
}

With parry-guard on your PATH (from Nix, cargo install, or a release binary):

{
  "hooks": {
    "PreToolUse": [{ "command": "parry-guard hook", "timeout": 1000 }],
    "PostToolUse": [{ "command": "parry-guard hook", "timeout": 5000 }],
    "UserPromptSubmit": [{ "command": "parry-guard hook", "timeout": 2000 }]
  }
}

Codex

Add to ~/.codex/config.toml:

[[hooks.PreToolUse]]
matcher = "Bash|Read|Write|Edit|Glob|Grep|WebFetch|WebSearch|apply_patch|mcp__.*"

[[hooks.PreToolUse.hooks]]
type = "command"
command = "parry-guard hook"
timeout = 1

[[hooks.PostToolUse]]
matcher = "Bash|Read|WebFetch|Edit|apply_patch|mcp__.*"

[[hooks.PostToolUse.hooks]]
type = "command"
command = "parry-guard hook"
timeout = 5

[[hooks.UserPromptSubmit]]
matcher = ""

[[hooks.UserPromptSubmit.hooks]]
type = "command"
command = "parry-guard hook"
timeout = 2

Other hook runners

parry-guard hook reads one JSON payload from stdin and writes a JSON decision to stdout. Any tool with command hooks can use it, as long as it sends Claude/Codex-style fields (hook_event_name, tool_name, tool_input, tool_response, prompt) and respects block/deny responses.

Claude Code can ask you to confirm a PreToolUse call. Codex can't yet, so for Codex payloads parry-guard turns an "ask" into a block: it exits with a blocking code and prints the reason to stderr.

Other installation methods

From source:

# Default: ONNX backend, statically linked, 5-6x faster than Candle
cargo install --path bin

# Candle backend: pure Rust, no native deps, portable
cargo install --path bin --no-default-features --features candle

Nix (home-manager)

# flake.nix
{
  inputs.parry.url = "github:vaporif/parry-guard";

  outputs = { parry, ... }: {
    # pass parry to your home-manager config via extraSpecialArgs, overlays, etc.
  };
}
# home-manager module
{ inputs, pkgs, config, ... }: {
  imports = [ inputs.parry.homeManagerModules.default ];

  programs.parry-guard = {
    enable = true;
    package = inputs.parry.packages.${pkgs.system}.default;  # onnx (default)
    # package = inputs.parry.packages.${pkgs.system}.candle;  # candle (pure Rust, portable, ~5-6x slower)
    hfTokenFile = config.sops.secrets.hf-token.path;
    # hfTokenCommand = "pass show huggingface/token";  # or fetch from a keychain / secret manager
    ignoreDirs = [ "/home/user/repos/trusted" ];
    # askOnNewProject = true;  # ask before monitoring new projects (default: monitor right away)
    # claudeMdThreshold = 0.9;  # ML threshold for CLAUDE.md scanning (default 0.9)

    # scanMode = "full";  # fast (default) | full | custom

    # Custom models (auto-sets scanMode to "custom")
    # models = [
    #   { repo = "ProtectAI/deberta-v3-small-prompt-injection-v2"; }
    #   { repo = "meta-llama/Llama-Prompt-Guard-2-86M"; threshold = 0.5; }
    # ];
  };
}

Setup

HuggingFace token

Set your token one of these ways (first match wins):

export HF_TOKEN="hf_..."                                   # direct value
export HF_TOKEN_COMMAND="pass show huggingface/token"      # command that prints the token
export HF_TOKEN_PATH="/path/to/token"                      # file path
# or place token at /run/secrets/hf-token-scan-injection

HF_TOKEN_COMMAND lets the token stay in a keychain or secret manager. The hook passes the command to the daemon instead of running it. The daemon runs it with sh -c (cmd /C on Windows) each time it loads the models, and uses the trimmed stdout as the token. The token is kept only in memory. Some examples:

export HF_TOKEN_COMMAND="security find-generic-password -s huggingface -w"  # macOS Keychain
export HF_TOKEN_COMMAND="secret-tool lookup service huggingface"            # GNOME Keyring / KWallet
export HF_TOKEN_COMMAND="op read op://Private/HuggingFace/token"            # 1Password
export HF_TOKEN_COMMAND="bw get password huggingface"                       # Bitwarden

The command runs with no stdin and must finish within 30 seconds. Its stderr is discarded, and it must exit 0 with non-empty stdout. If it fails, the model doesn't load and scans fail closed. The daemon tries again on a later scan (up to 3 attempts per daemon). Because the daemon is detached, a command that waits for an interactive prompt (for example, a locked 1Password CLI) will time out. Unlock the store first or use a non-interactive credential.

With HF_TOKEN or a token file, the hook copies the token to ~/.parry-guard/.hf-token (mode 0600) so the daemon can read it. With HF_TOKEN_COMMAND, nothing is written, and the hook deletes any leftover copy when it next starts the daemon.

The daemon starts on the first scan, downloads the model on the first run, and stops after 30 idle minutes. Without Nix, set the env vars in your shell profile or pass flags (see Config).

Project scanning

By default, every new project is scanned from its first session, with no prompt. To skip a repo, run parry-guard ignore <path>.

To be asked first instead, set PARRY_ASK_ON_NEW_PROJECT=true (askOnNewProject = true in Nix). docs/opt-in-flow.md shows the full flow.

Command Effect
parry-guard monitor [path] Turn scanning on for a repo
parry-guard ignore [path] Turn scanning off for a repo
parry-guard reset [path] Clear state and caches, back to unknown
parry-guard status [path] Show current repo state and findings
parry-guard repos List all known repos and their states

path defaults to the current directory.

What each hook does

PreToolUse runs 7 checks in order and stops at the first hit:

  1. Skip ignored or unknown repos
  2. Enforce taint
  3. Scan CLAUDE.md
  4. Block exfiltration
  5. Catch destructive operations
  6. Block sensitive paths
  7. Scan input content for injection (Write, Edit, Bash, MCP tools)

PostToolUse scans tool output for injection and secrets. A hit taints the project.

UserPromptSubmit audits your .claude/ directory for dangerous permissions, injected commands, and hook scripts.

Daemon and cache

Hook calls start the daemon if it isn't running. You can also run it yourself with parry-guard serve --idle-timeout 1800.

Scan results are cached in ~/.parry-guard/scan-cache.redb for 30 days. A cache hit takes about 8ms, versus 70ms+ for inference. All projects share the cache, and it's pruned hourly.

Detection layers

The scanner fails closed: if it can't tell whether something is safe, it treats it as unsafe.

  1. Unicode: invisible characters (PUA, unassigned codepoints), homoglyphs, RTL overrides.
  2. Substring: Aho-Corasick matching for known injection phrases.
  3. Secrets: 40+ regex patterns for credentials (AWS, GitHub/GitLab, cloud providers, database URIs, private keys, and more).
  4. ML classification: a DeBERTa v3 model. Text is split into 256-char chunks with 25 chars of overlap, and long texts are scanned head and tail. Default threshold is 0.7.
  5. Bash exfiltration: tree-sitter AST analysis for network sinks, command substitution, obfuscation (base64, hex, ROT13), DNS tunneling, cloud storage, 60+ sensitive paths, and 40+ exfil domains.
  6. Script exfiltration: the same source-to-sink analysis for script files in 16 languages.
Scan modes
Mode Models Latency per chunk Backend
fast (default) DeBERTa v3 ~50-70ms any
full DeBERTa v3 + Llama Prompt Guard 2 ~1.5s candle only
custom User-defined (parry-guard/models.toml in your config dir) varies any

Use fast for interactive work. Use full for high security or batch scans (parry-guard diff --full). DeBERTa v3 is good at common injection patterns; Llama Prompt Guard 2 is better at subtle ones like role-play jailbreaks and indirect injection. full flags text if either model does, so it misses less, but each chunk takes about 20x longer.

full mode needs the candle backend, since Llama Prompt Guard 2 has no ONNX export. Build with --features candle --no-default-features.

Config

Global flags
Flag Env Default Effect
--threshold PARRY_THRESHOLD 0.7 ML detection threshold (0.0-1.0)
--claude-md-threshold PARRY_CLAUDE_MD_THRESHOLD 0.9 ML threshold for CLAUDE.md scanning (0.0-1.0)
--scan-mode PARRY_SCAN_MODE fast ML scan mode: fast, full, custom
--hf-token HF_TOKEN HuggingFace token (direct value)
--hf-token-command HF_TOKEN_COMMAND Shell command that prints the HuggingFace token. The daemon runs it, and the token is never written to disk
--hf-token-path HF_TOKEN_PATH /run/secrets/hf-token-scan-injection HuggingFace token file
--ask-on-new-project PARRY_ASK_ON_NEW_PROJECT false Ask before monitoring new projects instead of monitoring them right away
--ignore-dirs PARRY_IGNORE_DIRS Comma-separated parent directories. Every repo under them is skipped.
Subcommand flags
Flag Env Default Effect
serve --idle-timeout PARRY_IDLE_TIMEOUT 1800 Daemon idle timeout in seconds
diff --full false Add the ML scan (default is fast scan only)
diff -e, --extensions Filter by file extension (comma-separated)
Environment-only variables
Env Default Effect
PARRY_LOG warn Tracing filter (trace, debug, info, warn, error)
PARRY_LOG_FILE ~/.parry-guard/parry-guard.log Override log file path

Custom patterns go in parry-guard/patterns.toml under your config dir: ~/.config on Linux, ~/Library/Application Support on macOS. You can add or remove sensitive paths, exfil domains, and secret patterns. Custom models go in parry-guard/models.toml in the same dir and are used with --scan-mode custom. See examples/models.toml.

ML backends

You need exactly one backend; the build fails without one. Nix uses ONNX on x86_64-linux, aarch64-linux, and aarch64-darwin. On other platforms, use the candle package.

Feature
onnx-fetch ONNX, statically linked. Downloads ORT at build time. Default.
candle Pure Rust ML. Portable, no native deps. About 5-6x slower.
onnx ONNX, you provide ORT_DYLIB_PATH.
onnx-coreml ONNX with CoreML on Apple Silicon. Experimental.

Performance

Apple Silicon, release build, fast mode (DeBERTa v3 only). To reproduce, run just bench-candle or just bench-onnx with HF_TOKEN set.

Scenario ONNX (default) Candle
Short text (1 chunk) ~10ms ~61ms
Medium text (2 chunks) ~32ms ~160ms
Long text (6 chunks) ~136ms ~683ms
Cold start (daemon + model load) ~580ms ~1s
Fast scan short-circuit ~7ms ~7ms
Cached result ~8ms ~8ms

Contributing

See CONTRIBUTING.md.

Credits

License

MIT

Llama Prompt Guard 2 (used in full mode) has its own license, the Llama 4 Community License. See LICENSE-LLAMA.

Metadata

Release files for parry-guard 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for parry-guard 0.1.6
File
parry_guard-0.1.6-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
parry_guard-0.1.6-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
parry_guard-0.1.6-py3-none-macosx_14_0_x86_64.whl Python 3 none macOS 14.0+ x86-64 Details
parry_guard-0.1.6-py3-none-macosx_14_0_arm64.whl Python 3 none macOS 14.0+ ARM64 Details

Total release size: 33.7 MB

Release files / parry_guard-0.1.6-py3-none-musllinux_1_2_x86_64.whl

Download URL parry_guard-0.1.6-py3-none-musllinux_1_2_x86_64.whl
Size 8.9 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
82925d6144aee4407b9b5e77d3f60a689c71348493629b354a100e60d62b1865
BLAKE2b-256 checksum
How to use checksums
a72036d771f1c2585d8dba0410d5ca907f73a5857776d7fffdc6e9b29d0c0413
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / parry_guard-0.1.6-py3-none-musllinux_1_2_aarch64.whl

Download URL parry_guard-0.1.6-py3-none-musllinux_1_2_aarch64.whl
Size 8.5 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
ab9fa6e7b3efb9ed644ffff4a32f109333096d2c8d7b2795c32795c241abdbba
BLAKE2b-256 checksum
How to use checksums
d9a28b9185bd7ef36615650f8fc4944ad80acc3509ae24ae6274b6b1b46f8543
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / parry_guard-0.1.6-py3-none-macosx_14_0_x86_64.whl

Download URL parry_guard-0.1.6-py3-none-macosx_14_0_x86_64.whl
Size 8.2 MB
Tags Python 3 macOS 14.0+ x86-64
SHA-256 checksum
How to use checksums
53c2499d174c2f80acfa83ce6970364d1439e491db8277b337f93b561f1d6bad
BLAKE2b-256 checksum
How to use checksums
14998c2296d91d9ac2136640cb7c34f808f178f0ade6e975aee4c206a582bcfd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / parry_guard-0.1.6-py3-none-macosx_14_0_arm64.whl

Download URL parry_guard-0.1.6-py3-none-macosx_14_0_arm64.whl
Size 8.1 MB
Tags Python 3 macOS 14.0+ ARM64
SHA-256 checksum
How to use checksums
27bc2c8dcd22a8c0df1a26464864fcfeb090871f99978e72b4f1cf56ed8b19ba
BLAKE2b-256 checksum
How to use checksums
18fca9c3e375d243bc2612ad6237725214dafc5ead271936e8d2ef777279172d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page