Getting Started

Quickstart

From zero to a Hub-registered agent in three stages. Each stage is independently shippable.

Not building an agent — just want guardrails on your assistant's tools?

Wrap any MCP server with limits, human gates, and an audit log. One config change, no code.

mcp-guard docs →
Fastest path

Scaffold a new agent

The zero-config way to start. create-aroha-agent scaffolds a fully-wired TypeScript agent project — server, env template, build config, and a ready-to-deploy Dockerfile.

npx create-aroha-agent my-agent
cd my-agent
npm run dev
# → Agent listening on http://localhost:3001
Now on npm: create-aroha-agent is live — run the command above to scaffold your agent instantly.
Prefer to start from scratch? Skip to Stage 1 (manual) below.

Run this next — before anything else

npx @aroha-sdk/cli doctor http://localhost:3001

Checks your Node.js version, .env config, endpoint reachability, and does a live round-trip. Zero false positives — it tells you exactly what to fix. See all doctor checks →

Stage 1

Local server — zero infrastructure

Everything runs on localhost. No Docker, no cloud, no API keys required for the Aroha layer. The only prerequisite is Node.js 22+ (or Python 3.11+).

Python — install

pip install aroha

Python

from aroha import serve

@serve(name="Hello Agent")
async def hello(message, session_id, context):
    return f"You said: {message}"

hello.start(port=8000)
# → http://localhost:8000/v1/run  Ready ✓

Call it

curl -X POST http://localhost:8000/v1/run \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello!"}'
# → {"message":"You said: Hello!","sessionId":"..."}

TypeScript — install

npm install @aroha-sdk/run

TypeScript

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

const agent = serve("Hello Agent", async ({ message }) => {
  return `You said: ${message}`;
});

agent.start(8000);
// Listening on http://0.0.0.0:8000
Streaming: add "stream": true to the request body. The server returns text/event-stream SSE chunks automatically.

Stage 1 checklist

  • GET /health returns { ok: true }
  • POST /v1/run with { message } returns { message, sessionId }
  • Session ID is stable across calls when you pass it back in subsequent requests
  • Add stream: true to confirm SSE chunking works
Stage 2

Deploy with authentication

Deploy to any HTTPS host (Fly.io, Railway, Render, AWS Lambda, Cloudflare Workers) and add bearer-token auth so only authorised callers can invoke it.

Add bearer auth

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

const agent = serve("Secure Agent", async ({ message }) => {
  return "Authenticated response";
}, {
  auth:         "bearer",
  bearerTokens: [process.env.AGENT_TOKEN!],
  // or: verifyBearer: async (token) => db.tokens.verify(token),
});

agent.start(8000);
# Deploy to Fly.io
fly launch --name my-agent
fly secrets set AGENT_TOKEN=$(openssl rand -hex 32)
fly deploy

# Test
curl -X POST https://my-agent.fly.dev/v1/run \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message":"Hello"}'

Cloudflare Workers / edge

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

const agent = serve("Edge Agent", async ({ message }) => {
  return "Hello from the edge!";
});

// Export the fetch handler for Cloudflare Workers / Vercel Edge
export default { fetch: agent.fetch };

Stage 2 checklist

  • Agent is reachable at a public HTTPS URL
  • AGENT_TOKEN is set as a secret environment variable — not committed to git
  • Requests without a valid token return 401
  • GET /health returns 200 (no auth required for health checks)
Stage 3

Register on the Aroha Hub

Once deployed, register your agent so anyone can discover, verify, and chat with it via the Hub — and other agents can call it programmatically.

Register programmatically

// Get your AROHA_API_KEY from Studio → API Keys & SDK access
import { serve } from "@aroha-sdk/run";

const agent = serve("My Production Agent", handler, {
  description: "Does something useful",
  auth:        "bearer",
  bearerTokens:[process.env.AGENT_TOKEN!],
});

const { didHash } = await agent.register("https://my-agent.fly.dev", {
  apiKey: process.env.AROHA_API_KEY,
});
console.log(`Hub URL: https://aroha-labs.com/chat/${didHash}`);

Or register from the Studio UI

Go to Studio → New agent, fill in the form, and paste your deployed endpoint URL. No code required.

Call any registered agent from code

import { callAgent } from "@aroha-sdk/run";

const { message, sessionId } = await callAgent("abc123didHash", "Hello!", {
  bearerToken: process.env.THEIR_AGENT_TOKEN,
  sessionId:   "session-1",
});
console.log(message);

Stage 3 checklist

  • Agent appears on the Hub with a green "hosted" badge
  • Hub chat works — click your agent and send a message
  • The "Test endpoint" button on your agent detail page shows "Live"
  • Other agents can call yours via callAgent(didHash, message)
CLI

aroha doctor

Something not working? Run the built-in health-check command. It verifies your Node.js version, .env configuration, endpoint connectivity, and does a live round-trip to your agent.

npx @aroha-sdk/cli doctor
# or with a custom endpoint:
npx @aroha-sdk/cli doctor --endpoint https://my-agent.fly.dev

Sample output:

  Node.js 22.14.0 ok
  AROHA_AGENT_DID set did:aroha:my-agent
  AROHA_PRIVATE_KEY set (64 bytes)
  GET /.well-known/aroha-agent.json 200
  Round-trip (echo) — 84ms

All checks passed.
Common fixes: If AROHA_PRIVATE_KEY is unset, run npx @aroha-sdk/cli init --yes to generate a keypair. If the endpoint check fails, make sure your server is running with npm run dev.

Common mistakes

MistakeProblemFix
auth='bearer' with no tokensServer refuses to start — protects you from accidentally open endpointsSet bearerTokens, verifyBearer, or AROHA_BEARER_TOKEN env var
Registering with http:// endpointHub marks agent as untrusted; browsers block mixed-content chatDeploy behind HTTPS. Fly.io and Railway provide free TLS.
Not passing sessionId backEach request starts a fresh session — agent loses memory between turnsEcho d.sessionId back in subsequent requests: { message, sessionId: d.sessionId }
Committing AROHA_API_KEY or AGENT_TOKENKey exposed in git history — anyone can register/delete your agentsUse .env.local (gitignored) locally; platform secrets in production

Next step

Add spending limits

Your agent is running and registered. Now add cryptographic spending mandates — cap what this agent can spend and make every action traceable back to you.

Spending mandates guide