SDK

@atbash/sdk

TypeScript SDK for the Atbash judge and risk-engine APIs. Evaluate agent actions against operator-defined policy before execution — programmatically, from any Node.js backend.

Installation

Terminal
npm install @atbash/sdk

Requires Node.js 18 or higher. Server-side only — private keys are sent in request bodies and must never be exposed to browsers.

Start here

Quickstart

Load an agent, judge the pending action, then branch on ALLOW / HOLD / BLOCK before anything irreversible runs.

TypeScript
import { createAgent, judgeAction } from "@atbash/sdk";

// 1. Load your agent identity — paste the private key from the Atbash
//    dashboard (https://atbash.ai/risk-engine/agents). createAgent()
//    validates the key and derives the matching public key for you.
const agent = createAgent(process.env.ATBASH_AGENT_PRIVKEY!);

// 2. Submit an action for judgment, before executing it
const result = await judgeAction(
  "Transfer $50,000 to external wallet 0xabc",
  "Outbound AML check — new recipient, over threshold",
  agent,
);

// 3. Enforce the verdict
switch (result.verdict) {
  case "ALLOW":
    // Proceed with the action
    break;
  case "HOLD":
    // Held — operator must approve in the dashboard
    console.log("Held for review:", result.tool_call_id);
    break;
  case "BLOCK":
    // Refused — agent is jailed in Enforcement tier
    throw new Error(`Blocked: ${result.reason}`);
}

Before this works, the agent must be onboarded at atbash.ai — assigned to an org, with a policy pack attached, and the org tier set to Audit+ or Enforcement.

Don't have an agent yet?

Generate one programmatically for local development:

TypeScript
import { generateKeyPair, createAgent } from "@atbash/sdk";

const { privKey } = generateKeyPair();
console.log("Save this private key somewhere safe:", privKey);
const agent = createAgent(privKey);

For production, always create agents in the dashboard so operators can attach policy packs and manage tier / jail state.

Secret storage

The private key authenticates every call. Treat it like any other long-lived credential:

  • Load it from an environment variable (ATBASH_AGENT_PRIVKEY) or a secret manager (AWS Secrets Manager, HashiCorp Vault, 1Password, etc.) — never hardcode it.
  • Never commit .env files containing the key. Add them to .gitignore.
  • If a key leaks, revoke the agent and create a new one in the Atbash dashboard.
  • The SDK is server-side only — the key must never ship to a browser bundle.

Verdicts

Every judgeAction call returns one of three verdicts:

VerdictMeaningWhat your code should do
ALLOWAction is within policyProceed with execution
HOLDRequires operator reviewPause — poll getJudgmentStatus until resolved
BLOCKViolates a red lineAbort — agent is jailed in Enforcement tier

API

Judge

Signature

judgeAction(
  action: string,
  context: string,
  auth: AgentAuth,
  opts?: JudgeOptions,
): Promise<JudgeResult>

Submit an action for judgment before execution. Returns the verdict, reason, confidence, provider, latency, and tool call ID.

Parameters

actionstring

The operation the agent is about to perform.

contextstring

Operational context for the risk engine (blast radius, AML notes, etc.).

authAgentAuth

Agent credentials from createAgent().

optsJudgeOptions (optional)

Endpoint, timeout, inference provider, model, audit fields.

AgentAuth

TypeScript
interface AgentAuth {
  pubkey: string;   // 66-char hex, secp256k1 compressed public key
  privkey: string;  // 64-char hex private key
}

JudgeOptions

TypeScript
interface JudgeOptions {
  endpoint?: string;          // API base URL (default: https://atbash.ai)
  timeout?: number;           // Request timeout in ms
  provider?: string;          // "atbash" | "openai" | "google" | "microsoft" | "custom"
  apiKey?: string;            // API key for non-atbash providers
  providerEndpoint?: string;  // Endpoint for microsoft/custom providers
  model?: string;             // Model override (e.g. "gpt-4o-mini")
  toolName?: string;          // Tool name for audit trail
  toolArgsJson?: string;      // Tool arguments JSON for audit trail
}

JudgeResult

TypeScript
interface JudgeResult {
  verdict: string;       // "ALLOW", "HOLD", or "BLOCK"
  action_type: string;   // "allow", "hold_for_user_confirm", or "block"
  reason: string;        // Human-readable explanation
  confidence: number;    // 0–1
  provider: string;      // Which provider evaluated the action
  latency_ms: number;    // Inference time
  tool_call_id: string;  // Unique ID for this judgment
  on_chain: boolean;     // Whether the record was written on-chain
}

Poll judgment status

Signature

getJudgmentStatus(judgmentId: string, opts?: ClientOpts): Promise<JudgmentStatus>

Check whether a held action has been approved or rejected by an operator.

TypeScript
interface JudgmentStatus {
  status: "pending" | "answered" | "error";
  verdict: string;
  reason: string;
  judgmentId: string;
  onChain?: boolean;
  cached?: boolean;
  responseTimeMs?: number;
}

Agent identity

createAgent(privkey: string): AgentAuth

generateKeyPair(): { privKey: string; pubKey: string }

derivePublicKey(privKeyHex: string): string

isValidPrivateKey(hex: string): boolean

toPubkeyHex(val: unknown): string

createAgent(privkey) is the canonical loader — pass in the private key from the dashboard, get back { pubkey, privkey } ready for judgeAction. It accepts 0x-prefixed, padded, or mixed-case input and throws on malformed keys. Use generateKeyPair() only for local development; for production, create agents in the dashboard so operators can attach policies.

Query APIs

Read from the Atbash audit trail and operator queues:

TypeScript
// Tool call history
getToolCalls(maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>
getOrgToolCalls(orgName: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>
getAgentToolCalls(agentPubkey: string, maxCount: number, opts?: ClientOpts): Promise<ToolCallRecord[]>
getToolCallCount(opts?: ClientOpts): Promise<number>
getToolCallFull(toolCallId: string, opts?: ClientOpts): Promise<ToolCallFull | null>

// Org and agent info
getOrgTierInfo(orgName: string, opts?: ClientOpts): Promise<TierInfo | null>
getAgentDetail(agentPubkey: string, opts?: ClientOpts): Promise<Record<string, unknown>>
getAgentPolicy(agentPubkey: string, opts?: ClientOpts): Promise<{ policy: string; is_jailed: boolean; is_custom: boolean; default_policy: string }>

// Operator review queue
getPendingHeldActions(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldAction[]>
getHeldActionReviews(orgName: string, maxCount: number, opts?: ClientOpts): Promise<HeldActionReview[]>

// Chain-wide stats
getSafetyStats(opts?: ClientOpts): Promise<Record<string, unknown>>

All query functions accept an optional ClientOpts to override the endpoint:

ClientOpts
interface ClientOpts {
  endpoint?: string;  // Default: https://atbash.ai
  timeout?: number;
}

Configuration

The SDK reads no environment variables and has no global state. Pass configuration explicitly:

TypeScript
// Custom endpoint
const result = await judgeAction(action, context, auth, {
  endpoint: "https://your-instance.example.com",
});

// Custom provider
const result = await judgeAction(action, context, auth, {
  provider: "openai",
  apiKey: process.env.OPENAI_API_KEY,
  model: "gpt-4o",
});
Recipes

Integration patterns

Drop-in shapes for gating execution, waiting on operator approval, and checking jail state before batch work.

Pre-execution gate

Judge first; only run the side effect when verdict is ALLOW.

TypeScript
async function safeExecute(action: string, context: string, execute: () => Promise<void>) {
  const result = await judgeAction(action, context, auth);
  if (result.verdict === "BLOCK") throw new Error(`Blocked: ${result.reason}`);
  if (result.verdict === "HOLD") throw new Error(`Held for review: ${result.tool_call_id}`);
  await execute();
}

Polling a held action

Loop until the operator resolves the judgment in the dashboard.

TypeScript
async function waitForApproval(toolCallId: string): Promise<string> {
  while (true) {
    const status = await getJudgmentStatus(toolCallId);
    if (status.status === "answered") return status.verdict;
    if (status.status === "error") throw new Error(status.reason);
    await new Promise((r) => setTimeout(r, 5000));
  }
}

Checking agent jail status before a batch

Fail fast if Enforcement tier has jailed the agent.

TypeScript
const policy = await getAgentPolicy(pubKey);
if (policy.is_jailed) {
  console.error("Agent is jailed — unjail via dashboard before retrying.");
  process.exit(1);
}

Error handling

The SDK throws standard Error objects. Known failure modes are enriched with a pointer to the relevant dashboard page, so the error message tells you where to fix the problem:

Example
API error 404: {"error":"Agent not registered..."}
  → Onboard the agent at https://atbash.ai/risk-engine/agents
ErrorCauseWhere to fix
API error 404: Agent not registeredAgent not onboardedatbash.ai/risk-engine/agents
API error 400: Agent has no policyNo policy attached to agentatbash.ai/risk-engine/agents
Agent is jailedBLOCK verdict triggered auto-jail (Enforcement tier)atbash.ai/risk-engine/agents
Org tier does not support verdictsOrg is on Audit tieratbash.ai/risk-engine/settings
API error 400: action is requiredEmpty action stringFix caller
API error 502: Incorrect API keyInvalid provider API keyCheck apiKey in JudgeOptions
TypeScript
try {
  const result = await judgeAction(action, context, auth);
} catch (err) {
  if (err instanceof Error && err.message.includes("Agent not registered")) {
    // Point the user at https://atbash.ai/risk-engine/agents to onboard.
  }
}

Dashboard

Policy authoring, operator reviews, and agent management happen at atbash.ai. The SDK is the programmatic interface; the dashboard is the operator interface.

License

Proprietary — all rights reserved. See LICENSE.