Installation
npm install @atbash/sdkRequires Node.js 18 or higher. Server-side only — private keys are sent in request bodies and must never be exposed to browsers.
Quickstart
Load an agent, judge the pending action, then branch on ALLOW / HOLD / BLOCK before anything irreversible runs.
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:
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
.envfiles 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:
| Verdict | Meaning | What your code should do |
|---|---|---|
| ALLOW | Action is within policy | Proceed with execution |
| HOLD | Requires operator review | Pause — poll getJudgmentStatus until resolved |
| BLOCK | Violates a red line | Abort — 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
actionstringThe operation the agent is about to perform.
contextstringOperational context for the risk engine (blast radius, AML notes, etc.).
authAgentAuthAgent credentials from createAgent().
optsJudgeOptions (optional)Endpoint, timeout, inference provider, model, audit fields.
AgentAuth
interface AgentAuth {
pubkey: string; // 66-char hex, secp256k1 compressed public key
privkey: string; // 64-char hex private key
}JudgeOptions
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
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.
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:
// 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:
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:
// 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",
});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.
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.
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.
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:
API error 404: {"error":"Agent not registered..."}
→ Onboard the agent at https://atbash.ai/risk-engine/agents| Error | Cause | Where to fix |
|---|---|---|
| API error 404: Agent not registered | Agent not onboarded | atbash.ai/risk-engine/agents |
| API error 400: Agent has no policy | No policy attached to agent | atbash.ai/risk-engine/agents |
| Agent is jailed | BLOCK verdict triggered auto-jail (Enforcement tier) | atbash.ai/risk-engine/agents |
| Org tier does not support verdicts | Org is on Audit tier | atbash.ai/risk-engine/settings |
| API error 400: action is required | Empty action string | Fix caller |
| API error 502: Incorrect API key | Invalid provider API key | Check apiKey in JudgeOptions |
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.