Examples

Five complete programs in increasing depth. Each is runnable exactly as shown — the network examples hit live agents, no account required.

01

Call a live Hub agent

30 seconds

No install, no API key, no account. The sandbox flight agent is live on the Hub right now — this is a real network call.

curl -X POST https://www.aroha-labs.com/api/sandbox/flight \
  -H "Content-Type: application/json" \
  -d '{
    "type": "ArohaRequest",
    "from": "did:aroha:you",
    "to": "did:aroha:sandbox:flight",
    "correlationId": "example-01",
    "body": {
      "capability": "search-flights",
      "params": { "origin": "AKL", "destination": "SYD", "date": "2026-08-15" }
    }
  }'

What happens: You get back a signed ArohaResponse envelope containing three mock flights. Change the capability to book-flight to see saga-style booking.

02

Serve your own agent

2 minutes

The zero-ceremony path: one function becomes an Aroha-compatible HTTP agent with health checks and a discovery manifest.

npm install @aroha-sdk/run
import { serve } from "@aroha-sdk/run";

const agent = serve("Translator", async ({ message }) => {
  // Your logic here — call an LLM, a database, anything.
  return `Translated: ${message}`;
});

agent.start(8000);
// POST http://localhost:8000/v1/run          { "message": "hello" }
// GET  http://localhost:8000/health
// GET  http://localhost:8000/.well-known/aroha-agent.json

What happens: A running agent with the three standard endpoints. Deploy it anywhere that runs Node — or export agent.fetch for Cloudflare Workers and Vercel Edge.

03

Discover an agent, then call it

5 minutes

The core Hub loop: query the registry by capability, pick the best agent by reputation, send it a request envelope.

npm install undici   # or use built-in fetch on Node 22+
const REGISTRY = "https://aroha-registry.aroha-labs.workers.dev";

// 1. Discover agents that can search flights
const res = await fetch(`${REGISTRY}/v1/agents?capabilities=search-flights`);
const agents = await res.json();

// 2. Pick the highest-reputation active agent
const best = agents
  .filter((a) => a.active && a.endpointUrl)
  .sort((a, b) => b.reputationScore - a.reputationScore)[0];

console.log(`Calling ${best.did} (reputation ${best.reputationScore})`);

// 3. Call it with an ArohaRequest envelope
const reply = await fetch(best.endpointUrl, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    type: "ArohaRequest",
    from: "did:aroha:my-orchestrator",
    to: best.did,
    correlationId: crypto.randomUUID(),
    body: { capability: "search-flights", params: { origin: "AKL", destination: "NRT" } },
  }),
});

console.log(await reply.json());

What happens: Runs against the live registry and live agents — the same discover → select → call loop the MCP server's aroha_discover and aroha_call tools perform.

04

Issue and enforce a spending mandate

10 minutes

The heart of the protocol: a human authorizes $500, the orchestrator attenuates $300 to a flight agent, and an over-limit attenuation fails cryptographically.

npm install @aroha-sdk/credentials @noble/ed25519 @noble/hashes
import * as ed from "@noble/ed25519";
import { sha512 } from "@noble/hashes/sha512";
import {
  issueIntentMandate,
  attenuateToPayment,
  verifyMandate,
} from "@aroha-sdk/credentials";

ed.etc.sha512Sync = (...m) => sha512(...m); // Node < 20 shim

// Keypairs: one per participant
const humanPriv = ed.utils.randomPrivateKey();
const humanPub  = await ed.getPublicKeyAsync(humanPriv);
const orchPriv  = ed.utils.randomPrivateKey();

const humanDID  = "did:aroha:human:alice";
const orchDID   = "did:aroha:agent:orchestrator";
const flightDID = "did:aroha:agent:flight-vendor";

// 1. Human → orchestrator: $500 envelope, 1 hour
const { mandate } = await issueIntentMandate(
  humanDID, orchDID,
  { spendLimitUsd: 500, allowedActions: ["book-flights"] },
  humanPriv,
  3_600_000,
);

// 2. Orchestrator → flight agent: attenuate DOWN to $300
const child = await attenuateToPayment(
  mandate, flightDID,
  { spendLimitUsd: 300 },
  orchPriv,
);

// 3. Flight agent verifies before acting
const { valid, mandate: decoded } = await verifyMandate(
  child.token, humanPub, flightDID,
);
console.log(valid);                                   // true
console.log(decoded.constraints.spendLimitUsd);       // 300

// 4. Attenuating UP fails — this throws:
await attenuateToPayment(mandate, flightDID, { spendLimitUsd: 900 }, orchPriv)
  .catch((e) => console.log("Blocked:", e.message)); // exceeds parent limit

What happens: Step 4 is the whole point: no bug, prompt injection, or malicious sub-agent can widen its own authority. The math says no.

05

Delegated research with compute limits and a human gate

15 minutes

A task mandate that lets a research agent search and summarise — but caps its compute, blocks irreversible actions, and pauses for approval before anything is sent.

npm install @aroha-sdk/credentials @noble/ed25519 @noble/hashes
import * as ed from "@noble/ed25519";
import { sha512 } from "@noble/hashes/sha512";
import {
  issueTaskMandate,
  verifyTaskMandate,
  TaskConstraintViolation,
} from "@aroha-sdk/credentials";

ed.etc.sha512Sync = (...m) => sha512(...m);

const myPriv = ed.utils.randomPrivateKey();
const myPub  = await ed.getPublicKeyAsync(myPriv);
const myDID     = "did:aroha:human:me";
const agentDID  = "did:aroha:vendor:researcher";

// Research within hard limits; email requires my approval
const { token } = await issueTaskMandate(
  myDID, agentDID,
  ["research", "web-search", "summarise", "send-email"],
  {
    reversibleOnly: false,
    humanGates: ["send-email"],          // pause before sending anything
    actionLimits: { "web-search": 20 },  // at most 20 searches
    computeLimit: { llmTokens: 50_000, webRequests: 30 },
  },
  myPriv,
  30 * 60_000,                            // 30-minute TTL
);

// On the agent side: verify, then check each action against constraints
const { valid, mandate } = await verifyTaskMandate(token, myPub, agentDID);
console.log(valid); // true

// The 21st web-search throws TaskConstraintViolation
// with .code === "ACTION_LIMIT_EXCEEDED".
// send-email throws with .code === "HUMAN_GATE_REQUIRED" —
// surface it to the user and resume after approval.

What happens: The agent works freely inside the envelope and hits hard walls at the edges. Every action lands in a TaskReceipt for audit. See the error reference for every violation code.