coderifts
v8.6.11
Published
Signed, offline-verifiable authorization for AI-agent contract changes. Only a granted change can proceed.
Maintainers
Readme
CodeRifts CLI
Signed, offline-verifiable authorization for AI-agent contract changes. Only a granted change can proceed. Works locally or with the CodeRifts cloud API.
Installation
npm install -g coderiftsOr run without installing:
npx coderifts diff old-api.yaml new-api.yamlQuick Start
# Compare two specs locally
coderifts diff old-api.yaml new-api.yaml
# Use cloud API for full governance report
coderifts login
coderifts diff old-api.yaml new-api.yaml --cloud
# CI mode — exit 1 if risk score exceeds threshold
coderifts diff old-api.yaml new-api.yaml --ci --threshold 50Commands
coderifts diff <old-spec> <new-spec>
Compare two OpenAPI specs and report breaking changes.
| Flag | Description | Default |
|------|-------------|---------|
| -f, --format <format> | Output format: terminal, json, markdown | terminal |
| --ci | CI mode — exit code 1 if breaking changes exceed threshold | false |
| --threshold <number> | Risk score threshold for CI mode (0-100) | 50 |
| --cloud | Use CodeRifts cloud API instead of local analysis | false |
| -c, --config <path> | Path to .coderifts.yml config file | auto-detect |
Output formats:
- terminal — Colored tables and risk score box (default)
- json — Full structured report for programmatic use
- markdown — Markdown table for CI comments
A coderifts-report.json file is always saved to the current directory.
coderifts init
Interactive configuration generator. Creates a .coderifts.yml file with industry-specific presets:
startup-leangrowth-balancedfintech-strictpublic-api-safemicroservices-internalai-agent-platform
coderifts init --agents
One command that wires this repository for governed agent work. Re-run is
a no-op. Existing JSON is merged (never clobbered); a .bak sits next to any
file it modifies.
coderifts init --agents # all hosts, all four pieces
coderifts init --agents --dry-run # plan only — writes nothing
coderifts init --agents --hosts=claude # Claude Code only
coderifts init --agents --hosts=cursor # Cursor only
coderifts init --agents --hosts=copilot # GitHub Copilot / VS Code only
coderifts init --agents --no-hook # skip host hook install
coderifts init --agents --no-workflow # skip .github/workflows/coderifts.yml
coderifts init --agents --strict # STRICT: require-verified-monitoring + require-grant (cr.exec.v2 primary)
coderifts init --strict --atomic-v2 # ATOMIC_V2 config + fail-closed wiring (implies --agents --strict)
coderifts verify atomic-v2 # 0 TARGET_ENFORCEMENT_VERIFIED; 2 WIRING_REQUIRED; 3 VERIFICATION_FAILED; 1 usage
coderifts keys create --email … --name … --use-case "Local development" [--save]
coderifts bind --repo owner/repo [--commit] [--prove]
coderifts claim --installation-id <id>
coderifts init --agents --check # which of the four pieces are present| Piece | What it writes | Host-specific shape |
|-------|----------------|---------------------|
| MCP config | .mcp.json (Claude Code), .cursor/mcp.json (Cursor), .vscode/mcp.json (Copilot / VS Code) | Three shapes, never normalised into one: Claude mcpServers + type: "http" + url; Cursor mcpServers + url only (no type); Copilot servers (not mcpServers) + type: "http" + url. Never httpUrl (that is Gemini, extension-level — this command does not write it). |
| Agent rules | generated host files via agent-setup's writer | AGENTS.md is always written. Claude also gets CLAUDE.md; Cursor also gets .cursor/rules/coderifts.mdc; Copilot also gets .github/copilot-instructions.md. --hosts=all adds LangGraph and OpenAI instructions. (coderifts init agent is a different command — it writes a .coderifts.yml template.) |
| Host hook | .claude/settings.json, .cursor/hooks.json | Reuses hook install --claude / --cursor (JSON-merge, backup, idempotent). Copilot shares Claude's .claude/settings.json — VS Code reads it by default. |
| CI workflow | .github/workflows/coderifts.yml | coderifts/contract-gate@v0. Default: require-verified-monitoring commented (honest default: false). --strict: the same file with require-verified-monitoring: 'true' and require-grant: 'true' (teaches cr.exec.v2) |
Grant versions (these names are not interchangeable; field-by-field matrix: Grant versions):
| What | Who uses it | What it proves |
|------|-------------|----------------|
| cr.exec.v2 | coderifts init --agents --strict (require-grant); Guard/SDK when grantVersion: 'v2' | Canonical production execution grant. Not the same bytes as v1. Mint with grantVersion: 'v2'. |
| cr.exec.v1 | capability-demo; legacy/migration | Older reference grant bound to operation ∥ target_id ∥ after_payload. Not the installer default. The gate still accepts it so a migration is not a hard cut. |
| ENFORCING_STRICT | Guard withCodeRifts({ profile }) | Permanent alias of ENFORCING_STRICT_V1 (must resolve to _V1 forever). Installer-reachable production lock. Not a grant format. Different axis from ENFORCING_ATOMIC_V1/V2 — those require customer-held executor wiring this installer does not emit. |
--strict turns on require-grant and require-verified-monitoring in the workflow; it teaches cr.exec.v2 as the grant to mint. It does not emit ENFORCING_ATOMIC_V2.
--atomic-v2 writes .coderifts/atomic-v2.json plus fail-closed unconfiguredCapability wiring placeholders. Init may exit 0 with PROFILE_CONFIGURED / WIRING_REQUIRED (state: ATOMIC_V2_CONFIGURED, atomic_v2_verified: false). That is configuration, not target enforcement. coderifts verify atomic-v2 is the verifier and must not exit 0 while placeholders remain:
| Exit | Meaning |
|------|---------|
| 0 | TARGET_ENFORCEMENT_VERIFIED — customer target fully verified |
| 2 | WIRING_REQUIRED — config installed, placeholders still throw |
| 3 | VERIFICATION_FAILED — wiring present but verification failed (including a document that claimed verified:true with incomplete wiring) |
| 1 | usage / missing config |
Never emit state: ENFORCING_ATOMIC_V2 with atomic_v2_verified: true while any of the adapter-contract (atomic-v2-adapter/1) operations is missing: state_challenge, consume, conditional_write, mutate, executor_attestation, provider_readback. Discovery is a machine probe, not a customer boolean. Exit 0 is target-bound (--target --environment --executor-identity --operation --adapter-id); a reference-executor success is not inherited. customer_target_verified:false and production_ready:true cannot be constructed together. The names are PROFILE_CONFIGURED / TARGET_ENFORCEMENT_VERIFIED, not STRICT-vs-ATOMIC (that pairing would read as a weaker-but-acceptable end state).
Next manual step: add CODERIFTS_API_KEY to the repository secrets. The
CLI never writes a key anywhere.
This command does not: enable branch protection (see
ENFORCEMENT.md),
make raw host tools inescapable, or enforce the workflow until
CodeRifts / contract-gate is a required check in repo settings.
The three separate commands this replaces for a fresh repo: coderifts
agent-setup, coderifts hook install --claude (and --cursor), plus a
hand-copied workflow file. Repo-level MCP config was generated by nothing
before 4.6.0.
coderifts login
Save your CodeRifts API key for cloud features. Get a free key at app.coderifts.com/api/signup.
coderifts publish-gate
Gate npm publish on the same contract-preflight family as the pre-push hook:
before = last published / merge-base baseline, after = working tree. Exit 0
only when execution_action permits publish; BLOCK, resolver errors, empty
before, and preflight unreachability all exit 1 (fail-closed). Empty before
is never treated as a silent NEW_ARTIFACT pass.
| Flag | Description | Default |
|------|-------------|---------|
| --spec <path> | Contract artifact path | git config coderifts.specPath or api/openapi.yaml |
| --json | Machine-readable result | false |
Recommended package.json wiring:
{
"scripts": {
"prepublishOnly": "coderifts publish-gate"
}
}Before resolution order (fail-closed):
- Git tag of
package.jsonversion (vX.Y.Z, thenX.Y.Z) git merge-base HEAD origin/main(fallbacks:origin/master,main,master)- Exit 1 with a clear error — never invent an empty baseline
On success the command prints a receipt reference when the preflight path issues one.
coderifts deploy-gate
Gate a deploy on the current { environment, artifact } using a signed
preflight receipt. Fail-closed by default (842 / hook pattern). The CD sibling of
publish-gate / the merge-gate App check.
Verification contract split (4.4.1 / guard 9.0.0):
- Verdict:
@coderifts/agent-guarddeployGateTOKEN mode is the verifier of record. The CLI passes{ token, decision_result, registry | pinnedKeyPem }— it never sends an unstampedcurrently_authorizedview. - WHY (842): the CLI still runs the app kernel (
verifyReceipt+evaluateVerifyAuthorization) to corroborateverify_statusand to print envelopeblocking_reasons. That is not a second verdict: one cause (the gatereason), one WHY.
Crypto is not reimplemented in the CLI. No scoring is reimplemented.
--receipt contract: the file MUST carry the signed chain receipt token
(token / chain_receipt / receipt.token / decision_result.receipt.token)
and the decision envelope (decision_result, or the file itself when it
is the envelope). A hand-written { currently_authorized: true } is not a
receipt. currently_authorized as an input field is untrusted — the gate
computes it from verification and logs a warning naming the field.
Default: no token / failed verification / unbound context / missing /
invalid / expired receipt / scope mismatch / deny-class exits 1 (policy
BLOCK). stderr prints WHY (verify_status + computed currently_authorized +
gate reason + renderDecisionWhy when a body exists) and
set CODERIFTS_DEPLOY_ADVISORY=1 to soften.
Advisory opt-out: CODERIFTS_DEPLOY_ADVISORY=1|true (also
CODERIFTS_ADVISORY=1|true) prints the verdict, always prints
ADVISORY MODE — this gate did not block, and exits 0. A forged /
unsigned file still prints the unsigned-receipt truth loudly.
--enforce / CODERIFTS_DEPLOY_ENFORCE=true: accepted silently (no-op
for exit — default is already fail-closed). This is a host claim that the
step is ENFORCING. It does not set inescapable_deploy. That field is
true only from queried pipeline protection (coderifts enforce --check).
CODERIFTS_DEPLOY_NO_BYPASS is recorded as a host claim and does not clear
bypass_possible.
Infra vs policy: missing --env / --artifact, malformed receipt JSON, or
an unreachable --keys registry prints distinct INFRA wording plus
retryable: true and this is not a policy BLOCK. Still fail-closed by
default; advisory softens it too. A missing receipt file is a policy fail
(no_receipt), not infra. A missing token on a present file is policy
(unsigned_receipt).
Keys: --keys <file|url> (default
https://app.coderifts.com/.well-known/coderifts-keys.json, ID131). Air-gapped
CI: --pinned-key <pem> (skips the registry fetch).
Happy path (signature verifies, envelope binds, currently_authorized computed true for this env+artifact+deploy) exits 0.
| Flag | Description | Default |
|------|-------------|---------|
| --env <environment> | Target environment (e.g. production, staging) | required |
| --artifact <artifact_id> | Immutable artifact identity (content digest or commit SHA) | required |
| --receipt <file> | Signed chain_receipt token + decision envelope JSON | none |
| --fingerprint <fp> | Optional intended fingerprint the receipt must match | none |
| --keys <file-or-url> | Ed25519 key registry (file or URL) | live /.well-known/coderifts-keys.json |
| --pinned-key <file> | Air-gapped PEM public key (skips registry fetch) | none |
| --json | Binding result as JSON | false |
| --enforce | Accepted silently (exit already fail-closed). Still attests ENFORCING. | false |
coderifts deploy-gate --env production --artifact "$SHA" --receipt receipt.json
coderifts deploy-gate --env production --artifact "$SHA" --receipt receipt.json --keys ./coderifts-keys.json
coderifts deploy-gate --env production --artifact "$SHA" --receipt receipt.json --pinned-key ./coderifts-current.pem
CODERIFTS_DEPLOY_ADVISORY=1 coderifts deploy-gate --env production --artifact "$SHA" --receipt receipt.jsoncoderifts outcome <kind>
Report a post-hoc observed outcome for a past decision_id to
POST /api/v1/outcomes. Records the caller's assertion (your deploy job
knows success/failure) — the command does not itself verify the deploy.
kind is a closed set: deploy_succeeded, deploy_failed, rolled_back,
consumer_break_reported, remediation_verified_working,
false_positive_reported, other_reported.
Requires a cloud API key (coderifts login or CODERIFTS_API_KEY). Reporter
is derived from the key. Exit 0 on success, 1 on usage / API error.
| Flag | Description | Default |
|------|-------------|---------|
| --decision <decision_id> | decision_result.decision_id (required) | none |
| --observed-at <iso> | When the outcome was observed (ISO-8601) | now |
| --details <json> | Optional JSON object/array bound to the row | none |
| --json | Print the API response JSON | false |
coderifts outcome deploy_succeeded --decision "$DECISION_ID"
coderifts outcome deploy_failed --decision "$DECISION_ID" --jsoncoderifts registry-gate [dir]
Local registry admission gate for a directory of OpenAPI/Swagger specs (audit 3.4/9). Runs the pure registry-validation core offline — no cloud calls — so a registry CI step does not depend on CodeRifts uptime to admit specs.
Discovers *.yaml / *.yml / *.json under [dir] (default: .), skips
node_modules and dotdirs, skips non-spec files (no openapi/swagger field),
and fails closed on unreadable files (GATE_ERROR) or zero specs
(REGISTRY_EMPTY).
| Flag | Description | Default |
|------|-------------|---------|
| [dir] | Registry root to scan | . |
| --glob <pattern> | Filter paths with @coderifts/agent-guard matchGlob (relative to dir) | all candidates |
| --errors-only | Fail only on ERROR severity (warnings printed, exit 0) | off |
| --warn-only | Advisory: print findings, always exit 0 | off |
Severity (important): core endpoint_collision is a WARNING and leaves
valid: true. This gate fails on ERROR + WARNING by default — it does not
branch on valid alone. Info findings are printed and do not fail.
GitHub Actions (minimal):
- uses: actions/checkout@v4
- name: Registry admission
run: npx coderifts registry-gate specs/Git hook (coderifts hook install)
Installs a pre-push hook that diffs the configured OpenAPI path against a
resolved baseline (remote tip, or for new branches a merge-base with the
default branch). Empty-before allow is not used. The hook prefers closed-set
execution_action from the diff API (legacy omega_decision only when action
is absent). Configure with git config coderifts.apiKey or CODERIFTS_API_KEY,
and optional git config coderifts.specPath. Installed with no key: the hook
exits 1 (absence is not permission). Explicit opt-out: CODERIFTS_ADVISORY=1
(same flag as claude-hook / deploy-gate). Loud skip: CODERIFTS_SKIP=1.
After upgrading the coderifts CLI, re-run coderifts hook install so the
installed hook matches the package (already-installed hooks are not
auto-updated).
2-minute Claude Code path: coderifts hook install --claude writes a
PreToolUse hook (matcher: Write|Edit|MultiEdit → coderifts claude-hook)
into .claude/settings.json (JSON-merge, backup first, idempotent).
--global writes ~/.claude/settings.json. --log <path> embeds
CODERIFTS_GUARD_EVENT_LOG and CODERIFTS_SESSION_ID in the command.
Unparseable settings JSON → refuse, no write.
Claude Code PreToolUse (coderifts claude-hook) — ID824
Tool-call-time gate for Claude Code: blocks contract-touching Write /
Edit / MultiEdit when authorize preflight returns BLOCK/STOP. The
matcher is tool-level (Write|Edit|MultiEdit); the path family is
enforced inside the hook.
Default governed family (from SUPPORTED_TYPES + @coderifts/contract-path
looksLikeContractPath / CONTRACT_EXT; agent_tools via *tool-schema*.json):
**/*openapi*.{yaml,yml,json}and**/*swagger*.{yaml,yml,json}(openapi)**/*.{graphql,gql}(graphql)**/*.proto(grpc)**/*asyncapi*.{yaml,yml,json}(asyncapi)**/*mcp*.{json,yaml,yml}(mcp_manifest)**/*tool-schema*.json(agent_tools)
node_modules/ and vendor/ are not governed (same as the path detector).
git config --add coderifts.specPath <path-or-glob> is additive (never
replaces the family). git config --add coderifts.specPathExclude <path-or-glob>
opts one path out. A not-governed path exits 0 and prints
coderifts claude-hook: not governed: <path> on stderr — the hook is never silent
on a write/edit.
Exit map (Claude Code semantics — fixed):
| Exit | Meaning |
|------|---------|
| 2 | BLOCK — tool call cancelled; stderr is shown to the model. Default fail-closed: no key, API down, unreadable spec, parse-gap, missing file_path |
| 0 | Allow (CONTINUE), or skip (non-spec path / identical content). CODERIFTS_ADVISORY=1 restores soft-allow on could-not-run sites |
| 1 | Never used for deny — Claude treats exit 1 as non-blocking (action proceeds) |
Push-time equivalent (git exit 1 on BLOCK): coderifts hook install.
Install path: coderifts hook install --claude (or agent-setup writes project .claude/settings.json when absent). Then set key; spec path is optional and additive:
git config coderifts.apiKey 'cr_live_…' # or: coderifts login / CODERIFTS_API_KEY
git config --add coderifts.specPath extra/spec.yaml # additive; does not drop the family
git config --add coderifts.specPathExclude docs/openapi.yaml # opt out one false positiveRecipe — project (.claude/settings.json) or user (~/.claude/settings.json):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "coderifts claude-hook",
"timeout": 60
}
]
}
]
}
}- Project
.claude/settings.json— shared with the repo (commit if the team wants the gate). - User
~/.claude/settings.json— personal; applies to all projects on this machine. - Matcher is case-sensitive tool names/regex. Exit-2 blocking is Claude Code semantics; the same decision discipline at push-time is the git pre-push hook above.
Fail-closed default: missing API key, API unreachable, disk unreadable, or
edit-apply failure on the spec path is exit 2 (enforce_indeterminate) —
absence of a key is not ALLOW. REQUEST_APPROVAL is exit 2
(approval_required). CONTINUE_WITH_MONITORING is exit 2
(monitoring_unwired) unless the host asserts a sink
(CODERIFTS_MONITORING_SINK_WIRED=1 or git config coderifts.monitoringSinkWired true)
— a host claim, not delivery proof.
CODERIFTS_ADVISORY=1|true: explicit opt-out that restores soft-allow on
“governance could not run” sites. Not implied by a missing key.
CODERIFTS_STRICT=1|true: same fail-closed as the default; cannot be
weakened by CODERIFTS_ADVISORY.
CODERIFTS_ADVISORY=1 coderifts claude-hookGitHub Copilot / VS Code agent mode
Measured (VS Code 1.110, Agent hooks Preview, 2026-08-24): VS Code reads
.claude/settings.json by default. coderifts hook install --claude
therefore arms Copilot agent mode — there is no separate --copilot hook
installer. VS Code Agent hooks are deny-capable: PreToolUse honours
permissionDecision allow|deny|ask and exit 2.
Property names (CLI 4.8.0): VS Code passes camelCase (toolName /
toolInput / filePath / targetFile). The hook accepts those alongside
Claude's snake_case (tool_name / tool_input / file_path). Flattened
payloads (path on the root object) are accepted. Garbage input is still
fail-closed.
Matcher limitation (host behaviour, not a CLI bug): VS Code parses hook
matchers (Write|Edit|MultiEdit) but does not apply them. The hook fires
on every tool call. Non-governed tools (Read, Bash, …) and non-contract paths
exit 0 silently with no network and no API key required — cheap
and fail-open for anything that is not a Write/Edit/MultiEdit of the spec. It
is noisy (every tool call invokes the process) and safe (irrelevant tools are
not blocked). A spec-path write without a key is still fail-closed.
Honest boundary: hooks gate the agent, not the human. Inline completions (ghost text) are not tool calls and are not governed. A developer typing in the editor is not a PreToolUse event.
MCP: coderifts init --agents (and --hosts=copilot) writes
.vscode/mcp.json with root key servers — a third shape. Claude Code and
Cursor use mcpServers; Copilot cloud Settings paste also uses mcpServers.
Do not copy one file onto another host.
coderifts status [repo]
Prints three lines derived from this process's actual config, not a static
string: Runtime / Merge / Deploy = PREVENTS or REPORTS.
| Line | PREVENTS when | REPORTS when |
|------|----------------|--------------|
| Runtime | host wrap fail-closed (CODERIFTS_ADVISORY unset) | CODERIFTS_ADVISORY=1\|true |
| Merge | MERGEGATE_ENFORCE=true (exact string; 1 is not enough) | otherwise (the App default) |
| Deploy | deploy-gate fail-closed | CODERIFTS_DEPLOY_ADVISORY=1\|true or CODERIFTS_ADVISORY=1\|true |
REPORTS is REPORTS — this does not claim a wrap or required check is
installed on a repository. With [repo] / --repo owner/repo, also calls
GET /api/v1/enforcement-status and prints the statuses the endpoint measured
(API key required for that half). No GitHub writes. Pair with coderifts
enforce (that command acts; this one only shows). Distinct from
coderifts hook status. Exit 0 on the local three-liner or a successful
cloud read, 1 on invalid repo, missing key (when a repo is given), or API
error.
| Flag | Description | Default |
|------|-------------|---------|
| [repo] / --repo <owner/repo> | Repository (cloud report) | none (local posture only) |
| --json | Machine-readable JSON (local posture, or raw API body when a repo is given) | false |
| --paths | Per-path ENFORCING / ADVISORY / NOT_CONFIGURED from the measured install (GitHub required check, Claude Code hook, MCP, Docker gateway). Offline. | false |
--paths is the J.4 report: it grades what is on disk, not the env-default three-liner. A missing .claude/settings.json is NOT_CONFIGURED even though coderifts status Runtime still prints PREVENTS. GitHub ENFORCING needs an issuer-bound required check (1104); a local workflow alone is ADVISORY. MCP is never a merge gate. Docker gateway is MEASURE and never ENFORCING from a compose file (gateway_enforcing_requires_external_negative_control).
coderifts status
coderifts status --paths
coderifts status --paths --json
coderifts status owner/repo
coderifts status --repo owner/repo --jsoncoderifts drift
J.5. Compare the installer pins (wire-digest sha256:7d6fe4a0…, SKILL sha256, .mcp.json digest, required-check context CodeRifts / contract-gate) to this repository. Live tools/list is fetched; unreachable live with a clean local tree is not a match.
| Exit | Meaning |
|------|---------|
| 0 | No drift |
| 1 | Named drift + next step |
| 2 | Not measurable (live wire unreachable; local pins clean) |
coderifts drift
coderifts drift --json
coderifts drift --out /path/to/repocoderifts enforce [repo]
Close enforcement gaps by chaining existing setup commands: Merge →
setup-required-check; Runtime → hook install (apply only) + agent-guard
guidance; Deploy → deploy-gate CD-step guidance; Content → guidance only.
Dry-run by default (--apply to mutate). Does not bypass underlying
command safety. Does not label Runtime/Content as server ENFORCING. Does not
upgrade UNKNOWN.
Requires a cloud API key for the status read. Merge --apply uses your local
gh credentials, not the CodeRifts API key. Exit 0 when no chained step
failed, 1 on missing repo/key, API error, or any apply failure.
| Flag | Description | Default |
|------|-------------|---------|
| [repo] / --repo <owner/repo> | Repository | none (required) |
| --apply | Run underlying setup commands | dry-run |
| --check | Query GitHub + local workflows (read-only evidence) | false |
| --env <name> | With --check: GitHub Environment to query | none |
| --branch <name> | With --check: branch (default: repo default) | default branch |
| --provider <name> | With --check: provider adapter. github only; others report UNVERIFIABLE and are never stubbed | github |
| --head-sha <sha> | With --check: pull-request head commit, so the required-workflow pin can be compared with the bytes being merged | none |
| --require-workflow-binding | With --check: exit non-zero when the required workflow is not sha-pinned to the head (workflow_binding.grants_pass !== true). Scope: the CLI's own audit verdict on the Action path — it is not the App-path merge clamp, which is decided server-side and is a separate control. Default OFF — without the flag the evidence is byte-identical, the verdict is unchanged and the exit code stays 0 (advisory) | false |
| --json | Machine-readable JSON result | false |
coderifts enforce owner/repo
coderifts enforce --repo owner/repo --apply
coderifts enforce --check --repo owner/repo --env production --jsoncoderifts enforce --check
Provider-native pipeline-protection evidence. This is not a host claim:
--enforce and CODERIFTS_DEPLOY_NO_BYPASS do not set
inescapable_deploy. The field is true only when every layer below is
VERIFIED and MERGEGATE_ENFORCE is observably true. A VERIFIED
required-check binding whose reason names the unobservable clamp is not an
inescapable deploy.
| Layer | VERIFIED when |
|-------|----------------|
| required_check | Default-branch protection required_status_checks.checks[] lists CodeRifts / contract-gate bound to the CodeRifts GitHub App app_id (public metadata: GET https://api.github.com/apps/coderifts). Name-only checks[] (app_id null) is no_issuer_binding. The legacy contexts array has no issuer field → legacy_contexts_no_issuer_binding. Both are NOT_VERIFIED (ID621). |
| enforce_admins | enforce_admins.enabled is true (admins cannot bypass). false = NOT_VERIFIED (admin bypass open) |
| strict_status_checks | required_status_checks.strict is true |
| environment_protection | --env names a GitHub Environment with protection rules and an active deploy-gate job sets that same environment: (mismatch → environment_mismatch) |
| workflow_contract_gate | an active job (not if: false) has a step whose uses: is coderifts/contract-gate at a pinned major. Comments, string literals, and commented-out steps are NOT_VERIFIED |
| workflow_deploy_gate | an active job step invokes coderifts deploy-gate (not echo of the string; not a comment) |
Invariant: any layer whose evidence cannot be bound to an issuer (our GitHub App id) or to an active job is NOT_VERIFIED or UNVERIFIABLE — never VERIFIED. inescapable_deploy remains true only when every layer is VERIFIED and MERGEGATE_ENFORCE is observably true.
Statuses: VERIFIED / NOT_VERIFIED / UNVERIFIABLE(reason). Auth:
GITHUB_TOKEN (never printed). No token → API layers UNVERIFIABLE with
the instruction to set GITHUB_TOKEN (repo + administration:read). The
CodeRifts GitHub App cannot query branch protection (permissions are
contents / pull requests / checks — not administration:read).
--json is the quotable evidence document (spec:
provider-enforcement-evidence.v2 — VERIFIED now requires issuer binding and
an active YAML-AST job; v1 treated name-only checks and text search as
VERIFIED): each layer's status, reason_code, the API endpoint consulted,
queried_at. Exit 0 when a report was produced (evidence, not a gate).
GitLab/Bitbucket: no adapter — UNVERIFIABLE, never stubbed.
coderifts provider-canary [repo]
Negative canary: open an ephemeral PR with the contract-gate check failing or absent and ask GitHub to merge it. Blocked is the healthy result. On a repository that does not block, the canary leaves a merge+revert commit pair on base (that is the finding, stated) — an admin token's revert bypasses required checks.
| Flag | Description | Default |
|------|-------------|---------|
| [repo] / --repo <owner/repo> | Repository | none (required) |
| --confirm | Required for a real run: this creates a branch and a pull request | none — without it (and without --dry-run) the command exits 2 rather than touching the repo |
| --dry-run | Do not touch the provider; report the shape only | false |
| --json | Machine-readable evidence document | false |
Exit 0 only when the provider refused the merge. A merge that succeeds
is the finding: blocked:false, status NOT_VERIFIED, exit 1.
coderifts provider-canary --repo owner/repo --dry-run
coderifts provider-canary --repo owner/repo --confirm
coderifts provider-canary --repo owner/repo --confirm --jsoncoderifts grant publish
Encodes an already-signed execution grant plus the PR head sha into a
comment marker on stdout, for the 1094 PR-comment delivery slot. It does not
mint a grant (src/commands/grant-publish.js:4) — the token must already be a
signed cr.exec.v1 or cr.exec.v2 (see Grant versions above).
| Flag | Description | Default |
|------|-------------|---------|
| --head-sha <sha> | Required. 40-hex PR head sha the grant binds | none |
| --grant <token> | Required. cr.exec.v1 or cr.exec.v2 token | none |
| --after-payload-hash <hash> | sha256:… of the after-payload | empty string |
Writes <!-- coderifts-grant-v2 --> followed by one JSON line. Pipe it to
gh api or post it as a PR comment. Bad --head-sha or a missing --grant
exits 2 with usage.
coderifts grant publish --head-sha "$HEAD" --grant "$TOKEN" > /tmp/marker.txtReaching the gate today: the contract-gate Action reads a grant from its
execution-grant: input, not from PR comments — main() in the gate does not
pass grantComments, so the comment path is not yet wired end to end. Until it
is, deliver the token through the CODERIFTS_EXECUTION_GRANT repository
variable, and note that a grant binds one after-payload, so a pinned variable
covers exactly one diff.
Agent host files (coderifts agent-setup)
Writes the six CodeRifts agent-host rule files plus the Claude Code
PreToolUse hook settings into the current repo (or --out <dir>):
| File | Host |
|------|------|
| AGENTS.md | multi-agent / open convention |
| CLAUDE.md | Claude Code |
| .cursor/rules/coderifts.mdc | Cursor |
| .github/copilot-instructions.md | GitHub Copilot |
| coderifts-langgraph-policy.js | LangGraph |
| openai-agent-instructions.md | OpenAI Agents SDK |
| .claude/settings.json | Claude Code PreToolUse → coderifts claude-hook (fail-closed) |
The generated .claude/settings.json matches the recipe above (matcher
Write|Edit|MultiEdit, command coderifts claude-hook, timeout 60).
Default is fail-closed (missing key / API down → exit 2). Explicit
opt-out: CODERIFTS_ADVISORY=1|true. CODERIFTS_STRICT=1 cannot be
weakened by ADVISORY. Existing files are skipped unless --force.
--force replaces the entire .claude/settings.json (no merge into
permissions / other hooks). If the file already exists, add the PreToolUse
recipe by hand or accept a full replace.
coderifts agent-setup # write (skip existing files)
coderifts agent-setup --force # overwrite
coderifts agent-setup --check # drift vs embedded content
coderifts agent-setup --out ./docs # custom rootContent is vendored from the app generator (scripts/generate-agent-host-files.js);
re-sync with node scripts/sync-cli-agent-host-embed.js when the rule source changes.
Policy template for agent/MCP APIs: coderifts init ai-agent (aliases: agent, mcp).
Copilot MCP configs (coderifts copilot-setup)
Writes GitHub Copilot MCP configs from the canonical tool names. Root keys
differ: VS Code uses servers; Copilot cloud Settings paste uses mcpServers.
coderifts copilot-setup # write (skip existing files)
coderifts copilot-setup --force # overwrite
coderifts copilot-setup --check # drift vs embedded content (exit 1 on drift)
coderifts copilot-setup --out ./docs # custom root| Flag | Description | Default |
|------|-------------|---------|
| --out <dir> | Target directory | cwd |
| --check | Exit 0 if on-disk files match embedded content; exit 1 on drift | false |
| --force | Overwrite existing files | skip collisions |
Writes: .vscode/mcp.json, copilot-cloud-agent-mcp.json,
copilot-custom-agent-mcp.frontmatter.md, docs/copilot-mcp.md. Unknown flags
exit 1 (never silently ignored).
Required check wizard (coderifts setup-required-check)
Guided setup so CodeRifts / contract-gate is a required status check on
your default branch (audit 3.4/1 + 3.5; registry admission is 3.4/9
via coderifts registry-gate). The wizard:
- Resolves
owner/repofromgit remote origin(or--repo) - Observes classic branch protection via
gh api(your login — never a CodeRifts API key) - Detects repository rulesets that already require the check (honest
report; classic GET
/protectiondoes not include rulesets) - Default dry-run: prints the exact
gh api --method PUT … --input -read-modify-write command that adds the check without clobbering existing protection settings --apply: runs that PUT with your credentials, then re-reads and only reports success if the check is required--bind-workflow: create/update a repository rulesetworkflowsrule pinning.github/workflows/coderifts.yml(the STRICT template filename) to its current blob sha on the target branch (enforcement: active,bypass_actors: []). Dry-run unless combined with--apply. Read-back usesgithub-workflow-pin.js; reportsAPPLIEDonly onVERIFIED.
Why the App never writes protection: mutating branch protection needs
administration:write — a trust jump we refuse. Your credentials do the write.
Merge queues: when this check is required, the GitHub App also handles the
merge_group webhook (checks_requested) and posts CodeRifts / contract-gate
on the merge group head (same gate as PR heads), so the queue does not stall.
| Flag | Description | Default |
|------|-------------|---------|
| --repo <owner/repo> | Override owner/repo | git remote origin |
| --branch <name> | Branch to protect | repo default branch |
| --apply | Apply the protection change | print the gh command only |
| --bind-workflow | Create/update a ruleset workflows rule pinning .github/workflows/coderifts.yml (STRICT template) by blob sha | false |
| --enforce-admins | Set enforce_admins:true on create | false — admins can bypass; the gate reports admin_bypass_open |
| --json | Machine-readable JSON result | false |
# Observe + print command (safe default)
coderifts setup-required-check
# Apply + re-verify
coderifts setup-required-check --apply
# Pin the STRICT workflow by blob sha (ruleset `workflows` rule)
coderifts setup-required-check --bind-workflow --apply
# Admins cannot bypass either (opt-in; NOT the default)
coderifts setup-required-check --apply --enforce-admins
# One-liner for CI docs
coderifts setup-required-check --branch maincoderifts lock [repo]
Fetch the observed agent-contract lockfile (coderifts.lock v1) from
GET /api/v1/lock, or --check MCP drift of a locked manifest against a
live one (scoreMcpRisk). --check is monitor only — it does not block.
Unreachable live → status unreachable (not drift). Lock v1 records
agents/ops, not MCP tool bodies; pass --locked-manifest (or additive
mcp_manifests on the lockfile) for --check.
Requires a cloud API key for write mode.
| Flag | Description | Default |
|------|-------------|---------|
| [repo] / --repo <owner/repo> | Repository | none (write mode) |
| --out <path> | Output path | coderifts.lock |
| --json | stdout-only JSON (write: lock doc; check: drift report) | false |
| --check | Compare locked vs live MCP manifest | false |
| --locked-manifest <path> | Baseline MCP manifest JSON (tools[]) for --check | none |
| --live-manifest <path> | Live MCP manifest JSON for --check | none |
| --live-url <url> | Fetch live MCP manifest JSON from URL for --check | none |
| --lockfile <path> | Read coderifts.lock for additive mcp_manifests | none |
coderifts lock owner/repo
coderifts lock --check --locked-manifest mcp.json --live-manifest live.json--check exit codes (from lock.js): 0 unchanged; 3 drift; 2
unreachable / missing locked or live (not clean); 1 usage/error.
coderifts adopt
Counterfactual report: what CodeRifts would have done on contract-artifact
changes in local git history. Extracts spec-only paths (not source files
whose names happen to contain “openapi”), then runs
buildCounterfactualReport. Step B (GitHub API + outcome correlation) is not
this command. Not a git repository → fail-closed (GIT_ERROR, exit 1).
Success → exit 0.
| Flag | Description | Default |
|------|-------------|---------|
| --days <n> | Look back N days (git log --since) | last 50 commits if neither flag set |
| --commits <n> | Last N commits | last 50 commits if neither flag set |
| --json | Machine-readable JSON report (analyzer logs silenced) | false |
coderifts adopt
coderifts adopt --days 30
coderifts adopt --commits 20 --jsonCI/CD Integration
GitHub Actions
- name: Check API breaking changes
run: npx coderifts diff old-api.yaml new-api.yaml --ci --format jsonGitLab CI
api-check:
script:
- npx coderifts diff old-api.yaml new-api.yaml --ci --threshold 40Jenkins
sh 'npx coderifts diff old-api.yaml new-api.yaml --ci'Reproduce our accuracy corpus
We publish the accuracy claims as a runnable proof matrix. Reproduce it yourself with one command — no repo, no API key, no cloud:
npx coderifts corpus verifyThis evaluates every trust vector (false-positive / false-negative cases, including MCP
prompt-injection "poison" vectors) through the real engine and prints a per-vector
PASS/FAIL table plus precision/recall. It exits 0 only when every evaluated vector
passes (CI-friendly). Add --json for machine-readable output.
Two honest modes:
MCP vectors (13) run on bundled pure-JS engines — always verified offline.
OpenAPI vectors (9) need the
oasdiffengine. The command auto-detects it: if present, all 22/22 are verified with the exact engine semantics our service uses; if absent, the 9 OpenAPI vectors are skipped (not failed) with a "13 of 22 verified" note. We do not substitute a weaker JS diff engine, because it would report different verdicts. To verify all 22 offline:brew install oasdiff # macOS go install github.com/tufin/oasdiff@latest # any platform with Go npx coderifts corpus verify
Environment Variables
| Variable | Description |
|----------|-------------|
| CODERIFTS_API_KEY | API key (alternative to coderifts login) |
| CODERIFTS_OASDIFF_BIN | Explicit path to the oasdiff binary (overrides auto-detect) |
| CODERIFTS_STRICT | claude-hook: 1 or true pins fail-closed (cannot be weakened by CODERIFTS_ADVISORY). Default is already fail-closed. |
| CODERIFTS_ADVISORY | claude-hook: 1 or true is the explicit soft-allow opt-out on “governance could not run” sites. Absence of a key is not this flag. Also accepted by deploy-gate as the same opt-out as CODERIFTS_DEPLOY_ADVISORY. Pre-push: 1 or true allows a push when no API key is configured (explicit opt-out; default is exit 1). |
| CODERIFTS_SKIP | Pre-push: 1 is the loud escape hatch (CodeRifts: CODERIFTS_SKIP=1 set — push allowed (explicit override). on stderr, exit 0). Not the missing-key default. Same class as git push --no-verify. |
| CODERIFTS_MONITORING_SINK_WIRED | claude-hook: 1 or true host-asserts a monitoring sink so CONTINUE_WITH_MONITORING may exit 0. Not delivery proof. |
| CODERIFTS_DEPLOY_ADVISORY | deploy-gate: 1 or true is the explicit opt-out — print the verdict, print ADVISORY MODE — this gate did not block, exit 0. |
| CODERIFTS_DEPLOY_ENFORCE | deploy-gate: true attests ENFORCING (same as --enforce; no-op for exit — default is already fail-closed); unknown is explicitly unobservable (never assume ENFORCING) |
| NO_COLOR | Disable colored output |
Exit Codes
| Code | Meaning |
|------|---------|
| 0 | Success: no breaking changes (or below threshold); deploy-gate allow, or advisory opt-out; lock --check unchanged; adopt / status / outcome success |
| 1 | Breaking changes (CI mode), analysis/usage/API failure, or deploy-gate policy/infra fail (fail-closed default) |
| 2 | claude-hook BLOCK (Claude Code cancel); lock --check unreachable / missing locked or live |
| 3 | lock --check drift detected (monitor only — does not block the package) |
status --paths --live --doctor
The four installation probes have their own three-code contract, and the third code is the one that matters:
| Code | Meaning | What a CI job should do |
|------|---------|-------------------------|
| 0 | Every probe ran and found nothing to fix | proceed |
| 1 | A probe ran and found something — each finding carries a next_step | fail the job; the report names the command |
| 2 | A probe could not run — nothing was measured on that axis | fail the job, but do not report a misconfiguration |
⚠ 2 is not a worse 1. They mean opposite things: 1 says we looked and it is wrong,
2 says we did not look. Collapsing them lets an outage read as a clean bill of health on the
one axis a CI job branches on. This mirrors status --paths --live, which already exits 2
rather than degrading to the offline report.
⚠ --doctor is opt-in precisely because it changes the exit code. status --paths --live
has an exit contract that existing CI jobs branch on; turning a finding into exit 1 for everyone
on upgrade would break them.
Finding codes
Every finding carries a next_step that is a command or a named action, never prose.
| Code | Means | Next step |
|------|-------|-----------|
| SETUP_REQUIRED | No API key, or the stored key is rejected | coderifts login |
| INSTALLATION_MISSING | The CodeRifts App is not installed on the repository | Install it, then re-run with --live |
| WORKSPACE_NOT_BOUND | The server reports no claimed binding for this tenant | coderifts claim |
| REQUIRED_CHECK_NOT_CONFIGURED | Installed, but the check is not a required status check | coderifts setup-required-check --repo <owner/repo> |
| ISSUER_MISMATCH | Required by context name only, or bound to a different app id | Re-bind the required check to the CodeRifts App |
| SURFACE_DRIFT | What the installer pinned and what the repository serves have diverged | coderifts drift --live <owner/repo> |
⚠ ISSUER_MISMATCH is not a milder REQUIRED_CHECK_NOT_CONFIGURED. A check required by name
with no app binding is satisfiable by any repository writer posting that context: it looks
enforcing and is not.
