npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coderifts/sdk

v3.14.3

Published

Agent Governance SDK — validate API changes before tool invocations. TypeScript client for the CodeRifts API.

Readme

@coderifts/sdk

Verifying a receipt offline

verifyReceipt runs locally: the same vendored receipt-verifier core the public CLI and the conformance measure run, in process, over bytes already in memory plus a keyring you pin. No network, no API key, full Ed25519.

import { verifyReceipt } from '@coderifts/sdk';

const r = verifyReceipt(token, { keyring: pinnedKeys });
if (!r.valid) throw new Error(`${r.status}: ${r.reason}`);

The keyring is required and is never fetched by this package. A verifier that downloads the key it is about to trust has verified nothing an attacker on the path could not arrange — pin the keys once, as a deployment decision, and hand them in.

Where the keys come from is your decision, and the SDK will not make it. This package ships no embedded keyring — @coderifts/conformance does, this does not — so "obtain a trustworthy keyring" is a step you own. Two routes are supported:

  1. A file you pinned. Fetch https://app.coderifts.com/.well-known/coderifts-keys.json once, out of band, review it, and commit it (the apex coderifts.com path redirects there). Load it and hand it in. This is the stronger form: the pin is a reviewed artifact in your repository, and it changes only when you change it.
  2. The copy vendored in @coderifts/conformance, at lib/vendor/receipt-verifier/keys/coderifts-keys.json. Same document shape, already in your dependency tree if you run the suite. Convenient — but the pin then rides on that package's version, so an upgrade can move the keys you thought you had pinned. Treat the version as part of the pin.

The boundary: pinning is a trust decision taken once, out of band, and this verifier will refuse rather than make it for you — a missing keyring is a TypeError, not a fetch. What pinning cannot give you is freshness. A pinned key is only as current as your last review, and revocation is invisible to any local verifier (see the mirror, below).

The server verify is a MIRROR, not the proof

client.verifyReceiptViaServer(token, intended) POSTs the receipt to CodeRifts and reports what CodeRifts says. It is the only path that can see revocation and the issuer's clock, which no local verifier can know — and it is not a verification you performed: the bytes travelled and the thing you are trusting is the endpoint.

If the two disagree, both are facts: the local answer is what the signature says, the server's is what the issuer currently says. Only the first is a proof you hold.

client.verifyReceipt is a deprecated alias of verifyReceiptViaServer. It sat on a network call in a package whose readers were told they could verify without CodeRifts, and a name is read more often than a docstring.

What a local valid: true does not say

Carried on every verdict as does_not_prove, so it travels with the answer:

  • not authorization. A valid signature is authenticity. Whether the receipt permits the action you are about to take is verifyReceiptViaServer(token, intended) or authorize().
  • not revocation. A key compromised a minute ago still verifies locally. Nothing local can know.
  • not one run. This checks ONE token; "these tokens came from one run" is a property of a set (cr.evidence.root.v1).

| I want to… | Use | | --- | --- | | verify a receipt offline | verifyReceipt(token, { keyring }) — local, this package | | check revocation / the issuer's clock | client.verifyReceiptViaServer(...) — a mirror | | ask "does this authorize my action?" | client.verifyReceiptViaServer(token, intended) | | decide authorized and committed | authorize(...) | | verify offline in Python | pip install coderifts-verifier |

Agent Governance SDK for the CodeRifts API. Validate API changes before tool invocations in AI agent infrastructure (LangChain, AutoGen, Copilot, Claude, Grok, etc.).

Installation

npm install @coderifts/sdk

Current package: 3.14.3.

Quick Start

authorizeChangeSet is the operation-bound entry point: it fixes preflight_mode: 'authorize', so decision, execution_action and safe_for_agent are present without narrowing, and it may mint a signed receipt.

import { CodeRifts } from '@coderifts/sdk';

const client = new CodeRifts({ apiKey: 'cr_live_...' });

const result = await client.authorizeChangeSet({
  artifacts: [
    { id: 'api', type: 'openapi', before: oldYaml, after: newYaml },
  ],
  context: { operation: 'merge' },   // required on authorize (HTTP 400 without it)
});

// Branch on execution_action only. It is a closed set:
// CONTINUE | CONTINUE_WITH_MONITORING | REQUEST_APPROVAL | STOP.
// Anything unrecognised is not permission — fail closed.
if (result.execution_action !== 'CONTINUE') {
  console.error('Halted:', result.execution_action, result.decision);
  process.exit(1);
}

Two request modes

Server-derived (the production path) — the server lists the change set from the repository, so you never assemble artifacts[]:

const result = await client.authorizeChangeSet({
  derivation: 'server',
  context: { repository: 'owner/repo', base: 'main', head: 'feature', operation: 'merge' },
});

Caller-supplied artifacts — you assemble the complete base→head set yourself:

const result = await client.authorizeChangeSet({
  artifacts: [{ id: 'api', type: 'openapi', before: oldYaml, after: newYaml }],
  context: { operation: 'merge' },
});

The two are mutually exclusive and the types enforce it: passing artifacts alongside derivation: 'server', or omitting repository/base/head from a derived request, is a compile error rather than a 400 at runtime.

For an ATOMIC-profile grant, pass the nonce from your executor's state-challenge:

await client.authorizeChangeSet({
  derivation: 'server',
  context: { repository: 'owner/repo', base: 'main', head: 'feature', operation: 'deploy' },
  include_execution_grant: true,
  state_nonce: nonceFromStateChallenge,
});

For risk inspection that is explicitly not permission, use analyzeChangeSet: the analyze branch carries no decision / execution_action / safe_for_agent by protocol.

Methods

preflightCheck(options) — legacy single-spec path

Still shipped and supported. It takes one old_spec / new_spec pair plus a tool_name and calls POST /api/v1/agent/preflight.

For new integrations prefer authorizeChangeSet / analyzeChangeSet: they take a multi-artifact change set (OpenAPI, GraphQL, gRPC, AsyncAPI, MCP manifest) in one call and carry the Decision Spec v2 mode discriminator, so risk inspection cannot be mistaken for permission.

const result = await client.preflightCheck({
  tool_name: 'get_refund_status',
  old_spec: '...',
  new_spec: '...',
});
// result.execution_action: 'CONTINUE' | 'CONTINUE_WITH_MONITORING' | 'REQUEST_APPROVAL' | 'STOP'
// result.decision?: 'BLOCK' | 'REQUIRE_APPROVAL' | 'WARN' | 'ALLOW'  // prose; absent if the server omitted it
// result.omega_api: number
// result.safe: boolean  // 3.9.0 fail-closed: true ONLY on an explicit CONTINUE.
//                       // Means "we verified it is safe", not "we saw no reason it is not".
//                       // Not the control input — branch on execution_action via readDecision.
// result.reflex_triggers: Array<{ rule: string; decision: string }>
// result.affected_tools: Array<{ tool_name: string; status: string }>

diff(options)

Full analysis of two OpenAPI specs.

const result = await client.diff({
  before: '...',
  after: '...',
});
// result.omega_decision: string
// result.risk_score: number
// result.breaking_changes: BreakingChange[]
// result.should_block: boolean

explainDecision / howToUnblock — prose, not gates

See Reading a decision. These two helpers render copy from execution_action. They are not permission checks.

scoreMcp(manifest)

Score an MCP manifest for agent safety.

const score = await client.scoreMcp({
  manifest: { tools: [...] },
});
// score.overall_score: number (0-100)
// score.band: 'STRONG' | 'GOOD' | 'NEEDS_WORK' | 'POOR' | 'CRITICAL'

getLedger(options)

Query compliance ledger entries.

const ledger = await client.getLedger({
  repo: 'owner/repo',
  decision: 'BLOCK',
  limit: 10,
});
// ledger.entries: LedgerEntry[]
// ledger.total: number

simulatePolicy(options)

Test a YAML policy against two OpenAPI specs.

const result = await client.simulatePolicy({
  policy_yaml: '...',
  old_spec: '...',
  new_spec: '...',
});
// result.effective_action: string
// result.matched_rules: MatchedRule[]

Envelope-aware methods (v1.1.0)

These return the decision-result.v1.1 envelope with a top-level execution_action (CONTINUE | CONTINUE_WITH_MONITORING | REQUEST_APPROVAL | STOP) and a signed chain receipt.

preflightChangeSet(request) / analyzeChangeSet / authorizeChangeSet

Preflight a multi-artifact change set (POST /api/v1/preflight). Required top-level preflight_mode: 'analyze' | 'authorize' (Decision Spec v2; server returns 400 if omitted).

Prefer the wrappers so the two meanings cannot be mixed:

// Risk-only (informational — not permission)
const risk = await client.analyzeChangeSet({
  artifacts: [{ id: 'payments', type: 'openapi', before: oldSpec, after: newSpec }],
});

// Operation-bound authorize (requires context.operation; may mint a receipt)
const auth = await client.authorizeChangeSet({
  artifacts: [{ id: 'payments', type: 'openapi', before: oldSpec, after: newSpec }],
  context: { operation: 'merge', environment: 'production' },
  idempotency_key: 'pr-1234',
});

// Or set the mode explicitly:
const res = await client.preflightChangeSet({
  preflight_mode: 'authorize',
  artifacts: [{ id: 'payments', type: 'openapi', before: oldSpec, after: newSpec }],
  context: { operation: 'merge' },
});

verifyReceipt(token)

Verify a chain receipt's signature and integrity. No API key required (public endpoint). POST /api/v1/verify-receipt. Expiry uses 30s clock-skew leeway (CLOCK_SKEW_LEEWAY_MS); 0s for destructive operations in production when the intended context declares them. The SDK does not compare expiry locally — the server does. Unknown intended-context keys are dropped by the REST route (not 400).

const v = await client.verifyReceipt(receiptToken);
// v.valid (boolean), v.status ('VERIFIED_CURRENT' | 'VERIFIED_EXPIRED' | ...), v.payload?

getDecisionDetails(request)

Look up a stored decision by decision_id or fingerprint; returns the stored envelope + meta. POST /api/v1/decisions/lookup.

const d = await client.getDecisionDetails({ decision_id: 'dec_...' });
// d.decision_result (DecisionResultEnvelope), d.meta

Reading a decision (start here)

readDecision(response) is the one correct entry point for turning any CodeRifts response into a go / no-go. It is fail-closed.

import { CodeRifts, readDecision } from '@coderifts/sdk';

const client = new CodeRifts({ apiKey: 'cr_live_...' });
const response = await client.authorizeChangeSet({
  artifacts,
  context: { operation: 'deploy' },
});

const read = readDecision(response);
if (read.executionAction === 'CONTINUE') {
  deploy();
} else if (read.executionAction === 'CONTINUE_WITH_MONITORING') {
  deployWithMonitoring();
} else {
  // REQUEST_APPROVAL, STOP, or anything unreadable
  halt(read.decision, read.reason);
}

execution_action is the control input. decision (ALLOW / WARN / REQUIRE_APPROVAL / BLOCK) is the governance explanation label: log it, print it, put it in a PR comment — never branch on it. That is the agent-host rule not_for_control_flow_use_execution_action, and @coderifts/conformance ships a deliberately-wrong branch-on-decision subject that the suite fails.

does_not_prove is prose, not a class. Every verdict carries it, and it is there to be read: log it, print it, put it in the PR comment beside the decision. It is not a control input, and it is a weaker thing than decision — decision is at least a closed set of labels, while does_not_prove is a list of sentences generated from what was actually measured on that call. Its wording moves when the measurement moves. Branching on its text, its length, or whether a particular phrase appears in it builds a guard on a string that was never promised to stay the same.

If you need a machine-readable limit to branch on, that is a different feature and it is built on request — not by pattern-matching this field.

Resolution order, and what falls closed:

| Input | Result | |-------|--------| | decision_result.execution_action (envelope) | that action, plus envelope / receipt | | top-level execution_action | that action | | unknown / misspelled / lowercase action | STOP, reason: 'UNREADABLE_DECISION' | | {}, null, a string, an error body | STOP, reason: 'UNREADABLE_DECISION' | | decision only (v1 compatibility arm, sunset 2026-09-07) | mapped action (ALLOW → CONTINUE, …) | | an analyze response | STOP — analyze is informational, not permission |

readDecision never throws, so a guard may call it on any value.

What it does not do: it does not verify a receipt. A returned receipt is transported, not validated.

The v1 {decision:"ALLOW"} → CONTINUE arm stays in the normaliser until the 2026-09-07 sunset. The two advisory helpers do not use it: they pass execution_action (or a response with top-level decision stripped) so the forbidden field cannot drive their sentences.

explainDecision / howToUnblock are prose, not gates

Both render human-readable copy. Neither is a permission check — always gate on readDecision. Their control input is execution_action, passed either as a full payload (preferred) or as the scalar:

await client.explainDecision({ omega_api: 0.62, decision: 'BLOCK', response });
await client.howToUnblock({ decision: 'BLOCK', breaking_changes: bcs, response });

Given an unreadable or absent execution action they say the action is unrecognised and must be treated as STOP. explainDecision never reports a change as "safe to proceed", and howToUnblock never says "no unblock needed" for an unreadable value — that wording is reserved for a readable CONTINUE / CONTINUE_WITH_MONITORING.

Policy delivery

File-based hosts (Claude / Cursor / Copilot / Gemini) load the CodeRifts rule file automatically. A host that builds its own system prompt does not — unless it interpolates the constant:

import { CODERIFTS_POLICY, withPolicy, detectPolicyPresence } from '@coderifts/sdk';

const yourPrompt = 'You are a coding agent.';
const content = `${yourPrompt}\n\n${CODERIFTS_POLICY}`;

// or one line, idempotent, no in-place mutation:
const messages = withPolicy([{ role: 'system', content: yourPrompt }, { role: 'user', content: '…' }]);

const presence = detectPolicyPresence(content); // 'detected' | 'absent' | 'unknown'

Three layers (the guard ships the same constant and a systemPrompt observation on the outcome):

  1. withPolicy — append if the marker is not already present.
  2. CODERIFTS_POLICY — one import, one interpolation.
  3. detectPolicyPresence — last net. Nothing supplied → unknown, no warn. Marker absent → once-per-process warn. Marker found → silent.

This proves the text is present, not that the model read or obeyed it.

Error Handling

All methods throw a typed CodeRiftsError on non-2xx responses:

import { CodeRifts, CodeRiftsError } from '@coderifts/sdk';

try {
  const result = await client.preflightCheck({ ... });
} catch (err) {
  if (err instanceof CodeRiftsError) {
    console.error(err.code, err.message);
  }
}

Documentation

Full API documentation: https://coderifts.com/docs

License

MIT