Blocks secrets, credentials, and sensitive files from ever entering an AI coding agent's context window.
Project description
ctxguard
Your AI coding agent should never see your API keys.
ctxguard blocks secrets before they enter the model's context.
ctxguard is a local Claude Code plugin and standalone Python CLI. It intercepts sensitive tool calls before execution and returns a masked explanation instead of letting likely secrets, credentials, or sensitive files enter context.
30-second installation
Claude Code plugin (no Python package install)
Inside Claude Code, add this repository as a marketplace and install ctxguard:
/plugin marketplace add Ismail-Rhoulam/ctxguard
/plugin install ctxguard@ctxguard-plugins
/reload-plugins
The plugin includes its own Python source. It requires python3 3.9 or newer
on PATH, but does not require pip install ctxguard.
For local development, run claude --plugin-dir . from this repository.
Standalone CLI
python3 -m pip install ctxguard
ctxguard init
ctxguard init creates .ctxguard.toml if absent and registers CLI-backed
hooks in .claude/settings.json. If that settings file already exists, ctxguard
asks before modifying it; non-interactive use requires the explicit --yes
flag. It merges entries and preserves unrelated settings.
Why ctxguard?
| Tool | Primary protection |
|---|---|
.gitignore |
Prevents selected files from being committed |
| Gitleaks / TruffleHog | Detects secrets in repositories and commits |
| ctxguard | Blocks sensitive content before an AI coding agent reads it |
ctxguard complements repository scanners; it does not replace them. Gitleaks and TruffleHog protect repository history and workflows. ctxguard addresses a different boundary: the agent tool call immediately before content enters the model context.
How it works
Claude Code hooks can allow or deny a tool call, but cannot rewrite its result. ctxguard is therefore block-and-report, not silent redaction:
- A
PreToolUsehook inspectsRead,Edit,Write,Bash,Grep, andGlobinputs before execution. - Sensitive filenames, small text-file contents, common shell reads, environment dumps, and inline high-confidence secrets are checked locally.
- In
blockmode, a flagged call is denied with a masked reason. Inwarnmode, it is allowed with warning context. - A capped
SessionStartscan tells Claude which files to avoid without returning their raw contents.
The CLI uses the same detector module as both plugin hooks.
Example blocked tool call
Claude Code attempts: Read .env
ctxguard intercepts: PreToolUse
Tool call result: DENIED
Explanation: ctxguard blocked this Read call: .env is a sensitive file by name
Raw values printed: no
Run the synthetic demonstration with python3 scripts/demo.py. See
docs/demo.md for details and safe GIF-recording instructions.
What it detects
- AWS access key IDs and secret access keys
- GitHub classic and fine-grained personal access tokens
- Slack tokens
- Stripe live and test secret keys
- GCP API keys (
AIza...) and service-account JSON (content marker plus the filename rule) - Azure storage, Service Bus, Event Hub, and IoT Hub connection string keys
(
AccountKey=...,SharedAccessKey=...) - Twilio API key SIDs and SendGrid API keys
- OpenAI-style keys (
sk-...,sk-proj-...) and Anthropic keys (sk-ant-...) - Database URLs with inline passwords: Postgres, MySQL/MariaDB (including
SQLAlchemy dialect+driver and Rails
mysql2://schemes), MongoDB, Redis, AMQP, and MSSQL - private key blocks and JWT-shaped strings
- credential-like assignments that pass length, digit, and entropy thresholds
- high-entropy values assigned to names containing
key,secret,token,password, orcredential - sensitive filenames including
.env, non-template.env.*, SSH keys,*.pem,*.pfx,credentials.json, andservice-account*.json
Detection is heuristic and local. ctxguard makes no network calls and uses no telemetry or machine learning.
Configuration
Copy .ctxguard.toml.example to .ctxguard.toml, or run ctxguard init:
mode = "block" # or "warn"
allowlist = [
"tests/fixtures/*",
]
[[custom_patterns]]
name = "acme_internal_token"
regex = "acme_[A-Za-z0-9]{32}"
confidence = "high"
Allowlisting suppresses detection and should be narrow. Custom regular expressions are applied alongside built-ins.
CLI
ctxguard scan # scan current directory; exit 1 on findings
ctxguard scan path/ --json # machine-readable masked report
ctxguard init # create config and register Claude Code hooks
ctxguard doctor # verify PATH resolution and hook registration
ctxguard --version
ctxguard doctor checks that the ctxguard executable resolves on PATH
(what the hooks registered by ctxguard init actually invoke), that the
resolved binary's version matches the one running the check, that
.claude/settings.json has both hooks registered with the expected matcher,
that .ctxguard.toml parses if present, and does an end-to-end smoke test by
actually invoking the PreToolUse hook once. Exits 1 if anything is broken
(--json for scripting), 0 if warnings only or fully healthy.
Honest limitations
- Pattern and entropy matching has false positives and false negatives. A secret that resembles prose may pass; an unusual random value may be flagged.
- ctxguard blocks calls; it cannot redact tool output in flight. If a detector misses sensitive content in a normal-looking file, that content can enter context.
- The Bash parser is heuristic and cannot model every shell construction.
- Filename checks intentionally deny some files without inspecting content.
- Files over 1 MB and binary files are not content-scanned. Session scans are time- and file-capped for responsiveness.
- Hook errors fail open so ctxguard cannot break a Claude Code session. This preserves availability but means a broken hook does not provide protection.
- Native Windows compatibility is not claimed; current testing covers the Python package and hook contract on Unix-like CI runners.
- Any non-placeholder password inline in a database URL is flagged, including
weak or "demo" ones (
hunter2,letmein123). This is intentional: a weak credential is still a working credential, and ctxguard can't judge which ones are safe to expose. Allowlist known-safe fixtures in.ctxguard.tomlinstead. - GCP
AIza...-style API keys are flagged even though some (notably Firebase web config keys) are designed to be usable client-side. Restrict such keys by API and referrer in the Google Cloud Console rather than relying on ctxguard to tell intent apart; allowlist the specific file if it's noisy. - A handful of well-known, publicly documented development constants (e.g. the Azurite/Azure Storage Emulator default key) are excluded by name since they're identical on every machine and not secrets. This list is small and only grows for equally unambiguous cases.
Use layered controls: least-privilege credentials, a secrets manager,
.gitignore, repository secret scanning, short-lived tokens, and revocation.
Security model
ctxguard trusts the local Python runtime, Claude Code's hook execution, the installed plugin files, and project configuration. Detection and reporting stay on the local machine. Reports contain masked matches, filenames, categories, and line numbers, never intentionally the full matched value.
An attacker who can alter the plugin, configuration, interpreter, hook input, or files between inspection and execution may bypass it. ctxguard is a preventive guardrail, not a sandbox, secrets manager, or formal non-disclosure guarantee. See SECURITY.md for private reporting guidance.
Development
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
.venv/bin/python -m build
.venv/bin/twine check dist/*
Test the plugin locally with claude plugin validate . and
claude --plugin-dir .. Fixtures containing detector-positive synthetic values
are confined to tests/fixtures/ and allowlisted by the repository example
configuration.
Contributing
Issues and pull requests are welcome. Detector changes must include positive and negative tests, and outputs must never reveal an unmasked match. Keep the project local, dependency-light, auditable, and honest about limitations. Run the complete test suite before submitting. See SECURITY.md for security reports rather than opening a public bypass report.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ctxguard-0.1.0.tar.gz.
File metadata
- Download URL: ctxguard-0.1.0.tar.gz
- Upload date:
- Size: 35.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d46ff301772d44ee824c9d143146d8cf7b165c2a3b0e0e6058b7b55d93a8483b
|
|
| MD5 |
2fa256b038f7cced43546e7851d8fcf2
|
|
| BLAKE2b-256 |
d0beb1d98d91ab80d08b08f647e10c946f44c53044d878928f8517f075b572ae
|
Provenance
The following attestation bundles were made for ctxguard-0.1.0.tar.gz:
Publisher:
publish.yml on Ismail-Rhoulam/ctxguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctxguard-0.1.0.tar.gz -
Subject digest:
d46ff301772d44ee824c9d143146d8cf7b165c2a3b0e0e6058b7b55d93a8483b - Sigstore transparency entry: 2153804184
- Sigstore integration time:
-
Permalink:
Ismail-Rhoulam/ctxguard@ae04d5fae8442ba72fd7a4e0d50ddf3d832d6328 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Ismail-Rhoulam
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ae04d5fae8442ba72fd7a4e0d50ddf3d832d6328 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ctxguard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ctxguard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59164662f3adbb976c74b4647cf440e6f25ba48a5c670d9586bd4f2fbc6e2606
|
|
| MD5 |
86c21191cf5d65f5ae501a68a40bb7d1
|
|
| BLAKE2b-256 |
be817f64336cf66d2827734d2e8a1aa0bcb18dd2c6c95295d5fe9597ddcee4fd
|
Provenance
The following attestation bundles were made for ctxguard-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Ismail-Rhoulam/ctxguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ctxguard-0.1.0-py3-none-any.whl -
Subject digest:
59164662f3adbb976c74b4647cf440e6f25ba48a5c670d9586bd4f2fbc6e2606 - Sigstore transparency entry: 2153804236
- Sigstore integration time:
-
Permalink:
Ismail-Rhoulam/ctxguard@ae04d5fae8442ba72fd7a4e0d50ddf3d832d6328 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Ismail-Rhoulam
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ae04d5fae8442ba72fd7a4e0d50ddf3d832d6328 -
Trigger Event:
release
-
Statement type: