Anorion: The Open-Source Agent Gateway That Treats Agents as First-Class Citizens
How we built an API-first orchestration layer where every button is an API call, agents are first-class consumers, and the control plane is infrastructure — not a dashboard
The only study in this domain so far.
All of InfrastructureSummary
How we built an API-first orchestration layer where every button is an API call, agents are first-class consumers, and the control plane is infrastructure — not a dashboard
- Published
- Filed under
- Infrastructure
- Reading time
- 14 min read
- Sections
- 15
- Research by
- Venym Labs Research
Published by Venym Labs. Methods and limits are in the study; it has not been peer reviewed.
Anorion: The Open-Source Agent Gateway That Treats Agents as First-Class Citizens#
How we built an API-first orchestration layer where every button is an API call, agents are first-class consumers, and the control plane is infrastructure — not a dashboard#
Every AI agent framework in production today has the same fundamental flaw: the control plane was designed for humans.
LangChain gives you chains but no runtime. AutoGen gives you multi-agent chat but no deployment story. CrewAI gives you roles but scales like a science project. OpenClaw gives you a mature single-process gateway but zero horizontal scaling. Hermes gives you a brilliant Python agent loop but locks you into a synchronous world where agents are second-class API citizens.
At Venym Labs, we ran headfirst into this wall. Our trading agents need to spawn sub-agents, our research agents need to query each other's sessions, and our operations team needs to monitor everything without SSH-ing into a server. No existing framework gave us all three. So we built Anorion.
Anorion is an open-source, JavaScript-native agent gateway with an API-first control plane. It runs on Bun, speaks Hono, streams via WebSocket, and federates across multiple gateways for horizontal scaling. The web UI is just one client — agents consume the exact same API endpoints, with the exact same authentication, in parallel, at machine speed.
This is the architecture we needed. It didn't exist. So we built it.
The Landscape: Why Everything Falls Short#
Before diving into architecture, here's the honest assessment of what exists:
| Framework | Language | Control Plane | Agent-as-Consumer | Horizontal Scale | Real-Time Streaming |
|---|---|---|---|---|---|
| OpenClaw | TypeScript | WebSocket (human) | ❌ No | ❌ Single process | ✅ WS protocol |
| Hermes | Python | CLI + ACP | ⚠️ Limited | ❌ Single process | ⚠️ Tool callbacks |
| LangChain | Python | None | ❌ No | ❌ No runtime | ❌ No |
| AutoGen | Python | None | ❌ No | ❌ No | ❌ No |
| CrewAI | Python | None | ❌ No | ❌ No | ❌ No |
| Anorion | TypeScript | REST + WS API | ✅ First-class | ✅ Federation | ✅ Throughout |
OpenClaw came closest — it's mature, supports 15+ channels, and has a rich plugin system. But it's a monolithic single process with no horizontal scaling path. If the gateway crashes, every agent, every channel, every cron job dies. Hermes has a brilliant agent runtime with parallel tool execution and shared iteration budgets, but it's Python-only, synchronous at its core, and its "API" is a CLI wrapper.
What we needed didn't exist: a JS-native gateway where agents are API consumers, not just API servers.
Architecture: Lazy Agents on a Fast Runtime#
The core architectural decision in Anorion is lazy instantiation. Agents are configs on disk. They don't consume memory, CPU, or tokens until a message actually arrives. This means you can define 500 agents in YAML — only the ones actively processing messages exist in memory.
The Tech Stack#
| Layer | Choice | Why |
|---|---|---|
| Runtime | Bun | 3-5x faster startup, built-in SQLite/TypeScript/fetch |
| HTTP | Hono | Ultra-fast, works on Bun/Node/Deno, built-in WebSocket |
| LLM | Vercel AI SDK | Unified interface for all providers with streaming + tool calling |
| Database | Drizzle ORM + SQLite | Type-safe, zero-dependency single-instance, swap to Postgres later |
| Queue | BullMQ + Redis | Async task processing with retries and dead-letter queues |
| Logging | pino | Fastest structured logger for Node/Bun |
Why Bun Was the Right Call#
We evaluated Node 22 and Deno seriously. Bun won for three reasons:
-
Cold start speed — Spawning a sub-agent means creating a new agent instance. On Bun, that's ~50ms. On Node with ts-node, it's 500ms+. When agents spawn children in parallel, this compounds.
-
Built-in SQLite —
bun:sqlitegives us a zero-dependency database for single-instance deployments. No Postgres setup, no Docker Compose, no connection pooling. Just write files. -
Native TypeScript — No compilation step, no tsconfig gymnastics, no build pipeline for the gateway itself. The agent runtime, tools, and gateway all run TypeScript directly.
Agent Configuration#
Every agent is a YAML file. Here's a real example from our codebase:
# agents/trading-agent.yaml
name: Vex Capital Trading Agent
model:
model: anthropic/claude-sonnet-4-20250514
fallbacks:
- model: openai/gpt-4o
retryOn: [429, 503]
params:
temperature: 0.3
maxTokens: 8192
skills:
- perps-trader
- coingecko
tools:
- system.file.read
- system.file.write
- external.web.search
memory:
shortTerm:
maxMessages: 60
longTerm:
directory: ./memory/trading/
provider: sqlite
permissions:
allow: [trading.*, external.api.*, system.file.*]
deny: [channel.*.send, agent.spawn]
maxIterations: 20
timeoutMs: 60_000
Notice what's missing: no server configuration, no database URLs, no deployment manifests. The agent config is just the agent. Infrastructure concerns live in anorion.yaml separately.
Per-Agent Tool Binding: Security Through Isolation#
This is one of the most important design decisions in Anorion. Tools are registered globally in a central registry, but each agent only sees the tools explicitly bound to it. The trading agent sees hyperliquid and coingecko. The research agent sees web-search and web-fetch. Neither can see the other's tools.
// Global registration — tool exists in the system
toolRegistry.register({
name: 'trading.perps.open_position',
description: 'Open a new position on Hyperliquid',
schema: { /* JSON Schema */ },
category: 'external',
permissions: ['agent:trader'],
});
// Agent config specifies which tools it gets
// tools: [trading.perps.open_position, trading.perps.get_positions]
// The agent's model function-calling schema ONLY includes these tools
// It literally cannot call tools it doesn't know about
This is more than access control — it's context window optimization. If an agent has 30 registered tools, the model's function-calling schema includes all 30 in every request. That's thousands of tokens wasted on tools the agent will never use. Per-agent binding means the trading agent's schema only has 5-8 tools, not 30+.
Tool Execution Sandbox#
Every tool runs with timeout enforcement and output size limits:
async function executeInSandbox(
fn: ToolHandler,
params: any,
opts: SandboxOptions,
): Promise<ToolResult> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 30_000);
try {
const result = await fn(params, { signal: controller.signal });
if (result.content.length > (opts.maxOutputBytes ?? 1_000_000)) {
result.content = result.content.slice(0, opts.maxOutputBytes) + '\n[TRUNCATED]';
}
return result;
} catch (err) {
if (err.name === 'AbortError') {
return { content: '', error: `Tool timed out after ${opts.timeoutMs}ms` };
}
return { content: '', error: err.message };
} finally {
clearTimeout(timeout);
}
}
No runaway tool calls. No infinite loops. No agents that hang forever because a web fetch decided to take 5 minutes.
The Skill System: SKILL.md as Portable Agent Intelligence#
We stole the best idea from both OpenClaw and Hermes: skills as markdown instructions, not code. A skill is a SKILL.md file bundled with optional tool implementations. When an agent loads a skill, the SKILL.md content is injected into its system prompt. Zero code required for most skills.
skills/
├── perps-trader/
│ ├── SKILL.md # Agent-readable instructions
│ ├── config.yaml # Dependencies, permissions, context budget
│ ├── references/ # Detailed docs for on-demand reading
│ │ └── api-reference.md
│ └── tools/ # Optional tool implementations
│ └── open-position.ts
├── deep-research/
│ ├── SKILL.md
│ └── config.yaml
└── content-creator/
├── SKILL.md
└── config.yaml
Here's what a SKILL.md looks like:
---
name: perps-trader
version: 2.0.0
dependencies: [coingecko]
---
# Perpetual Futures Trading
Trade perpetual futures on Hyperliquid.
## Available Tools
- `trading.perps.open_position` — open a new position
- `trading.perps.close_position` — close existing position
- `trading.perps.get_positions` — list open positions
## Risk Rules
- Max 5% of portfolio per position
- Always set stop-loss
- Check funding rates before opening shorts
The skill config controls context budget — how many tokens of SKILL.md to inject — so large skills don't eat the agent's context window:
# skills/perps-trader/config.yaml
name: perps-trader
tools:
- name: trading.perps.open_position
source: ./tools/open-position.ts
dependencies: [coingecko]
permissions: [external.api.call, system.env.read]
contextBudget: 2000 # max 2000 tokens of SKILL.md injected
This is the pattern that makes agent capabilities portable. A skill is a folder. Drop it in skills/, reference it in your agent config, and the agent knows how to trade, research, or create content — without writing a single line of glue code.
Sub-Agent Spawning: Parallelism with Guardrails#
Agents can spawn sub-agents for task delegation. But we learned from OpenClaw and Hermes that unconstrained spawning creates chaos. Anorion enforces strict isolation:
const subagent = await agentRuntime.spawn({
parentId: agent.id,
task: "Analyze this codebase and report issues",
model: "openai/gpt-4o-mini", // cheaper model for sub-tasks
tools: ['file-system', 'git'], // explicit subset only
maxTurns: 10, // hard iteration limit
timeout: 300_000, // 5 minute wall-clock limit
});
The Guardrails#
const BLOCKED_TOOLS_IN_CHILDREN = new Set([
'agent.spawn', // no recursive spawning
'agent.delegate', // no further delegation
'memory.write', // children can't write shared memory
'channel.message.send', // no direct messaging
]);
const MAX_DEPTH = 2; // root → child → grandchild (blocked)
const MAX_CONCURRENT = 4; // parallel children per agent
Children get isolated memory namespaces but can optionally read from the parent's context. They cannot write to shared memory or send messages to channels. The parent only sees the final summary result — intermediate tool calls are invisible. This prevents context bloat and keeps the parent's conversation clean.
The fan-out pattern is where this gets powerful:
// Parent spawns 3 parallel researchers
const handles = await Promise.all([
agent.spawn({ goal: 'Search the web for: ' + query, tools: ['web.search', 'web.fetch'] }),
agent.spawn({ goal: 'Find academic papers about: ' + query, tools: ['web.search', 'web.fetch'] }),
agent.spawn({ goal: 'Check social sentiment on: ' + query, tools: ['web.search'] }),
]);
const results = await Promise.all(handles.map(h => h.wait(60_000)));
// All three run in parallel, results stream back via WebSocket
API-First Control Plane: Every Button Is an API Call#
This is the architectural principle that separates Anorion from everything else. The UI is a thin visualization layer over a REST + WebSocket API. There are zero human-only workflows. If a human can click it in the dashboard, an agent can call it programmatically.
Every button in the UI maps 1:1 to an HTTP endpoint:
# Agent Management
POST /api/v1/agents Create agent
GET /api/v1/agents List agents
PATCH /api/v1/agents/:id Update config
DELETE /api/v1/agents/:id Destroy agent
POST /api/v1/agents/:id/spawn Spawn sub-agent
# Messaging
POST /api/v1/agents/:id/messages Send message to agent
GET /api/v1/sessions/:id Get session with full history
WS /api/v1/sessions/:id/stream Live message stream
# Session Control
POST /api/v1/sessions/:id/steer Inject steering instruction
POST /api/v1/sessions/:id/fork Branch session from current point
POST /api/v1/sessions/:id/compact Trigger context compression
# Tools & Skills
GET /api/v1/tools List registered tools
POST /api/v1/agents/:id/tools/:name Bind tool to agent
POST /api/v1/skills/:name/install Install skill to agent
# System
GET /api/v1/gateway/status Health check
GET /api/v1/gateway/metrics Prometheus metrics
GET /api/v1/bridge/peers List connected gateways
Why This Matters: Agents Controlling Agents#
Here's the killer pattern — an agent using the API to manage other agents:
// Vex (trading agent) spawns a research sub-agent via the API
const response = await fetch('http://localhost:4250/api/v1/agents/trader/spawn', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ANORION_API_KEY}`,
'Content-Type': 'application/json',
'X-Agent-Id': 'trader',
},
body: JSON.stringify({
goal: 'Research ETH staking yields across Lido, Rocket Pool, and Frax',
tools: ['web-search', 'web-fetch', 'file-read'],
maxIterations: 30,
timeoutMs: 300_000,
}),
});
An agent can create new agents, read other agents' sessions, install skills, manage cron jobs, and monitor gateway health — all through the same API a human uses. The only difference is the X-Agent-Id header. This makes agent-to-agent coordination a first-class pattern, not a hack.
WebSocket Streaming for Real-Time Everything#
The WebSocket API provides push-based updates for everything:
// Connect and subscribe
const ws = new WebSocket('ws://localhost:4250/api/v1/ws?token=API_KEY');
ws.send(JSON.stringify({ type: 'subscribe', agents: ['trader', 'coder'] }));
// Receive events in real-time
ws.onmessage = (e) => {
const event = JSON.parse(e.data);
// event.type: "session.message.delta" → streaming tokens
// event.type: "session.tool_call" → tool execution started
// event.type: "session.tool_result" → tool execution completed
// event.type: "agent.spawned" → sub-agent created
// event.type: "system.alert" → token budget warning
};
The control plane UI connects to this same WebSocket. When Vex executes a trade, the session inspector updates token-by-token in real-time — not after the next REST poll.
Multi-Gateway Federation: Horizontal Scaling from Day One#
This is where Anorion diverges most sharply from OpenClaw and Hermes. Both are single-process systems with no horizontal scaling path. Anorion gateways connect peer-to-peer over mTLS WebSocket connections and federate agent discovery, message routing, and capacity management.
Topology: Hybrid Mesh + Hub-Spoke#
- Within a region: Full mesh (3-5 gateways, all connected to each other)
- Cross-region: Hub-spoke through a regional relay gateway
- Default for <10 gateways: Full mesh everywhere (no relay needed)
The Bridge Protocol#
Gateways communicate over mTLS WebSocket with a simple message protocol:
interface BridgeMessage {
version: 1;
type: 'ping' | 'pong' | 'forward' | 'registry_sync' | 'handoff' | 'capacity';
sourceGateway: string;
targetGateway?: string;
timestamp: number;
payload: any;
}
Each gateway publishes its agent registry, channel bindings, and capacity signals to peers. When Gateway A receives a message for an agent hosted on Gateway B, it forwards via the bridge — the user never knows the agent isn't local.
Agent Handoff#
When a gateway needs maintenance or hits capacity, agents can be handed off to peers:
1. Source gateway serializes agent state (config + memory + session)
2. Sends handoff message to target gateway
3. Target recreates agent, resumes session
4. Channels stay on original gateway (acts as proxy)
5. Future messages route to new gateway automatically
What Makes Anorion Different#
Here's the direct comparison against the frameworks we studied and used:
| Feature | Anorion | OpenClaw | Hermes | LangChain | CrewAI |
|---|---|---|---|---|---|
| Language | TypeScript | TypeScript | Python | Python | Python |
| Runtime | Bun | Node | CPython | N/A | N/A |
| Agent-as-API-consumer | ✅ First-class | ❌ Human-only | ⚠️ CLI only | ❌ None | ❌ None |
| Horizontal scaling | ✅ Federation | ❌ Single proc | ❌ Single proc | ❌ None | ❌ None |
| Real-time control | ✅ REST + WS | ✅ WS only | ⚠️ Callbacks | ❌ None | ❌ None |
| Lazy instantiation | ✅ On-demand | ⚠️ All loaded | ⚠️ All loaded | N/A | N/A |
| Sub-agent isolation | ✅ Sandboxed | ⚠️ Shared proc | ✅ Thread pool | ❌ None | ❌ None |
| Per-agent tools | ✅ Bound | ✅ Profiles | ✅ Toolsets | ✅ Per chain | ✅ Per crew |
| Skill system | ✅ SKILL.md | ✅ SKILL.md | ✅ Skills | ❌ None | ❌ None |
| Config format | YAML | JSON5 | YAML | Python | Python |
| Open source | ✅ MIT | MIT | Apache 2.0 | MIT | MIT |
| Streaming | ✅ Throughout | ✅ WS | ⚠️ Partial | ❌ None | ❌ None |
| Metrics/Observability | ✅ Prometheus | ❌ None | ❌ None | ❌ None | ❌ None |
Session Lifecycle: Smart Resource Management#
Anorion doesn't keep agents alive forever. Sessions follow a deliberate lifecycle:
Context Compaction#
When a session approaches 60% of the model's context window, Anorion automatically compresses older messages using a cheaper model (like gemini-2.0-flash for summarization). It protects the first 3 turns (identity setup) and the last 4 turns (recent context), and summarizes everything in between.
// Before compaction: 80,000 tokens
// After compaction: 32,000 tokens
// Saved: 48,000 tokens of dead context
// Cost: $0.001 for the summarization call
This is borrowed from Hermes's context compressor pattern, but executed asynchronously in the background so it never blocks the agent's response.
What's Next for Anorion#
Anorion is open source and under active development. Here's the roadmap:
Near-Term (Now)#
- Gateway core — Hono server, agent registry, inference loop with Vercel AI SDK, WebSocket streaming, SQLite persistence
- Tool execution — Shell, HTTP, file system, web search with sandbox enforcement
- Channel adapters — Telegram and Discord as first-class channels
Mid-Term#
- Multi-gateway bridge — mTLS federation, agent handoff, capacity-based routing
- Control plane UI — Session inspector, agent config editor (Monaco), topology map, command palette
- Sub-agent spawning — Fan-out delegation with isolation guardrails
Longer-Term#
- Plugin system — Sandboxed plugin execution with manifest-based validation
- Vector memory search — Optional pgvector integration for semantic memory retrieval
- MCP protocol support — Model Context Protocol as a first-class tool category
- Self-healing — Agents that detect their own degradation and request restarts via the API
The Bigger Picture#
Agent infrastructure is where web infrastructure was in 2009. We have frameworks for building single agents, but no mature infrastructure for running fleets of agents that coordinate, scale, and self-manage.
The control plane should not be a dashboard. It should be an API. Agents should be able to create other agents, read each other's sessions, install capabilities, and respond to system alerts — the same way microservices discover each other and coordinate through APIs. That's the pattern Anorion encodes.
We're building this in the open because agent infrastructure is a horizontal problem. Every team building autonomous systems will hit the same wall we did. The question isn't whether we need agent gateways — it's whether those gateways treat agents as first-class citizens or as passive chatbots waiting for human input.
Anorion is our answer. It's open source, it's TypeScript, and it's designed for the world where agents are the primary consumers of infrastructure.
Key Takeaways#
-
Existing agent frameworks treat agents as passive chatbots — They're designed for human-driven conversations, not machine-to-machine coordination. Anorion treats agents as first-class API consumers that can manage each other, spawn children, and coordinate through the same endpoints humans use.
-
Lazy instantiation enables unlimited agents — Agent configs live on disk and only consume resources when processing messages. You can define 500 agents in YAML; only the active ones exist in memory. This is the key to scaling without Kubernetes.
-
Per-agent tool binding is both security and performance — Agents only see their assigned tools, which prevents privilege escalation and reduces token waste on irrelevant function schemas. A trading agent with 5 tools has a much leaner context than one with 30+ tools globally available.
-
The control plane must be an API, not a dashboard — Every UI action maps 1:1 to an HTTP endpoint. Agents use the same API keys, same endpoints, same WebSocket streams. This enables agent-to-agent coordination as a first-class pattern, not a bolted-on hack.
-
Multi-gateway federation solves the horizontal scaling problem — OpenClaw and Hermes are single-process systems with no scaling path. Anorion's mTLS bridge protocol enables full-mesh within regions and hub-spoke across regions, with agent handoff for maintenance and load balancing.
Venym Labs Research — Published 2026-03-15