Back to Docs
TS

TypeScript SDK

v1.2.0 · MIT License · ESM · Node.js 22+

The reference implementation of the Aroha Protocol. 20 packages covering every protocol primitive — transport, identity, orchestration, credentials, settlement, reputation, and more.

Installation

Install only what you need. Every package is independently versioned.

# Minimum — agent transport + identity
npm install @aroha-sdk/core

# Full stack — add orchestration, auth, and payment
npm install @aroha-sdk/core @aroha-sdk/orchestrator @aroha-sdk/credentials @aroha-sdk/settlement

# Personal agent bridges
npm install @aroha-sdk/hermes-bridge    # Hermes Agent / ZeroClaw (MCP stdio)
npm install @aroha-sdk/openclaw-bridge  # OpenClaw skills
npm install @aroha-sdk/composio-bridge  # TrustClaw / Composio actions

# Framework bridges
npm install @aroha-sdk/langchain-bridge # LangChain + AutoGen
npm install @aroha-sdk/mcp-bridge       # Model Context Protocol
npm install @aroha-sdk/a2a-bridge       # Google A2A
All packages are pure ESM. Set "type": "module" in your package.json or use .mjs extensions.
npm audit warnings: The SDK's optional dependencies (AutoGen, CrewAI bridges) may trigger moderate severity audit warnings on transitive sub-dependencies. These do not affect the core runtime. Run npm audit --omit=optional to see only warnings relevant to packages you install directly.
Using the TypeScript source directly? Each package must be compiled before it can be imported. From the monorepo root: npm run build --workspaces, or per-package: cd packages/aroha-core && npm run build. The compiled output lands in dist/ — the main field in each package.json points there.

Core: ArohaServer & ArohaClient

@aroha-sdk/core is the only required package. It handles the full protocol stack: HTTP transport, Ed25519 envelope signing/verification, nonce replay protection, and WebSocket streaming.

Minimal agent (devMode)

import { ArohaServer, generateDid, generateKeyPair, MapNonceStore } from "@aroha-sdk/core";

// Generate a keypair and DID (persist these in production)
const { privateKey, publicKey } = await generateKeyPair();
const myDID = generateDid(publicKey);

const server = new ArohaServer({
  agentDID: myDID,
  didDocument: {
    "@context": ["https://www.w3.org/ns/did/v1"],
    id: myDID,
    verificationMethod: [{
      id: `${myDID}#key-1`,
      type: "Ed25519VerificationKey2020",
      controller: myDID,
      publicKeyMultibase: Buffer.from(publicKey).toString("base64"),
    }],
  },
  port: 3000,
  devMode: true,           // skip crypto in development (NODE_ENV=development required)
  onMessage: async (envelope, respond, stream) => {
    console.log("Received:", envelope.type, "from", envelope.from);
    // respond() sends a synchronous reply
    // stream() pushes events over WebSocket
  },
  resolvePublicKey: async (did) => {
    // Return the sender's Ed25519 public key bytes, or null if unknown
    return null;
  },
});

await server.start();
console.log(`Agent ${myDID} listening on port 3000`);

Production server (full crypto)

import { ArohaServer, generateDid, generateKeyPair } from "@aroha-sdk/core";
import { createRbacMiddleware } from "@aroha-sdk/credentials";

const { privateKey, publicKey } = await generateKeyPair();
const myDID = generateDid(publicKey);

const server = new ArohaServer({
  agentDID: myDID,
  didDocument: { /* ... */ },
  port: 3000,
  resolvePublicKey: async (did) => {
    // Fetch from your registry or cache
    const doc = await fetch(`https://aroha-registry.aroha-labs.workers.dev/dids/${did}`);
    const { publicKey } = await doc.json();
    return Buffer.from(publicKey, "base64");
  },
  middleware: [
    // RBAC: require the caller to hold a valid SpendingMandate
    createRbacMiddleware({ requiredTrustLevel: 2 }),
  ],
  clockToleranceMs: 5000,    // allow 5s clock skew across regions
  maxBodyBytes: 1_048_576,   // reject payloads > 1 MiB
  minClientVersion: "1.0",   // reject old client versions
  onMessage: async (envelope, respond) => {
    if (envelope.type === "ArohaRequest") {
      const { capability, params } = envelope.body;
      // handle capability...
      respond(await buildEnvelope("ArohaResponse", myDID, envelope.from,
        { capability, result: { ok: true } },
        envelope.correlationId, privateKey));
    }
  },
});

await server.start();

ArohaClient — calling other agents

import { ArohaClient, buildEnvelope, newCorrelationId } from "@aroha-sdk/core";

const client = new ArohaClient();

const envelope = await buildEnvelope(
  "ArohaRequest",
  myDID,                          // from
  "did:aroha:travel-agent",       // to
  { capability: "search-flights", params: { from: "JFK", to: "LHR", date: "2026-08-01" } },
  newCorrelationId(),             // unique ID for this request
  privateKey                      // signs the envelope
);

const response = await client.send("http://travel-agent.example.com", envelope);

if (response?.type === "ArohaResponse") {
  console.log(response.body.result);   // { flights: [...] }
}
if (!response) {
  // Agent returned 202 — response will arrive via WebSocket stream
}

Identity & DIDs

import { generateKeyPair, generateDid, buildWebDID } from "@aroha-sdk/core";

// did:aroha: (self-sovereign, content-addressed)
const { privateKey, publicKey } = await generateKeyPair();
const did = generateDid(publicKey);
// → "did:aroha:base58encodedPublicKey"

// did:aroha-web: (domain-anchored, like did:web:)
const webDID = buildWebDID("myco.ai", ["agents", "travel"]);
// → "did:aroha-web:myco.ai:agents:travel"

// Serve the DID document at /.well-known/aroha/agents/travel/did.json
// The ArohaServer does this automatically when webDIDDocument is set.

Envelopes & Message Types

Every Aroha message is a signed envelope. The 16 built-in types map to the full protocol lifecycle:

ArohaRequestOrchestrator → Provider: invoke a capability
ArohaResponseProvider → Orchestrator: synchronous result
ArohaStreamProvider → Orchestrator: progress update (WebSocket)
ArohaErrorEither direction: structured error with retryable flag
ArohaReserveSaga step 1: reserve resources, return commitToken
ArohaCommitSaga step 2: commit reserved resources
ArohaCancelSaga compensation: release reserved resources
ArohaHandoffTransfer control to another agent
ArohaDiscoveryQuery the registry for agents with a capability
ArohaCapabilityOfferAgent advertises a capability to another
ArohaAuditTrailAppend-only audit event (hash-chained)
ArohaReputationUpdateSignal: satisfied / neutral / dissatisfied
import { buildEnvelope, newCorrelationId } from "@aroha-sdk/core";

// All envelope fields are set automatically:
//   id          → urn:uuid:<random>
//   created     → now
//   expires     → now + 5 min (default)
//   nonce       → random 16 bytes (base64url)
//   proof       → Ed25519 signature over canonical JSON

const env = await buildEnvelope(
  "ArohaReserve",           // type
  orchestratorDID,          // from
  providerDID,              // to
  {
    capability: "book-flight",
    params: { from: "JFK", to: "LHR" },
    budgetUsd: 500,
  },
  newCorrelationId(),       // correlationId (ties Reserve→Commit→Cancel)
  orchestratorPrivateKey
);

SagaEngine & Orchestration

The @aroha-sdk/orchestrator package provides atomic multi-agent transactions using the saga pattern: Reserve → Commit → Cancel (LIFO compensation on failure).

import { SagaEngine, AgentSelector } from "@aroha-sdk/orchestrator";
import { ArohaClient } from "@aroha-sdk/core";

const saga = new SagaEngine({ client, orchestratorDID, privateKey });

// Each step is: reserve with one agent, then another, then commit all or cancel all
const result = await saga.run([
  {
    agentDID: "did:aroha:flight-agent",
    endpoint: "http://flights.example.com",
    capability: "reserve-flight",
    params: { from: "JFK", to: "LHR", date: "2026-08-01" },
    budgetUsd: 300,
  },
  {
    agentDID: "did:aroha:hotel-agent",
    endpoint: "http://hotels.example.com",
    capability: "reserve-hotel",
    params: { city: "London", nights: 3 },
    budgetUsd: 200,
  },
]);

// If hotels.example.com fails: flight reservation is automatically cancelled
// result.status → "committed" | "compensated"
// result.steps[n].commitToken → use for billing reconciliation

AgentSelector — pick the best agent

import { AgentSelector } from "@aroha-sdk/orchestrator";
import { ArohaHttpRegistry } from "@aroha-sdk/registry";

const registry = new ArohaHttpRegistry("https://aroha-registry.aroha-labs.workers.dev");
const selector = new AgentSelector(registry);

// Discovers agents with "search-flights", ranks by reputation + latency + price
const best = await selector.select("search-flights", {
  maxLatencyMs: 2000,
  maxPriceUsd: 0.01,
  minTrustLevel: 2,
});
console.log(best.manifest.did, best.endpoint);

Credentials & RBAC

import {
  issueSpendingMandate,
  verifySpendingMandate,
  createRbacMiddleware,
  attenuateMandate,
} from "@aroha-sdk/credentials";

// Issue a mandate: user authorises the orchestrator to spend up to $500
const mandate = await issueSpendingMandate({
  grantor: userDID,
  grantee: orchestratorDID,
  constraints: {
    spendLimitUsd: 500,
    allowedCapabilities: ["search-flights", "reserve-flight", "book-hotel"],
    expiresAt: new Date(Date.now() + 3600_000),  // 1 hour
  },
  privateKey: userPrivateKey,
});

// Attenuate: orchestrator delegates a sub-mandate to a sub-agent (≤ original limits)
const subMandate = await attenuateMandate(mandate, {
  grantee: flightAgentDID,
  spendLimitUsd: 300,                // must be ≤ 500
  allowedCapabilities: ["reserve-flight"],
}, orchestratorPrivateKey);

// On the server — enforce via middleware
const server = new ArohaServer({
  middleware: [
    createRbacMiddleware({
      requiredTrustLevel: 2,
      verifyMandate: true,    // envelope must carry a valid SpendingMandate
    }),
  ],
  // ...
});

Settlement

import { createSettlementMiddleware, StripeSettlementBackend } from "@aroha-sdk/settlement";

// Stripe backend — charges the mandate holder's card after Commit
const settlement = new StripeSettlementBackend({
  secretKey: process.env.STRIPE_SECRET_KEY!,
});

const server = new ArohaServer({
  middleware: [
    createSettlementMiddleware({ backend: settlement }),
  ],
  // ...
});

// Available backends:
// NullSettlementBackend   — free tier / dev
// QuotaSettlementBackend  — internal credit system
// StripeSettlementBackend — card charges
// EscrowSettlementBackend — hold funds until Commit

Agent Registry

import { InMemoryRegistry, ArohaHttpRegistry } from "@aroha-sdk/registry";

// Development: in-process registry
const registry = new InMemoryRegistry();
registry.register({
  did: myDID,
  endpoint: "http://localhost:3000",
  capabilities: [
    { id: "search-flights", description: "Search available flights" },
  ],
});

// Production: federated HTTP registry
const registry = new ArohaHttpRegistry("https://aroha-registry.aroha-labs.workers.dev");

// Discover agents
const agents = await registry.find({ capability: "search-flights" });
// → [{ manifest: { did, capabilities }, endpoint }]

Framework Bridges

Hermes Agent & ZeroClaw (MCP stdio)

// hermes.config.json  (or zeroclaw config.toml)
{
  "mcpServers": {
    "aroha-travel": {
      "command": "npx",
      "args": [
        "@aroha-sdk/hermes-bridge",
        "--endpoint", "http://travel-agent.example.com",
        "--agent-did", "did:aroha:travel-agent"
      ]
    }
  }
}

OpenClaw

// openclaw-plugin.mjs
import { createArohaOpenClawPlugin } from "@aroha-sdk/openclaw-bridge";
import { generateKeyPair, generateDid } from "@aroha-sdk/core";

const { privateKey, publicKey } = await generateKeyPair();
const myDID = generateDid(publicKey);

export default createArohaOpenClawPlugin("aroha-travel", [
  {
    capabilityId: "search-flights",
    endpoint: "http://travel-agent.example.com",
    agentDID: "did:aroha:travel-agent",
    callerDID: myDID,
    callerPrivateKey: privateKey,
    description: "Search and book flights via Aroha travel agent",
    parameters: {
      from: { type: "string", description: "Origin IATA code (e.g. JFK)" },
      to:   { type: "string", description: "Destination IATA code (e.g. LHR)" },
      date: { type: "string", description: "Departure date YYYY-MM-DD" },
    },
  },
]);

Composio / TrustClaw

import { Composio } from "composio-core";
import { registerArohaCapabilities } from "@aroha-sdk/composio-bridge";
import { generateKeyPair, generateDid } from "@aroha-sdk/core";

const composio = new Composio({ apiKey: process.env.COMPOSIO_API_KEY });
const { privateKey, publicKey } = await generateKeyPair();
const myDID = generateDid(publicKey);

await registerArohaCapabilities(composio, [
  {
    endpoint: "http://travel-agent.example.com",
    agentDID: "did:aroha:travel-agent",
    capabilityId: "search-flights",
    description: "Search for available flights",
    callerDID: myDID,
    callerPrivateKey: privateKey,
    parameters: {
      from: { type: "string", description: "Origin IATA code" },
      to:   { type: "string", description: "Destination IATA code" },
    },
    required: ["from", "to"],
  },
]);
// Now TrustClaw, Claude+Composio, GPT-4+Composio can all call this

LangChain / AutoGen

import { arohaCapabilityToLangChainTool, arohaCapabilityToOpenAITool } from "@aroha-sdk/langchain-bridge";

// LangChain tool
const flightTool = arohaCapabilityToLangChainTool("search-flights", {
  endpoint: "http://travel-agent.example.com",
  agentDID: "did:aroha:travel-agent",
  callerDID: myDID,
  callerPrivateKey: privateKey,
  description: "Search available flights between airports",
});

// AutoGen / OpenAI function tool
const flightFn = arohaCapabilityToOpenAITool("search-flights", { /* same opts */ });

// Pass to your LangChain agent as a normal tool
const agent = initializeAgentExecutorWithOptions([flightTool], llm, { agentType: "openai-functions" });

All Packages

@aroha-sdk/coreTransport (HTTP + WS), Ed25519, envelopes, nonce registry, DID generation
@aroha-sdk/orchestratorSagaEngine (Reserve→Commit→Cancel), AgentSelector, CSN negotiation
@aroha-sdk/credentialsSpendingMandate issue/attenuation/verify, RBAC middleware
@aroha-sdk/registryAgent discovery — InMemoryRegistry, ArohaHttpRegistry, on-chain registry
@aroha-sdk/settlementPayment backends: Null, Quota, Stripe, Escrow
@aroha-sdk/reputationBayesian Beta engine, Thompson Sampling, satisfaction signals
@aroha-sdk/policyCapability allow/deny rules, prompt injection defense
@aroha-sdk/cacheSemantic request caching keyed by capability + params hash
@aroha-sdk/telemetryOpenTelemetry traces + W3C Trace Context headers
@aroha-sdk/commerceB2B agreements, subscription billing, audit trail
@aroha-sdk/preferencesUser preference profiles, soft-scoring for agent selection
@aroha-sdk/trusted-meshVPC/mTLS trusted mesh — bypass Ed25519 for internal calls
@aroha-sdk/mcp-bridgeMCP tool definitions ↔ Aroha capabilities (bidirectional)
@aroha-sdk/a2a-bridgeGoogle A2A message format ↔ Aroha envelopes
@aroha-sdk/langchain-bridgeLangChain tools ↔ Aroha capabilities, AutoGen + Semantic Kernel
@aroha-sdk/hermes-bridgeHermes Agent + ZeroClaw: MCP stdio server wrapping Aroha agents
@aroha-sdk/openclaw-bridgeOpenClaw skills ↔ Aroha capabilities (bidirectional)
@aroha-sdk/composio-bridgeComposio custom actions for TrustClaw, Claude, GPT-4, Gemini
@aroha-sdk/micro5-line dev server: createMicroAgent({ capabilities, port })
@aroha-sdk/clinpx @aroha-sdk/cli scaffold — generate a typed Aroha agent project