Skip to content
Research · Infrastructure

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

14 min readVenym Labs Research
Study record/Infrastructure
Published

Summary

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
Research is not peer reviewedvenym.io/research/anorion-the-open-source-agent-gateway

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:

FrameworkLanguageControl PlaneAgent-as-ConsumerHorizontal ScaleReal-Time Streaming
OpenClawTypeScriptWebSocket (human)❌ No❌ Single process✅ WS protocol
HermesPythonCLI + ACP⚠️ Limited❌ Single process⚠️ Tool callbacks
LangChainPythonNone❌ No❌ No runtime❌ No
AutoGenPythonNone❌ No❌ No❌ No
CrewAIPythonNone❌ No❌ No❌ No
AnorionTypeScriptREST + 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#

LayerChoiceWhy
RuntimeBun3-5x faster startup, built-in SQLite/TypeScript/fetch
HTTPHonoUltra-fast, works on Bun/Node/Deno, built-in WebSocket
LLMVercel AI SDKUnified interface for all providers with streaming + tool calling
DatabaseDrizzle ORM + SQLiteType-safe, zero-dependency single-instance, swap to Postgres later
QueueBullMQ + RedisAsync task processing with retries and dead-letter queues
LoggingpinoFastest structured logger for Node/Bun

Why Bun Was the Right Call#

We evaluated Node 22 and Deno seriously. Bun won for three reasons:

  1. 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.

  2. Built-in SQLite — bun:sqlite gives us a zero-dependency database for single-instance deployments. No Postgres setup, no Docker Compose, no connection pooling. Just write files.

  3. 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:

yaml
# 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.

typescript
// 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:

typescript
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.

text
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:

markdown
---
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:

yaml
# 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:

typescript
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#

typescript
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:

typescript
// 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:

text
# 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:

typescript
// 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:

typescript
// 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:

typescript
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:

text
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:

FeatureAnorionOpenClawHermesLangChainCrewAI
LanguageTypeScriptTypeScriptPythonPythonPython
RuntimeBunNodeCPythonN/AN/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 loadedN/AN/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 formatYAMLJSON5YAMLPythonPython
Open source✅ MITMITApache 2.0MITMIT
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.

typescript
// 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#

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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

Venym Labs ResearchReturn to the archive

From research to a working system

A better question. A tested answer.

Bring the system, the constraint or the question. We will work out what the evidence needs to show.

New message
ToVconnect@venym.io
SubjectResearch-backed system inquiry
Opens in your mail appSend the question