CAIN-42 CAIN Studio

CAIN-42 · For developers

TypeScript SDK

Guard agent actions from Node.js and TypeScript.

Last reviewed 2026-10-01

Live   Core platform · Platform feature

Where it fits

Part of For developers: SDKs, CLI, MCP server, APIs and offline verifiers. Every CAIN-42 product runs behind the same rule: an AI agent's action is checked before it runs (identity, authority, policy, risk), decided as allow, hold for a human, or block, and recorded as signed evidence. Unknown or error never becomes allow.

Use it

Live now

Checked from your browser when this page opened, not from a cached list.

Fire a real decision

Send an action through the live CAIN-42 pipeline from this page, with no account, and watch every stage decide. This is the same pipeline every product here sits behind; it runs for a throwaway demo tenant and is rate limited.

For AI engineers

Every product sits behind one decision path: your agent proposes an action with the exact arguments, CAIN runs it through identity, authority, policy, risk, trust and quorum consensus, and answers ALLOW, REQUIRE_APPROVAL or DENY with an Ed25519-signed record. A timeout, outage or unknown verdict never becomes ALLOW. A brand-new agent has no trust history, so its first actions usually come back REQUIRE_APPROVAL.

Python (zero dependencies)

pip install https://cainstudio.online/cainstudio-0.3.0-py3-none-any.whl
export CAIN_API_KEY=...   # free key: https://cainstudio.online/signup

import cainstudio

@cainstudio.guard()
def transfer(amount_usd: float, to: str) -> str:
    ...  # runs only if CAIN allows this call, with these arguments

try:
    transfer(5000, "acme")
except cainstudio.ApprovalRequired as e:
    print("held for a human:", e.approval_id)
except cainstudio.ActionBlocked as e:
    print("refused:", e.decision.reasons)
except cainstudio.CainUnavailable:
    print("CAIN unreachable: not run")   # fail-closed

See a real decision with no account

cainstudio try          # live pipeline, stage by stage
cainstudio try --list   # the other attack scenarios

MCP clients (Claude Code, Cursor)

claude mcp add --transport http cain https://cainstudio.online/mcp

More: Python SDK · TypeScript SDK · framework integrations · AI quickstart · decision signing key

Tested guarantees in this area

Every rule in these niches has its own page with its recorded result.

Related

Full documentation

The complete reference, also at /docs/sdk-typescript.

TypeScript and JavaScript#

There is no npm package yet. The decision API is one HTTP call, so the helper below is the whole integration: copy it into your project. Node 18+ (global fetch), Deno, Bun and edge runtimes. No dependencies.

// cain.ts
const CAIN_URL = process.env.CAIN_BASE_URL ?? "https://cainstudio.online";

export class CainRefused extends Error {
  constructor(message: string, readonly decision?: any) { super(message); }
}

/** Ask CAIN whether a tool call may run. Resolves with the decision; never throws. */
export async function decide(tool: string, payload: object, agentId = "my-agent", runId?: string) {
  try {
    const r = await fetch(`${CAIN_URL}/fabric/decisions`, {
      method: "POST",
      headers: { "X-API-Key": process.env.CAIN_API_KEY ?? "", "content-type": "application/json" },
      body: JSON.stringify({ path: `/tools/${tool}`, payload, agent_id: agentId, chain_id: runId }),
      signal: AbortSignal.timeout(5000),
    });
    return await r.json();
  } catch (e) {
    return { verdict: "ERROR", blocked: true, detail: String(e) };   // fail closed
  }
}

/** The only permit: an ALLOWED* verdict that is not blocked. */
export const allowed = (d: any) =>
  d?.blocked === false && String(d?.verdict ?? "").startsWith("ALLOWED");

/** Wrap a tool: it runs only if CAIN allows this call with these arguments. */
export function guard<A extends object, R>(tool: string, fn: (args: A) => Promise<R>, agentId?: string) {
  return async (args: A): Promise<R> => {
    const d = await decide(tool, args, agentId);
    if (!allowed(d)) throw new CainRefused(`${tool}: ${d.verdict ?? d.detail ?? "no verdict"} (decision ${d.decision_id ?? "-"})`, d);
    return fn(args);
  };
}
import { guard } from "./cain";

export const sendEmail = guard("send_email", async ({ to, subject }: { to: string; subject: string }) => {
  // unchanged
});

A new agent has no trust history, so its first calls come back REQUIRE_APPROVAL: approve them in the console, or add a tool rule. The verdicts, the decision record and the approval flow are the same as in the Python SDK and the HTTP quickstart. The full OpenAPI schema is operator-only.

Try CAIN-42 on your own agents

Create a free account and every new account starts with a 7-day trial of the full platform. Or try the sandbox first, with no account at all.

Create a free account →  ·  Try the sandbox  ·  See the whole ecosystem