Agent operations scanner — grades a repo against the Agent Flight Rules (AFR) with a plain-English GO / NO-GO / PROVISIONAL verdict. Orchestrates open-source scanners (SkillSpector, gitleaks, OSV-Scanner) behind an interpretation layer. Defensive tooling.
Project description
Runworthy
An agent operations scanner. Runworthy scans a code repository for AI-agent security and operational-safety gaps and returns a plain-English GO / NO-GO / PROVISIONAL grade against the Agent Flight Rules (AFR) — for the small teams shipping agents who can't read a SARIF file.
It is defensive tooling: it orchestrates published open-source scanners (NVIDIA SkillSpector, gitleaks, OSV-Scanner) behind adapters and adds an interpretation layer on top. It does not build exploits, malware, or attack tooling.
How it grades. Phase 0 is the deterministic core: fingerprint the agent surface, run the three contained detectors, and produce a self-contained
ReadinessReportwhere every finding is evidence-bound to afile:lineand secrets are redacted. Phase 1 adds the AFR grade: a LangGraph interpretation layer maps that evidence to controls, translates each into plain English, and computes the GO / NO-GO / PROVISIONAL verdict. Every claim traces to a finding, and it says "couldn't determine" wherever the code can't. Use--no-llmfor the deterministic report alone.
Install
pip install runworthy # deterministic core + --no-llm
pip install "runworthy[llm]" # + the AFR grade (interpretation layer)
The [llm] extra pulls anthropic, langgraph, and openai. A graded scan
needs a Claude API key (bring your own — the CLI is BYOK, and only redacted
findings ever reach the model). By default that key is ANTHROPIC_API_KEY; if you
hold an OpenRouter key instead, point Runworthy at it with one env var (see
Bring your own key). Without the extra or a key, runworthy scan still runs and returns the provisional report.
Runworthy orchestrates three external scanners. They are version-pinned,
resolved on your PATH, and never vendored — install them once and Runworthy
finds them:
| Tool | Pinned | Install |
|---|---|---|
| gitleaks | 8.30.1 |
scoop install gitleaks · brew install gitleaks · release binary |
| osv-scanner | 2.4.0 |
scoop install osv-scanner · brew install osv-scanner · release binary |
| SkillSpector | 2.3.9 |
pipx install "git+https://github.com/NVIDIA/skillspector.git@v2.3.9" or uv tool install git+https://github.com/NVIDIA/skillspector.git |
Then verify:
runworthy doctor
doctor reports each tool's presence, resolved version, and any pin mismatch,
and exits nonzero if a required tool is missing — so CI fails loudly rather
than scanning with half a toolchain.
Usage
# scan a public repo and print the AFR report as Markdown (needs a key)
runworthy scan https://github.com/langchain-ai/open_deep_research
# owner/repo shorthand; write the machine-readable report
runworthy scan langchain-ai/open_deep_research --format json -o report.json
# deterministic findings only — no model, no key, fully offline
runworthy scan ./path/to/repo --no-llm
# don't prompt (skip the operational overlay; stays PROVISIONAL)
runworthy scan ./repo --non-interactive
By default scan renders a plain-English Markdown report to stdout (--format json for the self-contained ReadinessReport; -o FILE to write either, format
inferred from the extension). A one-line summary goes to stderr.
After the scan, the CLI asks the handful of Boldface questions code can't see (a
named owner for AFR-01, a tested kill-switch for AFR-20, an incident runbook for
AFR-25) and folds your answers into the grade. --non-interactive skips them.
Other flags: --byok (fail if no key rather than falling back), --token-budget N
(a per-scan ceiling; a breach fails loud), --model ID, --pretty.
Bring your own key
The graded step runs Claude, and you can reach it two ways. Direct Anthropic is the default — one fewer intermediary in the privacy story:
export ANTHROPIC_API_KEY=sk-ant-...
runworthy scan langchain-ai/open_deep_research
If you hold an OpenRouter key (many teams do), switch transport with env vars — no flags, no code change:
export RUNWORTHY_MODEL_TRANSPORT=openai_compat # default is 'anthropic'
export OPENROUTER_API_KEY=sk-or-...
runworthy scan langchain-ai/open_deep_research
openai_compat talks to any OpenAI-compatible endpoint using strict JSON-schema
structured output. It defaults to OpenRouter (https://openrouter.ai/api/v1) and,
there, pins routing to Anthropic upstream so a graded scan always runs on Claude.
Point it elsewhere with RUNWORTHY_MODEL_BASE_URL and a generic
RUNWORTHY_MODEL_API_KEY; set the model with --model (or RW_MODEL), which maps
to the provider's slug (e.g. claude-sonnet-5 → anthropic/claude-sonnet-5).
Either way, only redacted findings reach the model, and the same token budget and
validate-reject-retry checks apply.
What a scan emits
verdict:GO(every Boldface assessed ≥1 with evidence),NO_GO(a Boldface control confirmed at 0), orPROVISIONAL(a Boldface control not yet assessed — unknown never counts as a failure).band: the AFR band (Exposed…Resilient), or a provisional band with a count of controls assessed.posture_items[]: the interpreted assessment per control — status, confidence tier (Confirmed / Likely gap — verify / Couldn't determine), evidence ids, plain explanation, and the fix.findings[]: normalized, deduplicated, redacted findings, each withfile:lineand the detector(s) that found it — embedded, so the report renders offline.agent_mapand full provenance (commit_sha,engine_version,detector_versions,generated_at).
On a repo with no agent surface, Runworthy exits early with an honest "no agent surface detected" rather than inventing findings.
How it works
scan target ─▶ intake ─▶ fingerprint ─▶ detectors ─▶ normalize ─▶ interpret ────────▶ overlay ─▶ ReadinessReport
(clone/ (AgentMap) gitleaks · (dedup, (LangGraph: (the (graded, self-
resolve OSV · redact) map→translate→ Boldface contained report)
SHA) SkillSpector) synthesize) Q&A)
Design rule: deterministic detectors produce evidence; they never produce the
grade. The model only reasons over evidence that already exists and must cite
it; the band and verdict are computed in tested code (afr.py), not by the model.
Nothing is asserted that isn't traceable to a file:line or an explicit,
labeled "couldn't determine." See
docs/adapters.md for the adapter containment rules — in
particular the SkillSpector filter that turns an 86-finding false-positive flood
into a handful of defensible findings.
Privacy
Local and private scans run entirely on your machine — nothing is uploaded.
The engine's own network egress is limited to git clone (intake) and the
pinned detectors' own remote, OSV.dev. Secret values never appear in any
emitted artifact (gitleaks runs with --redact; a redaction pass masks every
other snippet).
Development
git clone https://github.com/geofflava/runworthy && cd runworthy
python -m venv .venv && . .venv/Scripts/activate # or .venv/bin/activate
pip install -e ".[dev,llm]"
python -m runworthy.schema_export schemas # regenerate JSON Schemas
pytest # grade, interpret, overlay, render, eval, golden
Tests tagged @pytest.mark.tools need the pinned detector binaries on PATH and
skip cleanly when a tool is absent. The eval suite (tests/test_evals.py) replays
recorded model output against labeled scans (no key needed) and is the release
gate for the grade; see evals/README.md to add a labeled repo.
License
Runworthy is MIT licensed. The orchestrated scanners retain their own
permissive licenses (SkillSpector — Apache-2.0, gitleaks — MIT, OSV-Scanner —
Apache-2.0); see NOTICE. TruffleHog (AGPL-3.0) is deliberately
excluded. The Agent Flight Rules framework is CC BY 4.0.
Maintained by Geoff "Lava" Lavagnino · Obsidicore LLC.
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 runworthy-0.2.0.tar.gz.
File metadata
- Download URL: runworthy-0.2.0.tar.gz
- Upload date:
- Size: 63.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b1b2383bb1487e83590d0fef621dccf2031c479481e2c7e568bbea2dc39fe98
|
|
| MD5 |
7e92383d77b2799b963dcd887cc9dbb4
|
|
| BLAKE2b-256 |
850cd7af3e22c4d61f4d40742895089980699dd875c7939fd764f2b4eb87b09b
|
Provenance
The following attestation bundles were made for runworthy-0.2.0.tar.gz:
Publisher:
publish.yml on geofflava/runworthy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runworthy-0.2.0.tar.gz -
Subject digest:
9b1b2383bb1487e83590d0fef621dccf2031c479481e2c7e568bbea2dc39fe98 - Sigstore transparency entry: 2096080229
- Sigstore integration time:
-
Permalink:
geofflava/runworthy@62561ace0c7230b300990764f71ee7a748acbbb0 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/geofflava
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@62561ace0c7230b300990764f71ee7a748acbbb0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file runworthy-0.2.0-py3-none-any.whl.
File metadata
- Download URL: runworthy-0.2.0-py3-none-any.whl
- Upload date:
- Size: 71.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 |
6ab92b7ebd3410f3d50a613afcb380ab7e63c5331db58b791b18e8726e533815
|
|
| MD5 |
00723a8a996e4d8f7962f49bfe9f483d
|
|
| BLAKE2b-256 |
78c7a2cfb65e586c94971c01ec68144ddebb2177c334f82ea7bb63cc27824bb2
|
Provenance
The following attestation bundles were made for runworthy-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on geofflava/runworthy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
runworthy-0.2.0-py3-none-any.whl -
Subject digest:
6ab92b7ebd3410f3d50a613afcb380ab7e63c5331db58b791b18e8726e533815 - Sigstore transparency entry: 2096080514
- Sigstore integration time:
-
Permalink:
geofflava/runworthy@62561ace0c7230b300990764f71ee7a748acbbb0 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/geofflava
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@62561ace0c7230b300990764f71ee7a748acbbb0 -
Trigger Event:
release
-
Statement type: