Skip to main content

🚀 Beta: All Pro and Team features are free. Install on GitHub →

TypeScript primary · Python secondary

Agent Framework
Integrations

Add API governance to AI agents. Primary path: @coderifts/agent-guard + TypeScript (OpenAI tools / LangGraph-JS). Python SDK examples stay available as a secondary section. Contract: Decision Spec v2preflight_mode required; act only on authorize + verified receipt.

Installation

Primary — TypeScript runtime guard + SDK

npm install @coderifts/agent-guard @coderifts/sdk

Secondary — Python SDK

pip3 install coderifts-sdk

Full mode contract (analyze vs authorize): /decision-spec/. MCP clients: /mcp/.

Framework Examples

Default tab is TypeScript. Every acting example uses preflight_mode: "authorize" with context.operation, branches on execution_action, and only then trusts a receipt for downstream gates. Analyze is risk-only — see the decision-spec page.

TypeScript — withCodeRifts (canonical)

Wrap the OpenAI (or any) tool table so mutating tools cannot run without a CodeRifts authorize preflight. The host must register only the returned tools array. Operation is required (receipts bind to it). See also agents quickstart.

Install

npm install @coderifts/agent-guard @coderifts/sdk

Guard the tool table (authorize path inside the package)

import { withCodeRifts } from '@coderifts/agent-guard';
import { CodeRifts } from '@coderifts/sdk';

const client = new CodeRifts({ apiKey: process.env.CODERIFTS_API_KEY });

// Register ONLY tools from the returned table — anything else bypasses the guard.
const { tools } = withCodeRifts({
  tools: rawOpenAITools,
  client,
  operation: 'merge',  // required: merge | deploy | publish | tool_call | …
  environment: 'production',
});

// When you call preflight yourself (e.g. MCP or REST twin), use the authorize wrapper:
const result = await client.authorizeChangeSet({
  context: { operation: 'merge' },
  artifacts: [{ id: 'api', type: 'openapi', before: baseSpec, after: headSpec }],
});
// Branch on execution_action only (authorize). Unknown values fail closed.
if (result.execution_action !== 'CONTINUE') {
  throw new Error(`halted: ${result.execution_action} (${result.decision})`);
}
// A valid signature is NOT authorization. currently_authorized lives on the
// verify-receipt response, not on the preflight result — check it before acting.
// Token-only verifyReceipt returns currently_authorized:null (not evaluated).
// Pass VerifyReceiptIntendedContext so the server can return true/false.
if (!result.chain_receipt) {
  throw new Error('CodeRifts: authorize returned no chain_receipt to verify');
}
if (!result.decision_result) {
  throw new Error('CodeRifts: missing decision_result');
}
const authz = await client.verifyReceipt(result.chain_receipt, {
  operation: 'merge',  // same operation the authorize preflight used
  target_id: result.decision_result.artifact_digest ?? undefined,
  fingerprint: result.verdict_fingerprint,
  decision_result: result.decision_result,  // DecisionResultEnvelope (JSON object; no to_dict)
});
if (authz.currently_authorized !== true) {
  throw new Error(`not authorized: ${authz.status ?? 'unknown'}`);
}
// Downstream gates: verify chain_receipt conjunctively (receipt + currently_authorized
// + operation + fingerprint + execution_action) — see /decision-spec/

LangGraph.js sketch (same authorize rule)

// Same contract: preflight_mode "authorize", context.operation set, branch on execution_action.
// Prefer withCodeRifts so the model never holds raw mutating tools.
const { tools } = withCodeRifts({ tools: graphTools, client, operation: 'merge' });
// bind tools into your LangGraph.js agent / tool node

LangGraph (Python) — Preflight Node

Secondary path. Authorize mode + branch on execution_action. For strongest prevention use TypeScript withCodeRifts above.

Install dependencies

pip3 install coderifts-sdk langgraph

Full working example

from langgraph.graph import StateGraph
from coderifts import CodeRifts

coderifts = CodeRifts(api_key="cr_live_...")

def preflight_node(state):
    result = coderifts.authorize_change_set(
        artifacts=[{
            "id": state["artifact_id"],
            "type": "openapi",
            "before": state["old_spec"],
            "after": state["new_spec"],
        }],
        context={"operation": "merge", "environment": "staging"},
    )
    # Authorize only: branch on execution_action; decision explains why.
    action = result.execution_action
    # CONTINUE_WITH_MONITORING requires monitoringSinkWired; this example proceeds only on CONTINUE.
    if action != "CONTINUE":
        return {**state, "blocked": True, "reason": f"aborted: {action} (decision={result.decision})"}
    # A valid signature is not authorization: currently_authorized comes from verify_receipt.
    receipt = getattr(result, "chain_receipt", None)
    authz = coderifts.verify_receipt(
        receipt,
        operation="merge",
        environment=getattr(result, "environment", None),
        target_id=getattr(result.decision_result, "artifact_digest", None),
        fingerprint=getattr(result, "verdict_fingerprint", None),
        decision_result=result.decision_result.to_dict(),
    ) if receipt else None
    if getattr(authz, "currently_authorized", None) is not True:
        return {**state, "blocked": True, "reason": "receipt not currently_authorized"}
    return {**state, "blocked": False, "receipt": receipt}

def tool_node(state):
    if state.get("blocked"):
        return state
    return state

builder = StateGraph(dict)
builder.add_node("preflight", preflight_node)
builder.add_node("execute", tool_node)
builder.add_edge("preflight", "execute")
graph = builder.compile()

AutoGen — Safe Tool Wrapper (Python)

Authorize preflight before the tool runs. Branch on execution_action only.

Install dependencies

pip3 install coderifts-sdk pyautogen

Full working example

import autogen
from coderifts import CodeRifts

coderifts = CodeRifts(api_key="cr_live_...")

def safe_tool_call(artifact_id, old_spec, new_spec, tool_fn, *args, **kwargs):
    result = coderifts.authorize_change_set(
        context={"operation": "tool_call"},
        artifacts=[{
            "id": artifact_id,
            "type": "openapi",
            "before": old_spec,
            "after": new_spec,
        }],
    )
    action = result.execution_action
    if action != "CONTINUE":
        return f"BLOCKED: aborted execution_action={action!r} (decision={result.decision})"
    # A valid signature is not authorization: currently_authorized comes from verify_receipt.
    receipt = getattr(result, "chain_receipt", None)
    authz = coderifts.verify_receipt(
        receipt,
        operation="tool_call",
        environment=getattr(result, "environment", None),
        target_id=getattr(result.decision_result, "artifact_digest", None),
        fingerprint=getattr(result, "verdict_fingerprint", None),
        decision_result=result.decision_result.to_dict(),
    ) if receipt else None
    if getattr(authz, "currently_authorized", None) is not True:
        return "BLOCKED: receipt not currently_authorized"
    return tool_fn(*args, **kwargs)

CrewAI — Safe API Tool (Python)

Authorize on init; on run, re-check execution_action before executing.

Install dependencies

pip3 install coderifts-sdk crewai

Full working example

from crewai import Agent, Task, Crew
from coderifts import CodeRifts

coderifts = CodeRifts(api_key="cr_live_...")

class SafeAPITool:
    def __init__(self, artifact_id, old_spec, new_spec):
        self.artifact_id = artifact_id
        self.preflight = coderifts.authorize_change_set(
            context={"operation": "tool_call"},
            artifacts=[{
                "id": artifact_id,
                "type": "openapi",
                "before": old_spec,
                "after": new_spec,
            }],
        )

    def run(self, *args, **kwargs):
        action = self.preflight.execution_action
        if action != "CONTINUE":
            raise RuntimeError(f"CodeRifts aborted: execution_action={action!r} (decision={self.preflight.decision})")
        # A valid signature is not authorization: currently_authorized comes from verify_receipt.
        receipt = getattr(self.preflight, "chain_receipt", None)
        authz = coderifts.verify_receipt(
            receipt,
            operation="tool_call",
            environment=getattr(self.preflight, "environment", None),
            target_id=getattr(self.preflight.decision_result, "artifact_digest", None),
            fingerprint=getattr(self.preflight, "verdict_fingerprint", None),
            decision_result=self.preflight.decision_result.to_dict(),
        ) if receipt else None
        if getattr(authz, "currently_authorized", None) is not True:
            raise RuntimeError("CodeRifts: receipt not currently_authorized")
        # ... execute tool

LangChain (Python) — Preflight Tool

Authorize + branch on execution_action inside the tool.

Install dependencies

pip3 install coderifts-sdk langchain-core

Full working example

from langchain_core.tools import tool
from coderifts import CodeRifts

coderifts = CodeRifts(api_key="cr_live_...")

@tool
def get_order_status(order_id: str) -> str:
    """Look up an order status before calling the API."""
    result = coderifts.authorize_change_set(
        context={"operation": "tool_call"},
        artifacts=[{
            "id": "orders-api",
            "type": "openapi",
            "before": OLD_SPEC,
            "after": NEW_SPEC,
        }],
    )
    action = result.execution_action
    if action != "CONTINUE":
        raise RuntimeError(f"CodeRifts aborted: execution_action={action!r} (decision={result.decision})")
    # A valid signature is not authorization: currently_authorized comes from verify_receipt.
    receipt = getattr(result, "chain_receipt", None)
    authz = coderifts.verify_receipt(
        receipt,
        operation="tool_call",
        environment=getattr(result, "environment", None),
        target_id=getattr(result.decision_result, "artifact_digest", None),
        fingerprint=getattr(result, "verdict_fingerprint", None),
        decision_result=result.decision_result.to_dict(),
    ) if receipt else None
    if getattr(authz, "currently_authorized", None) is not True:
        raise RuntimeError("CodeRifts: receipt not currently_authorized")
    return status

SDK surface (summary)

Full field contract: Decision Spec v2. Below is a short map only.

Method Description Returns
preflight_change_set(..., preflight_mode=) preflight_mode required. authorize (with context.operation): branch on execution_action. analyze: risk only (analysis_outcome, may_execute:false) — not permission. mode-dependent (see /decision-spec/)
verify_receipt(token, ...) Verify a signed chain-receipt you already hold. A valid signature is not authorization — check currently_authorized. valid, currently_authorized, status
get_decision_details(decision_id=..., fingerprint=...) Look up a past decision by decision_id or fingerprint. Read-only — not for making new safety decisions. execution_action, decision, safe_for_agent, risk_score

Default Policy

CodeRifts provides a recommended default policy for all agent frameworks. Fetch it programmatically from the GET /api/v1/policy/default endpoint.

The default policy blocks every breaking change — field or endpoint removal, type changes, enum narrowing, and newly required fields — and flags everything else for review. Each endpoint returns the same decision shape, so one check works across every framework.

Copy-paste preflight decorator (secondary)

A framework-agnostic Python decorator can wrap a tool function for demos. Prefer the TypeScript @coderifts/agent-guard path above for production table wrapping. Any path that acts on a change set must use preflight_mode: "authorize" with context.operation and branch on execution_action — not mode-less public GET checks as a gate. Full contract: /decision-spec/.

Runtime prevention that wraps a whole tool table: @coderifts/agent-guardagents quickstart.

Wrap any tool function

from coderifts_decorator import coderifts_guard

@coderifts_guard(old_spec, new_spec)          # halts on BLOCK
def call_order_api(order_id):
    # runs only if the change is safe for agents
    return requests.get(f"https://your-api.com/orders/{order_id}").json()

Human-in-the-loop: also halt on REQUIRE_APPROVAL

@coderifts_guard(old_spec, new_spec, strict=True)
def call_order_api(order_id):
    ...

Runnable LangGraph and LangChain examples, verified end-to-end, live in the example-langgraph-guard repo.

MCP Integration

For agents that support the Model Context Protocol (Claude Desktop, Cursor, Windsurf), CodeRifts exposes governance tools directly via MCP — no SDK required.

View MCP Integration