api-governance 1.2.9
CodeRifts contract authorization for Claude Code: the MCP server, the skill that runs before a contract change is merged or shipped, and a mod that checks a contract edit before it is written.
What it contains
- MCP server
coderifts(https://app.coderifts.com/mcp):preflight_change_set,verify_receipt,get_decision_details. - Skill
coderifts: when to call preflight, and how to branch onexecution_action. - Mod
hooks/register.js(blast-radius, for API contracts): needs Claude Code 2.1.287 or later.
The mod: what its hook decides, and when
The mod has one decision hook, on tool.check: the permission check Claude Code runs before a
Write, Edit, MultiEdit or Bash call. It decides only when the call touches a contract file (a file
named under schema: in .coderifts.yml, or an OpenAPI / Swagger / AsyncAPI / GraphQL / protobuf /
MCP manifest file). Then it sends the file before and after the edit to CodeRifts preflight (one
request, at most 8 s) and answers in one of four ways:
| CodeRifts answers | The hook | So |
|---|---|---|
STOP | deny | the call does not run; the reason says what broke and the next step |
REQUEST_APPROVAL (without a key: a break the analysis found) | ask | you decide |
| nothing it can use (no answer within 8 s, an error, an answer it cannot read) | ask | you decide; the reason says GOVERNANCE_UNAVAILABLE … this is not a finding about your change |
CONTINUE or CONTINUE_WITH_MONITORING (without a key: no break) | passes the check on | your settings and permission mode decide, as without the mod |
A call that touches no contract file is passed on unchecked, and nothing is sent. The hook never
answers anything but deny or ask. An ask is the mod's own answer: it does not consult a deny
from your own permission rules or another hook below it, so such a rule can be asked over instead of
applied. A Bash command that writes a named contract file is refused with a pointer to the Write or
Edit tool, which is checked; one that can reach a directory holding a contract without naming it
asks. An Edit that does not apply to the file on disk is refused, because there is no after text
to check.
A refusal or a question says in one sentence what CodeRifts found, what to do next, and that the
merge gate is the required CodeRifts check on the pull request. A band above the prompt shows this turn's decisions
in the terminal and the Desktop app; elsewhere each decision is one transcript line. Both carry the
decision id and the receipt digest: coderifts.get_decision_details with that id returns the
decision and its receipt, which coderifts.verify_receipt (or the open receipt verifier) checks.
Your API key
The key is the plugin's api_key option. Claude Code asks for it when you install or enable the
plugin in /plugin, and you can set it at any time with /plugin configure api-governance@coderifts
(or, from a shell, claude plugin configure api-governance@coderifts --values-stdin with
{"api_key": "…"} on stdin). It is stored in your platform's secure credential store, not in
settings.json. The same key authenticates the coderifts MCP server. Without a key the mod
analyzes (preflight_mode=analyze), which authorizes nothing, and the MCP server answers keyless.
The plugin no longer reads the CODERIFTS_API_KEY or CODERIFTS_API_BASE environment variables
(before 1.2.2 it did). The coderifts CLI still reads CODERIFTS_API_KEY; that is a separate tool.
What this plugin sends, and where
- The mod checks a Write, Edit or MultiEdit of a contract file with one
POSTtohttps://app.coderifts.com/api/v1/preflight, the only call it makes out of your machine. CodeRifts diffs the two texts and answers with a decision; the mod uses the answer for exactly one thing, the choice in the table above, and shows the decision id and the receipt digest from it. The request carries the file's whole before and after text and its type (openapi,graphql,grpc,asyncapiormcp_manifest). The file is named only by the fixed idapi; its path is not sent. When theapi_keyoption is set, the request ispreflight_mode=authorize, with your key in theAuthorizationheader and the fixed contextoperation=merge, environment=staging; otherwise it ispreflight_mode=analyzewith no key. It also carries the headerX-CodeRifts-Source: claude_marketplace, which tells CodeRifts the call came from this plugin; it identifies the plugin, not you. To decide whether a call touches a contract, it reads.coderifts.ymlon every Write, Edit, MultiEdit and Bash call, the file being edited, and, for a Bash command, the names of the files under the directories the command can write (not their contents). A Bash command's text is read on your machine and never sent. It sends no other file, no prompt and no transcript, and it contacts no other host. An MCP client configuration, which can hold the credentials its servers start with, is never sent:.mcp.json,.cursor/mcp.json,.vscode/mcp.json,claude_desktop_config.jsonand(cline_)mcp_settings.jsonare not read at all, and any other file namedmcp.jsonis read on your machine only to classify it. A client configuration (mcpServersorservers, notools) is passed on and never sent; a file that is both a client configuration and a tool manifest, or that does not parse, is never sent either — the mod asks. - The MCP server
coderiftsishttps://app.coderifts.com/mcp: what you or Claude pass to its tools is sent there. - The skill sends nothing by itself.
Privacy policy: https://coderifts.com/privacy/
What this mod does not prove
- Installed as a mod, it is one of the mods the user installed: a mod that runs before it can answer or rewrite the call, or what this mod reads, so it never sees the change; disableAllHooks in any settings file (the .claude/settings.json of the project included), --safe-mode or --bare turns it off.
- Only an organization mod (installed in place from a marketplace directory the administrator owns, and enabled in managed settings) runs before the mods users install and keeps its refusal for the calls it sees; installed from its GitHub marketplace this plugin never counts as one, and --safe-mode or three crashes of the hooks worker unload even that.
- Bash is read from the command text: a shell write to a named contract file is refused, and one that can reach a directory holding a contract without naming it asks; a command shape it does not recognise passes, and the required check is the guarantee.
- It does not prove that the mod was running: a plugin can fail to load its mod without a message; /plugin shows "1 mod active" when it runs, and the required check is the guarantee.
- A contract generated from source code — annotations, decorators, a build step — changes when the source changes; the hooks see the source edit, not the contract; the required check sees the generated contract.