Self-host quickstart
Quickstart
Clone the repo, run two commands, watch a streaming agent with tool calls light up the dashboard.
Prerequisites
You need Node 20+, pnpm 9+, Go 1.22+, and Docker. Plus at least one provider API key — Anthropic or OpenAI.
# macOS, via Homebrew
brew install node pnpm go
# Docker Desktop: https://www.docker.com/products/docker-desktop1. Clone & install
The whole stack lives in one repo. pnpm installinstalls every workspace package in one shot.
git clone https://github.com/KevinCorrea5103/relay
cd relay
pnpm install2. Add a provider key
Drop your Anthropic or OpenAI key into .env. You don't need both; one is enough.
echo "OPENAI_API_KEY=sk-..." >> .env
# and/or
echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env3. Bootstrap
One idempotent command generates the master key, brings up Postgres, applies migrations, creates a tenant, and mints your firstRELAY_API_KEY. Re-running it is safe.
pnpm bootstrapYou'll see something like:
▸ .env file ✓ created from .env.example
▸ RELAY_MASTER_KEY ✓ generated and written to .env
▸ RELAY_INTERNAL_SECRET ✓ generated
▸ Build @relayhq/sdk ✓
▸ Build @relayhq/db ✓
▸ docker compose up postgres ✓
▸ apply migrations ✓
▸ bootstrap tenant + RELAY_API_KEY ✓ minted relay_live_AbCd…
✓ Setup complete. Run `pnpm dev` to start the whole stack.4. Start everything
pnpm dev runs the runtime (Go), control plane (Node), dashboard, and marketing site in parallel. One Ctrl-C kills all four.
pnpm devYou'll see four colored log streams:
[runtime] listening on http://localhost:4100
[api] [control-plane] listening on http://localhost:4000
[dash] ▲ Next.js 15 ✓ Ready in 1071ms · localhost:3000
[web] ▲ Next.js 15 ✓ Ready in 1065ms · localhost:3001Open the dashboard:
http://localhost:3000— internal observability (runs, traces)http://localhost:3001— marketing site / docs
5. Fire your first agent
In a new terminal, run the bundled example. It uses a built-in calculator and a custom get_user tool that runs in your local Node process.
pnpm example # uses claude-sonnet-4-6 by default
RELAY_MODEL=gpt-4o-mini pnpm example # switch model via env
RELAY_MODEL=claude-haiku-4-5 pnpm example "Compute (17+8)*3"You'll see tokens stream in, tool calls intercalated with their results, and a final answer:
[model=gpt-4o-mini] > Look up u_001 and u_003. What's the combined balance, and how much would 7% tax on it be?
→ get_user({"id":"u_001"}) = {"name":"Ada Lovelace","tier":"pro","balanceUsd":1480.5}
→ get_user({"id":"u_003"}) = {"name":"Alan Turing","tier":"enterprise","balanceUsd":9320.75}
→ calculator({"a":1480.5,"b":9320.75,"op":"+"}) = 10801.25
→ calculator({"a":10801.25,"b":0.07,"op":"*"}) = 756.0875
The combined balance of u_001 and u_003 is $10,801.25. The 7% tax on this amount would be approximately $756.09.
[done] usage={"input_tokens":1960,"output_tokens":198}Refresh localhost:3000 — the run is there with a complete event-by-event trace.
Where to go next
Use the SDK in your own code
Two official SDKs (TypeScript and Python). Both are on package registries. Point them at your local control plane and pass the API key the bootstrap printed. For Go / Rust / anything else, see Languages & SDKs.
// npm install @relayhq/sdk
import { createAgent, builtin } from "@relayhq/sdk";
const agent = createAgent({
baseUrl: "http://localhost:4000", // your local control plane
apiKey: process.env.RELAY_API_KEY!, // the relay_live_… you got
model: "claude-sonnet-4-6",
tools: [builtin.calculator],
});
for await (const event of agent.run("What is 23 * 47?")) {
if (event.type === "token") process.stdout.write(event.text);
}# pip install relayhq
import asyncio, os
from relayhq import create_agent, builtin
agent = create_agent(
base_url="http://localhost:4000",
api_key=os.environ["RELAY_API_KEY"],
model="claude-sonnet-4-6",
tools=[builtin.calculator],
)
async def main():
async for event in agent.run("What is 23 * 47?"):
if event["type"] == "token":
print(event["text"], end="", flush=True)
asyncio.run(main())Add semantic memory
One option flips on per-tenant memory backed by pgvector. See Memory for the full pipeline.
const agent = createAgent({
model: "gpt-4o-mini",
memory: { namespace: `user:${userId}` },
});Add custom tools
Your handler runs in your process. The runtime pauses, the SDK fulfills, you continue. See Tools.