SDK

@atbash/sdk

TypeScript SDK for Atbash — the safety layer that evaluates AI agent actions against operator-defined policies before execution.

Installation

Terminal
npm install @atbash/sdk

Requires Node.js 18 or higher. Server-side only — private keys are used for local signing 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 { loadAgent, judgeAction } from "@atbash/sdk";

// 1. Load your agent identity using the private key you saved during
//    agent creation. loadAgent() validates the key and derives the
//    matching public key for you.
const agent = loadAgent(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?

There are two ways to create an agent:

1. Dashboard (recommended) — create an agent at atbash.ai/risk-engine/agents. The dashboard generates the keypair, assigns the agent to your org, and lets you attach a policy pack — all in one step.

2. Programmatic — generate a new keypair locally or bring an existing secp256k1 private key from another platform, then onboard it via the dashboard:

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

const { privKey } = generateKeyPair();
// Persist privKey to your secret manager (AWS Secrets Manager, Vault, etc.).
// Never log it, commit it, or print it to stdout — it controls the agent's identity.
const agent = loadAgent(privKey);

Note: After generating a key programmatically, you must still onboard the agent at atbash.ai/risk-engine/agents — assign it to an org and attach a policy pack before judgeAction will work.

Secret storage

The private key is used to sign on-chain transactions locally. 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

NB: If your org is on the Audit tier, the judge returns "No verdict" — actions are logged on-chain for the audit trail but not evaluated by an AI provider. Upgrade to Audit+ or Enforcement at atbash.ai/risk-engine/settings for active verdicts.

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 identity from loadAgent().

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;          // "openai" | "google" | "microsoft" | "custom"
  model?: string;             // Model override (e.g. "gpt-4o-mini")
  toolName?: string;          // Tool name for audit trail
  toolArgsJson?: string;      // Tool arguments JSON for audit trail
  chainOpts?: ChainOpts;      // Override Chromia chain connection
  verifyPubKey?: string;      // 66-hex pubkey for self-hosted judge response verification
}

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, agentPubkey: 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

loadAgent(privkey: string): AgentAuth

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

derivePublicKey(privKeyHex: string): string

isValidPrivateKey(hex: string): boolean

toPubkeyHex(val: unknown): string

loadAgent(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

Pass configuration explicitly via options, or use the SDK's resolve() function which checks CLI flags → environment variables → config file (~/.config/atbash/config.json):

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

// Custom provider (API keys are saved in the dashboard, not passed here)
const result = await judgeAction(action, context, auth, {
  provider: "openai",
  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, agentPubkey: string): Promise<string> {
  while (true) {
    const status = await getJudgmentStatus(toolCallId, agentPubkey);
    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 keyatbash.ai/risk-engine/settings
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.