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.
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:3001create-aroha-agent is live — run the command above to scaffold your agent instantly.Run this next — before anything else
npx @aroha-sdk/cli doctor http://localhost:3001Checks 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 →
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 arohaPython
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/runTypeScript
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"stream": true to the request body. The server returns text/event-stream SSE chunks automatically.Stage 1 checklist
- ✓
GET /healthreturns{ ok: true } - ✓
POST /v1/runwith{ message }returns{ message, sessionId } - ✓Session ID is stable across calls when you pass it back in subsequent requests
- ✓Add
stream: trueto confirm SSE chunking works
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_TOKENis set as a secret environment variable — not committed to git - ✓Requests without a valid token return
401 - ✓
GET /healthreturns 200 (no auth required for health checks)
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
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.devSample 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.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
| Mistake | Problem | Fix |
|---|---|---|
| auth='bearer' with no tokens | Server refuses to start — protects you from accidentally open endpoints | Set bearerTokens, verifyBearer, or AROHA_BEARER_TOKEN env var |
| Registering with http:// endpoint | Hub marks agent as untrusted; browsers block mixed-content chat | Deploy behind HTTPS. Fly.io and Railway provide free TLS. |
| Not passing sessionId back | Each request starts a fresh session — agent loses memory between turns | Echo d.sessionId back in subsequent requests: { message, sessionId: d.sessionId } |
| Committing AROHA_API_KEY or AGENT_TOKEN | Key exposed in git history — anyone can register/delete your agents | Use .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