Headless Mode
Free · open source

rigscore

Configuration hygiene checker for AI development environments

Shipped

Reads a project’s AI configuration — governance docs, MCP server configs, permissions, skill files — and checks it against what the project’s own rules claim, so drift between “written down” and “actually enforced” gets caught instead of assumed away.

One local command runs 28 checks — 13 scored plus 15 advisory — over MCP configs, skill files, governance docs, secrets, container isolation and infrastructure, and prints two independent 100-point grades, each A–F: Hygiene, the security axis, and Practice — does this team drive its agents well? Most scanners grade the config; rigscore also grades the workflow.

Requires Node >= 18.17. Fully offline: no token, no account, no data leaves the machine unless you pass --online. Release notes: github.com/Back-Road-Creative/rigscore/releases.


Documentation

The full manual, on this page: what it checks, scoring, fixing what it finds, known limits, CI integration, the command reference, the supported-clients matrix and the release-verification recipe.

Scope. rigscore measures configuration hygiene, not runtime security: it reads the files on disk — governance docs, MCP configs, Docker settings, skill files, permissions — and scores what they say. Pair it with a runtime scanner and source-level SAST; see known limits and verify rigscore.

Two scores

One local command runs 28 checks — 13 scored plus 15 advisory — and prints two independent 100-point scores, each A–F: the Hygiene score (the security axis) and Practice. Fully offline: no token, no data leaves the machine.

npx github:Back-Road-Creative/rigscore

Requires Node >= 18.17. --online adds site probes and MCP supply-chain checks.

Sample output

  rigscore v2.2.0

  [X]   MCP server configuration  [mechanical]  CRITICAL "filesystem" mounts /
  [OK]  Cross-config coherence    [keyword]     governance matches config
  [ADV] Semantic tool judge       [pattern]     advisory — run with --semantic

  HYGIENE 78/100 (B)   ·   Practice 71/100 (B)
  Grade: B

  Each row's [mechanical] / [pattern] / [keyword] tag shows how its points
  are earned (see the legend below).

Why rigscore

  • The only cross-config coherence pass. rigscore reads your governance claims and checks them against your actual configuration, catching contradictions single-surface scanners miss — a CLAUDE.md that forbids /etc while the MCP config mounts /, a “no shell” rule sitting next to a shell server with filesystem access. A config that is honestly insecure scores better than one that lies about its posture.
  • A second axis nobody else scores. Alongside Hygiene, every scan prints a Practice score — does this team drive its agents well? — from loop governance, spec goals, workflow maturity, and sandbox posture. Most scanners grade the config, rigscore also grades the workflow.
  • Full power offline, whole surface, one score. Every scored check runs with no token, no account, and no data leaving the machine (--online only adds optional probes) — and instructions, permissions, MCP, skills, hooks, CI, Docker, and env all fold into one A–F grade and an embeddable badge, not a single-tool silo.

Fix what it finds

rigscore . --fix dry-runs every auto-fixable issue — plus the starter packs that target your red checks — and writes nothing. --fix --yes applies the safe, file-level fixes to files you already have; --fix --yes --install-packs also scaffolds guard packs for the major AI clients wherever a check is red. To install a single pack on its own, run rigscore init --<pack>. The full flag ladder lives in the command reference.

What it checks

Scored checks (13 · 100 points)

CheckWeightCategory
Cross-config coherence14governance
MCP server configuration14supply-chain
Governance documentation10governance
Skill file safety10supply-chain
Claude settings safety8governance
Deep source secrets8secrets
Secret exposure8secrets
Credential storage hygiene6secrets
Docker security6isolation
Infrastructure security6process
Permissions hygiene4process
Unicode steganography4governance
Git hooks2process

AI-specific checks (MCP, coherence, skill files, governance docs) hold 48 of the 100 points; the rest cover secrets, container isolation, infrastructure, hygiene.

Advisory checks (15 · zero weight)

Advisory checks carry zero Hygiene weight; several score on the Practice axis instead.

CheckCategoryEnforcement
Agent output schemasgovernancemechanical
AI disclosuregovernancekeyword
CI agent capsprocesspattern
Documentation coverageprocessmechanical
Instruction effectivenessgovernancekeyword
Loop governanceprocesspattern
Memory hygienegovernancemechanical
Network exposureisolationmechanical
Sandbox postureisolationmechanical
Semantic tool-description judgesupply-chainpattern
Site securityisolationmechanical
Skill ↔ governance coherencegovernancekeyword
Spec goalsgovernancekeyword
Windows/WSL securityisolationmechanical
Workflow maturitygovernancekeyword

Scoring

ScoreGradeMeaning
90–100AStrong hygiene posture
75–89BGood foundation, some gaps
60–74CModerate risk, needs attention
40–59DSignificant gaps
0–39FCritical issues, fix immediately
  • CRITICAL zeros out its sub-check; a coherence CRITICAL costs an extra 10 points.
  • WARNING deducts 15 each (1 = 85, 2 = 70, 3 = 55); INFO deducts 2 each, floored at 50 with no WARNINGs.
  • PASS / SKIPPED: no impact.

Coverage scaling: unscannable checks are marked N/A and their weight redistributed; the score scales by min(1, W / 100) for applicable weight W.

The Practice score

Every scan prints a second 100-point axis. Hygiene asks is this rig safe? Practice asks does this team drive its agents well?

Practice checkWeight
Loop governance25
Spec goals20
Workflow maturity20
Sandbox posture15
CI agent caps10
Memory hygiene5
AI disclosure5

Practice checks contribute zero to Hygiene. A repo with no practice surface prints Practice: n/a, not an unfair 0/100; --json reports practiceScore (number or null). The cross-config coherence pass and this second axis are what set rigscore apart — see Why rigscore.

Enforcement-grade labels

Each check’s label shows how its points are earned; it is display-only.

  • [mechanical] — deterministic config inspection, ungameable by wording. 15 of 28.
  • [pattern] — regex/structural scan, evadable only by novel obfuscation. 7 of 28.
  • [keyword] — phrase presence in governance prose, gameable by keyword-stuffing (see known limits). 6 of 28.

Scoring profiles

A profile is a weight map. Five ship built in:

ProfileUse-case
defaultBalanced AI dev environment audit
minimalSmoke test — AI-moat checks only
ciCI pipelines (identical to default today)
homeSingle-user dev boxes — infra/docker/windows off
monorepoMulti-project repos — hints --recursive --depth 3
npx github:Back-Road-Creative/rigscore --profile minimal

Resolution order: --profile, then project .rigscorerc.json, then ~/.rigscorerc.json, then default. Custom weights: promote an advisory check into the scored lane (non-zero) or demote a scored one (0).

Compliance report

--report compliance groups findings by framework control — deterministic text, diffable in CI or for an auditor:

npx github:Back-Road-Creative/rigscore --report compliance .
FrameworkStatusCoverage
OWASP Top 10 for Agentic Applications 2026Finalfull
OWASP MCP Top 10Beta (pilot)partial
NIST AI RMF 1.0Finalfull
EU AI Act — Regulation (EU) 2024/1689In force, phasedfull

The table only claims what it can prove: controls with no supporting check print NOT EVIDENCED, and unmapped checks print UNMAPPED. Muted findings (suppress: / --ignore) are disclosed by count and id. Not with --recursive.

Limitations

rigscore is a configuration presence checker, not an enforcement tool. Read this before relying on the score as a governance quality signal.

  • Semantic reversal bypasses keyword checks. The governance checks (governance docs + coherence, 24 of the 100 scoring points) only confirm your file mentions concepts like “path restrictions”; stuffed headers over a body that dismantles them still pass. See test/keyword-gaming.test.js for the bypass list.
  • Injection detection is pattern-based. Unicode-normalized patterns catch common prompt-injection; encoded payloads, rephrasings, and homoglyphs can evade them.
  • Config-shape pinning only, not runtime tool descriptions. rigscore hashes each MCP server’s configured shape — {command, args, envKeys} — and warns on change (CVE-2025-54136 / MCPoison class), but does not hash the tool descriptions a running server advertises. The mcp-hash / mcp-pin / mcp-verify workflow pins those operator-side.
  • Secret scanning covers named config files in the project root. Use --deep for recursive source, or a dedicated git-history secret scanner for git history.
  • Point-in-time snapshots only. No continuous monitoring, no git-history scan.
  • Score depends on project shape. rigscore ships 28 checks; an npm package legitimately sees most as N/A. rigscore itself is a package, not an agent workspace — no committed MCP config, no .claude/skills/, no per-client rule sets — so only a minority apply. See Dogfooding for the exact self-score.

Dogfooding

rigscore runs on rigscore in CI.

  • Self-score 37/100 (Grade F). The real score: as an npm package only 10 of its 28 apply (weight 46/100), the rest N/A.
  • CI threshold --fail-under 30. The baseline (37) minus a 7-point buffer; the public default is 70.
  • .rigscorerc.json disables four checks (infrastructure-security, skill-coherence, workflow-maturity, agent-output-schemas) needing artifacts rigscore doesn’t ship.

CI integration

GitHub Actions

1- uses: Back-Road-Creative/rigscore@v2.2.0
2  with:
3    fail-under: 70
4    upload-sarif: true

Or run directly:

- run: npx github:Back-Road-Creative/rigscore --ci --fail-under 70

Pin to a released tag (the action rejects @main-style refs); tags through v2.2.0 are available.

SARIF

rigscore emits SARIF v2.1.0: npx github:Back-Road-Creative/rigscore --sarif > results.sarif, uploaded via github/codeql-action/upload-sarif.

Baseline gate

Gate on new findings, not an absolute score: the first run writes the baseline; later runs report only findings not in it and exit 1 if any exist:

npx github:Back-Road-Creative/rigscore --baseline .rigscore-baseline.json

Commit the baseline: the gate reads the HEAD copy, so a deleted or corrupt working-tree baseline fails closed. Refresh with --baseline-refresh.

State file and MCP-shape gate

With a repo-level .mcp.json, a scan writes .rigscore-state.json: a SHA-256 pin of each MCP server’s {command, args, envKeys} shape (env keys only, never values). A changed pin flags a silent swap of a trusted server (the MCPoison class, CVE-2025-54136); an uncommitted pin detects nothing.

npx github:Back-Road-Creative/rigscore --verify-state

--verify-state is the read-only CI gate: it reads the pin at HEAD (a fresh pin can’t satisfy it) and exits 1 on drift, 2 if missing or corrupt, 0 if all match. --no-state-write skips the write on a borrowed checkout, flagging the lost coverage.

Runtime tool pinning

The state file above pins each MCP server’s configured shape. The tool descriptions a running server advertises are pinned separately, on demand — rigscore never launches the server itself (that would hand code execution to the thing you distrust), so it uses a print-and-paste workflow:

1npx -y <mcp-server-package> | rigscore mcp-hash | xargs rigscore mcp-pin <server>   # pin
2npx -y <mcp-server-package> | rigscore mcp-verify <server>                          # verify later

rigscore mcp-hash canonicalizes and hashes a tools/list JSON read from stdin; mcp-pin stores it in .rigscore-state.json; mcp-verify re-compares. Verify exits 0 on a match, 3 when nothing is pinned for that server, and 4 on drift — a tool description that silently changed under a trusted server (the CVE-2025-54136 rug-pull class). Each server also gets a zero-weight INFO during a normal scan showing whether its runtime hash is pinned.

Exit codes

CodeMeaning
0At or above --fail-under (baseline: no new findings)
1Below --fail-under, or new findings vs baseline
2Config error — bad .rigscorerc.json, unknown --profile, or corrupt baseline
3rigscore mcp-verify <server> with no runtime hash pinned
4rigscore mcp-verify <server> drift — the piped tools/list no longer matches the pinned runtime hash

Command reference

Scoring, compliance mapping and CI gates are above; this section covers the working-session commands and packaging.

Auto-fix and starter packs

--fix identifies safe, reversible remediations — and offers the starter packs that target your red checks. Consent is layered: dry run by default, --yes unlocks file-level edits to files you already have, --install-packs additionally allows scaffolding new files:

1npx github:Back-Road-Creative/rigscore --fix                        # dry run: lists auto-fixes AND installable packs, writes nothing
2npx github:Back-Road-Creative/rigscore --fix --yes                  # applies auto-fixes; packs are offered, not installed
3npx github:Back-Road-Creative/rigscore --fix --yes --install-packs  # also installs the packs targeting your red checks

--fix never rewrites governance content, and a pack install never overwrites an existing file — it is reported skipped (exists) and left byte-for-byte alone. To install one specific pack, or overwrite deliberately:

1npx github:Back-Road-Creative/rigscore init --list-packs             # what ships, and which checks each pack targets
2npx github:Back-Road-Creative/rigscore init --<pack> [dir] [--force] # install one pack directly
The packs that ship

Seven guard packs ship in the box. Each turns a specific red check green:

PackTurns greenWhat it installs
guardsClaude settings · git hooksdeny rules, an expiring permissions manifest, and a working pre-commit secret scan
docsgovernance docsone vendor-neutral AGENTS.md agent contract plus a CLAUDE.md that defers to it
containerDocker security · network exposurea devcontainer with an egress-proxy allowlist — the agent reaches only the hosts you list
cursor-guardssandbox posturea narrow Cursor terminal/MCP allowlist with no auto-run wildcard
codex-guardssandbox posturea least-privilege Codex CLI baseline — always-prompt approvals, a workspace-write sandbox, network off
gemini-guardssandbox posturea Gemini CLI baseline where every tool call prompts instead of auto-approving edits or shell
opencode-guardssandbox posturean opencode ask/deny permission block so no shell, edit, or fetch auto-runs without approval
Hardening an existing config

By default a pack install never touches a file you already have — an existing config is reported skipped (exists) and left byte-for-byte alone. To fold a pack’s keys into an existing JSON or YAML config instead of scaffolding a new one, add a merge mode:

1npx github:Back-Road-Creative/rigscore init --<pack> --<mode>   # <mode>: merge (alias: harden)

In merge mode, absent keys are added, arrays are unioned, and existing values, comments, and key order are all preserved — it is idempotent, so re-running changes nothing. Merge is mutually exclusive with --force (which overwrites); when both are supplied, merge wins.

Recursive / monorepo mode

--recursive discovers project subdirectories (any recognizable marker file: package.json, pyproject.toml, Dockerfile, CLAUDE.md, …) and scans each independently, four at a time. The overall score is the average across discovered projects; each project’s own .rigscorerc.json is honored.

1npx github:Back-Road-Creative/rigscore . --recursive
2npx github:Back-Road-Creative/rigscore . -r --depth 2   # two levels, e.g. workspace/_active/svc-foo

Suppressing findings

Mute a finding by its stable ID via the flag or .rigscorerc.json:

1npx github:Back-Road-Creative/rigscore --ignore "env-exposure/env-not-gitignored,skill-files/shell-exec"
1{ "suppress": ["env-exposure/env-not-gitignored"] }
Pattern forms

Each suppress entry is matched one of three ways (all case-insensitive):

  • Exact ID — a full <check-id>/<slug> such as env-exposure/env-not-gitignored; a bare <check-id> such as skill-files mutes every finding from that check.
  • Glob — any entry containing * is a glob against the finding ID: skill-files/* mutes that check’s findings, **/shell-exec matches the slug across every check (* stays within a path segment, ** spans /).
  • Regex — an entry that begins re:/…/ is a regular expression tested against both the finding ID and its title, e.g. re:/inject|exfil/.

A plain word that is neither an exact ID nor a glob falls back to a case-insensitive substring match on the finding title — handy, but broad: token mutes every finding whose title contains “token”. Prefer an ID or a re:/…/ form when you need precision.

Suppression is never silent: the terminal report, JSON (suppressed: { count, ids }), and SARIF all disclose how many findings were muted and which. IDs follow <check-id>/<slug>, are stable within a major version, and a bare <check-id> mutes a whole check. Full catalog: docs/FINDING_IDS.md.

Semantic tool-description judge

Static regex checks catch known prompt-injection phrasings; an attacker who paraphrases the same intent into a natural-sounding MCP tool description slips past them. The opt-in --semantic judge closes that gap:

1npx github:Back-Road-Creative/rigscore --semantic

It runs only when you pass --semantic — a default scan makes zero external calls from it. For each tool description (read from the tools/list snapshot files listed under paths.mcpToolsSnapshot in .rigscorerc.json — the same JSON you pipe into rigscore mcp-hash), it asks your own first-party agent CLI to classify the text benign or suspicious: your claude CLI in print mode by default, or whatever you set in semantic.command (gemini, codex exec, …). No API key, no SDK client. Each description is wrapped in a data-only frame and the judge is told to treat it as data, not instructions, so a poisoned description can’t hijack the judge. If the configured binary isn’t on PATH, the check skips cleanly — no finding, no crash. It scores on the advisory (zero-weight) lane; enforcement grade pattern.

CycloneDX AI-BOM export

--cyclonedx prints a CycloneDX 1.6 BOM of the AI components a scan discovers: every repo-level MCP server as a component (with version + purl when the package is stably pinned), AI client configs and governance files with SHA-256 content digests, and a dependency graph. AI facts (transport, launch command, declared env-var names, config-shape hash) ride on properties rows.

1npx github:Back-Road-Creative/rigscore --cyclonedx . > ai-bom.json

Repo-scoped on purpose — home-dir client configs never land in a shippable BOM. Not combinable with --recursive.

Docker image

Every v*.*.* release tag publishes an image to GHCR. It runs as a non-root user and scans /workspace by default:

1docker run --rm -v "$PWD:/workspace" ghcr.io/back-road-creative/rigscore:<tag>

Explain a finding

rigscore explain <findingId> prints the docs page for the check behind a finding, jumping to the finding-specific section when one exists:

1npx github:Back-Road-Creative/rigscore explain skill-files/indirect-injection

Watch mode

--watch re-runs rigscore when relevant files change — governance files, MCP configs, .env, Docker Compose files, git hooks, .rigscorerc.json — debounced at 500ms:

1npx github:Back-Road-Creative/rigscore --watch

Pre-commit hooks

Two ways to run rigscore before every commit:

  • Built-inrigscore --init-hook writes a git pre-commit hook that runs a scan with --fail-under 70.
  • pre-commit.com framework — rigscore ships a .pre-commit-hooks.yaml, so a repo already using the pre-commit framework adds it as a hook:
1repos:
2  - repo: https://github.com/Back-Road-Creative/rigscore
3    rev: v2.2.0
4    hooks:
5      - id: rigscore

Sharing one config across repos

A .rigscorerc.json can inherit another config with extends, so a team maintains one hardened baseline and each repo layers its own overrides on top:

1{
2  "extends": "../shared/.rigscorerc.json",
3  "suppress": ["env-exposure/env-not-gitignored"]
4}

extends takes a local path or an array of them (later entries win); the file that declares extends outranks the base it inherits, so a repo always overrides the shared defaults. Bases are resolved recursively and cycles are rejected. Values are read from disk only — an http(s):// target is refused, never fetched, so extends never adds egress.

Plugins

rigscore auto-discovers rigscore-check-* packages (scoped @org/rigscore-check-* too) from node_modules and runs them as extra checks — no core changes needed. Plugins default to weight 0 (advisory); give one scoring weight via .rigscorerc.json:

1{ "weights": { "my-custom": 5 } }

An invalid plugin warns to stderr and is skipped — it never crashes the scan.

More flags

FlagWhat it does
--badgeGenerate a markdown score badge for your README
--badge-format <f>Badge format: markdown (default, shields.io image snippet), endpoint (shields.io Endpoint Badge JSON, for a README badge that updates itself), or svg (self-contained, no network)
--check <id>Run a single check; an N/A check reports n/a and exits 0, not a red 0/100
--code-qualityOutput a GitLab Code Quality report (CodeClimate JSON)
--junitOutput JUnit XML — one testcase per check, for Jenkins, Azure Pipelines and GitLab
--quiet / -qSummary-only output: score, grade, posture and finding counts. For pre-commit hooks and terse CI logs
--record-scoreAppend this scan’s score to .rigscore-history.json (opt-in)
--trendPrint the recorded score history and deltas, then exit — read-only; --record-score is what writes it
--cta / --no-ctaShow (--cta) or hide (--no-cta) the promotional call-to-action footer after a terminal scan — off by default; --ci implies --no-cta
--include-home-skillsAlso scan the home-scope skill, command, and prompt directories of every registered client — ~/.claude/skills, ~/.claude/commands, ~/.codex/prompts, ~/.gemini/commands, and the rest — instead of project scope only
--init-hookInstall a pre-commit hook that runs rigscore with --fail-under 70 before each commit
--refresh-mcp-registryForce refetch of the MCP registry cache (implies --online, bypasses the 24h TTL)

Troubleshooting & FAQ

  • Which clients does rigscore scan? See the supported-clients matrix — every governance file, MCP config, sandbox, and skill directory rigscore reads, listed per client.
  • Why is my score capped below Grade A? Without a --deep scan, a Dockerfile, and infrastructure paths, the reachable weight ceiling is 80, so a repo missing those surfaces tops out at Grade B even with every check passing. That is by design — the absent checks are N/A (weight redistributed via min(1, W / 100)), not failing.

For deeper issues — config parse errors, unexpected exit codes, CI setup — see the product TROUBLESHOOTING guide.

Supported clients

rigscore reads a single client registry — one source of truth for every AI coding tool it knows about — and scans, scores, and rug-pull-pins whichever of these surfaces exist in your repo. No flag turns a client on; if the file is there, it is scanned.

Each row lists only the surfaces a client genuinely has: its instruction file (CLAUDE.md, AGENTS.md, GEMINI.md, …); its MCP config (a committed repo config is what a pull request can mutate, so it’s exactly what the CVE-2025-54136 rug-pull pin covers — a ~/ home config is scanned but never pinned); its sandbox boundary; and its skill dirs (slash-command / skill / prompt directories scanned for hijack and exfil, with home dirs gated behind --include-home-skills).

The registry

ClientInstruction fileMCP configSandboxSkill / command dirs
Claude CodeCLAUDE.md.mcp.json (repo), ~/.claude.json.claude/settings.json, .claude/settings.local.json.claude/commands, .claude/skills (repo + home)
Claude Desktop~/.claude/claude_desktop_config.json
Cursor.cursorrules.cursor/mcp.json (repo + home).cursor/permissions.json
Windsurf.windsurfrules~/.windsurf/mcp.json
Cline.clinerules~/.cline/mcp_settings.json
Continue.continuerules~/.continue/config.json
GitHub Copilotcopilot-instructions.md, .github/copilot-instructions.md.vscode/mcp.json (repo)
Codex CLIAGENTS.mdin .codex/config.toml (TOML).codex/config.toml (repo + home)~/.codex/prompts
Aider.aider.conf.yml
Gemini CLIGEMINI.md.gemini/settings.json (repo + home).gemini/settings.json.gemini/commands (repo + home)
opencodeAGENTS.mdopencode.json (repo), ~/.config/opencode/opencode.jsonopencode.json / home config.opencode/commands, ~/.config/opencode/commands
Amp~/.amp/mcp.json
Zed~/.config/zed/settings.json
Amazon Q Developer.amazonq/rules/ (dir).amazonq/mcp.json, .amazonq/default.json (repo + ~/.aws/amazonq)
Roo Code.roorules.roo/mcp.json (repo)
Cody.vscode/settings.json (cody.mcpServers)
JetBrains Junie.junie/guidelines.md.junie/mcp/mcp.json (repo + home)
Goose.goosehintsin ~/.config/goose/config.yaml (YAML)
Warp.warp/.mcp.json (repo + home)
Kiro.kiro/steering/ (dir).kiro/settings/mcp.json (repo + home)
Qwen CodeQWEN.md.qwen/settings.json (repo + home)
CrushCRUSH.md.crush.json / crush.json (repo), ~/.config/crush/crush.json
OpenClaw~/.openclaw/openclaw.json (mcp.servers)
AntigravityAGENTS.md~/.gemini/antigravity/mcp_config.json

A dash means the client has no such surface, or none at a stable path rigscore can resolve. Codex and Goose keep their MCP servers in TOML and YAML respectively, so the JSON MCP reader doesn’t parse them — their servers surface through the sandbox and credential readers instead.

Directory-form rule sets

Modern clients read a directory of rule files rather than one file. rigscore scans these by default, so a repo governed only by, say, .cursor/rules/*.mdc still counts as governed:

DirectoryRead byFiles
.cursor/rules/Cursor*.mdc
.windsurf/rules/Windsurfany non-dotfile
.clinerules/Clineany non-dotfile
.github/instructions/GitHub Copilot*.instructions.md
.amazonq/rules/Amazon Q*.md
.kiro/steering/Kiro*.md

Example configs

Starter configurations for the common clients live in the product repo under docs/examples/ — ready-to-copy baselines for claude, cursor, cline, continue, copilot, windsurf, aider, and a vendor-neutral agents-md. For opinionated, check-targeting baselines you can install in place, use the guard packs (rigscore init --list-packs).

Verify rigscore

Below is the full verification recipe for the current release, v2.2.0. Substitute any signed tag.

The canonical reference for these commands lives in the rigscore README — see the Verify this tool section. This section mirrors it for readers who land here first.

1. Why verify

rigscore inspects your AI agent’s configuration for supply-chain risk. If the tool itself has supply-chain risk, you’re auditing one black box with another. Every signed release ships with:

  • A Sigstore-backed build-provenance attestation generated by GitHub Actions, tying the artifact to a specific commit and workflow on this repo.
  • A CycloneDX SBOM listing every dependency, runtime and transitive.
  • A public bypass-test catalog documenting cases rigscore intentionally does NOT catch.

2. Tag signature

1git verify-tag v2.2.0

Tag signing is opt-in for rigscore maintainers. The authoritative signal is the build attestation in step 3: generated by GitHub Actions on a clean runner, it binds the artifact to a specific workflow run rather than to whoever holds a signing key.

3. Provenance attestation (the canonical check)

Two commands:

1gh release download v2.2.0 --repo Back-Road-Creative/rigscore --pattern '*.tgz'
2gh attestation verify rigscore-2.2.0.tgz --owner Back-Road-Creative

One-liner equivalent (downloads, verifies, cleans up):

1scripts/verify-release.sh v2.2.0

A green result means: this exact tarball was built by GitHub Actions on Back-Road-Creative/rigscore, at the commit pointed to by v2.2.0, by the workflow .github/workflows/release-provenance.yml, and signed via Sigstore keyless OIDC against the GitHub Actions identity.

What this rules out:

  • A maintainer cannot silently publish a tarball built on a laptop.
  • A release asset cannot be swapped post-hoc — the hash in the attestation is bound to a Sigstore-signed certificate.
  • A release cannot be backdated. The Rekor entry is timestamped at signing.

4. SBOM review

Every release bundles a CycloneDX 1.6 SBOM:

1gh release download v2.2.0 --repo Back-Road-Creative/rigscore --pattern 'sbom.cdx.json'
2cat sbom.cdx.json | jq '.components[].name' | sort -u

rigscore has two runtime dependencies, full stop:

  • chalk — terminal coloring
  • yaml — YAML parsing for governance files

The SBOM lists ~50 components total because it includes the dev/test transitive graph (vitest, esbuild, chai). Everything not listed under dependencies in package.json is dev-only — present at build time, absent at run time. To see the runtime-only set:

1node -e "const p=require('./package.json');console.log(Object.keys(p.dependencies))"

A two-dependency runtime surface is intentional: the attack surface for a rigscore install is the smaller of GitHub itself and those two well-known packages.

5. Reproducible bundle hash

Rebuild the tarball locally and compare bytes:

1git clone https://github.com/Back-Road-Creative/rigscore.git
2cd rigscore && git checkout v2.2.0
3nvm use 20      # match the Node version the release workflow used
4npm ci          # package-lock.json is the anchor
5npm pack
6sha256sum rigscore-2.2.0.tgz

Ask the release what the published digest is, rather than trusting a hash typed on this page:

1gh release view v2.2.0 --repo Back-Road-Creative/rigscore \
2  --json assets --jq '.assets[] | select(.name | endswith(".tgz")) | "\(.digest | sub("^sha256:";""))  \(.name)"'

GitHub records the digest on the asset itself, and that --jq prints it in exactly sha256sum format — so the two outputs compare line for line, and there is no number here to go stale. Or download the asset and hash it yourself:

1gh release download v2.2.0 --repo Back-Road-Creative/rigscore --pattern '*.tgz'
2sha256sum rigscore-2.2.0.tgz

Caveats. npm pack is reproducible across the same Node minor version when package-lock.json is committed (it is); cross-minor or different npm versions can shift tar headers. If your local hash differs but the step-3 attestation verifies, trust the attestation — the hash recipe is a sanity check, not the primary signal. Note also that the published digest and the tarball both come from GitHub, so this step establishes reproducibility, not independence; step 3 is what binds the artifact to a build.

6. Public bypass tests

We publish the cases rigscore intentionally does NOT catch:

They lock the current behavior — a future PR that closes one of these gaps flips the corresponding characterization test into a positive assertion. Do not assume rigscore catches anything that is not asserted in the test suite.

7. Threat model

The full attack-surface and out-of-scope catalog lives in the rigscore repo:

  • THREAT-MODEL.md — what rigscore inspects, what it doesn’t, and the trust boundaries.
  • docs/known-limits.md — concrete examples of attacks rigscore will not detect, with pointers to tools that will.

If you need a check rigscore does not implement, file an issue with a fixture.

Source and releases

← All software