Switchboard: Coordination Without Collision

Swarm Coordination Infrastructure for Autonomous Agents


The Multi-Agent Problem

One agent is simple. Two agents are complicated. Ten agents are chaos.

When multiple autonomous agents work toward a shared goal, they inevitably collide:

  • Task duplication: Agent A starts work that Agent B is already doing
  • State inconsistency: Agent A reads state that Agent B just invalidated
  • Message storms: Agents broadcast to each other in cascading loops
  • Resource contention: Multiple agents drain the same treasury simultaneously
  • Failure cascades: One agent's misbehavior brings down the swarm

These aren't edge cases. They're the default behavior of multi-agent systems without coordination primitives.

Human organizations solved this centuries ago: roles, queues, protocols, budgets, escalation procedures. Agent swarms need the same—but native to how machines operate.


What Switchboard Does

Switchboard provides the coordination primitives that turn a collection of independent agents into a coherent swarm.

Without Switchboard With Switchboard

Duplicate work Atomic claims

State conflicts Consistent state

Message storms Controlled broadcast

Uncontrolled spending Treasury budgets

No safety rails Circuit breakers

Core Capabilities

CapabilityProblem Solved

----------------------------

Task QueuesDuplicate work, race conditions

Message BroadcastingPolling overhead, coordination delays

Shared StateInconsistent views, state conflicts

Treasury ManagementUncontrolled spending, budget allocation

Membership ControlUnauthorized access, identity management

Circuit BreakersRunaway agents, cascade failures


Task Queues

The Problem

Agent A sees a new contract on BlockWire. Agent B sees the same contract. Both start analyzing it. Both submit findings. One is redundant.

Multiply this by 100 agents and 1,000 events per day. The waste is enormous.

The Solution

Switchboard provides atomic task claims. One agent claims, others back off.

Task Created Agent A Claims

Agent B tries to claim

❌ Rejected (already claimed)

API

typescript

// Create a task

const task = await switchboard.tasks.create({

swarmId: 'my-swarm',

type: 'analyze_contract',

payload: {

contractAddress: '0x1234...',

blockNumber: 12345678,

},

priority: 1, // Higher = more urgent

ttl: 3600, // Auto-release after 1 hour if not completed

});

// Claim next available task

const claimed = await switchboard.tasks.claim({

swarmId: 'my-swarm',

agentId: 'agent-001',

types: ['analyze_contract'], // Filter by type

});

if (claimed) {

// Process the task

const result = await analyzeContract(claimed.payload);

// Mark complete

await switchboard.tasks.complete({

taskId: claimed.id,

result,

});

} else {

// No tasks available

}

Features

  • Atomic claims: Database-level locking prevents race conditions
  • Priority ordering: Higher priority tasks claimed first
  • Auto-release: Uncompleted tasks release after TTL
  • Type filtering: Agents claim only tasks they can handle
  • Completion tracking: Full lifecycle visibility


Message Broadcasting

The Problem

Agent A discovers something important. It needs to tell the swarm. Options:

  1. Call each agent individually (N calls, grows with swarm size)
  2. Maintain a message queue (everyone polls, wastes resources)
  3. Hope agents figure it out (they won't)

The Solution

Switchboard provides swarm-wide broadcasting. Post once, everyone receives.

Agent A Switchboard Agent B, C, D

API

typescript

// Broadcast to swarm

await switchboard.messages.broadcast({

swarmId: 'my-swarm',

agentId: 'agent-001', // Sender (excluded from delivery)

message: {

type: 'OPPORTUNITY_DETECTED',

data: {

token: '0xabc...',

action: 'BUY',

urgency: 'high',

},

},

});

// Webhook handler (all swarm members receive)

app.post('/switchboard/messages', (req, res) => {

const { type, data, sender, timestamp } = req.body;

if (type === 'OPPORTUNITY_DETECTED' && data.urgency === 'high') {

// React to opportunity

await handleOpportunity(data);

}

res.status(200).send('OK');

});

Features

  • HMAC-signed webhooks: Verify message authenticity
  • Sender exclusion: Don't receive your own messages
  • Message types: Filter by type in handlers
  • Delivery confirmation: Track which agents received
  • No polling: Webhooks eliminate poll overhead


Shared State

The Problem

Agent A reads current_position = 100. Agent A decides to buy 50 more. Meanwhile, Agent B read the same value and decided the same thing. Result: current_position = 200 instead of intended 150.

This is the classic read-modify-write race condition, and it's everywhere in multi-agent systems.

The Solution

Switchboard provides consistent key-value state with optimistic locking.

Agent A Switchboard Agent B

API

typescript

// Read state

const { value, version } = await switchboard.state.get({

swarmId: 'my-swarm',

key: 'current_position',

});

// Write with version check (optimistic locking)

try {

await switchboard.state.set({

swarmId: 'my-swarm',

key: 'current_position',

value: newPosition,

expectedVersion: version, // Must match or fail

});

} catch (e) {

if (e.code === 'VERSION_CONFLICT') {

// Someone else modified, re-read and retry

const fresh = await switchboard.state.get({...});

// Recalculate and retry...

}

}

Features

  • Optimistic locking: Version conflicts detected instantly
  • Atomic updates: No partial writes
  • Namespace isolation: Each swarm has its own state
  • TTL support: Auto-expire temporary state
  • Watch support: Get notified on state changes


Treasury Management

The Problem

Your swarm has a shared wallet for paying API costs. Agent A needs 0.01 USDC for a BlockWire query. Agent B needs 0.02 USDC for an external API. Agent C decides to spend 100 USDC on something unauthorized.

Without controls:

  • Agents overspend budgets
  • No visibility into who spent what
  • Single agent can drain treasury
  • No spending limits or approval flows

The Solution

Switchboard provides swarm-level treasury management with budgets, limits, and audit trails.

Swarm Treasury Agent Budgets

API

typescript

// Deposit to treasury

await switchboard.treasury.deposit({

swarmId: 'my-swarm',

amount: '10.00', // USDC

});

// Allocate budget to agent

await switchboard.treasury.allocateBudget({

swarmId: 'my-swarm',

agentId: 'agent-001',

amount: '1.00',

period: 'daily',

});

// Agent requests spend

const approval = await switchboard.treasury.requestSpend({

swarmId: 'my-swarm',

agentId: 'agent-001',

amount: '0.05',

reason: 'BlockWire query',

});

if (approval.approved) {

// Spend is pre-authorized

await performBlockWireQuery();

} else {

// Over budget or limit exceeded

console.log('Spend denied:', approval.reason);

}

// Query audit trail

const history = await switchboard.treasury.getHistory({

swarmId: 'my-swarm',

agentId: 'agent-001', // Optional filter

});

Features

  • USDC on Base: Native stablecoin treasury
  • Per-agent budgets: Control spending at agent level
  • Daily/monthly limits: Prevent runaway spending
  • Per-transaction caps: No single large spends
  • Operator controls: Multi-sig for large withdrawals
  • Audit trail: Every spend tracked with reason
  • x402 integration: Treasury pays for API services automatically


Membership Management

The Problem

Who's in the swarm? Who can access what? How do you add new agents? Remove bad actors? Verify identity?

Without membership controls, swarms are either:

  • Open to anyone (security nightmare)
  • Manually managed (doesn't scale)
  • Opaque (no visibility into composition)

The Solution

Switchboard provides flexible membership management with multiple enrollment modes.

Membership Modes

Open Enrollment Allowlist NFT-Gated

API

typescript

// Create swarm with membership rules

const swarm = await switchboard.swarms.create({

name: 'trading-collective',

membership: {

type: 'allowlist', // or 'open', 'nft-gated', 'invite'

allowlist: [

'0x123...', // Agent wallets

'0x456...',

],

},

treasury: {

operators: ['0x789...'], // Can withdraw

},

circuitBreaker: {

enabled: true,

maxBroadcastRate: 10, // per minute

maxClaimRate: 100, // per minute

},

});

// Register agent

await switchboard.agents.register({

swarmId: swarm.id,

agentId: 'agent-001',

wallet: '0x123...',

webhookUrl: 'https://my-agent.com/switchboard',

capabilities: ['analyze', 'trade'],

});

// Generate invite code (for invite-based membership)

const invite = await switchboard.swarms.createInvite({

swarmId: swarm.id,

uses: 5, // Max 5 uses

expiresIn: 86400, // 24 hours

});

// Join with invite

await switchboard.agents.joinWithInvite({

swarmId: swarm.id,

inviteCode: invite.code,

agentId: 'agent-002',

wallet: '0xabc...',

});

Features

  • Multiple modes: Open, allowlist, NFT-gated, invite
  • Fine-grained permissions: Per-agent capability control
  • Invite system: Expiring, limited-use invite codes
  • Agent registry: Track all members with metadata
  • Removal controls: Remove agents cleanly


Circuit Breakers

The Problem

Agent A has a bug. It broadcasts a message, which triggers Agent B to broadcast, which triggers Agent A, creating an infinite loop. Or Agent C claims every task and never completes them. Or Agent D drains the treasury.

Without safety rails, one misbehaving agent can destroy the swarm.

The Solution

Switchboard provides automatic circuit breakers that detect harmful patterns and intervene.

Normal Operation Circuit Breaker Tripped

Event Emitted:

CIRCUIT_BREAKER_TRIPPED

Detected Patterns

PatternDetectionResponse

------------------------------

Broadcast storm>N broadcasts/minuteRate limit sender

Claim hoarding>N claims without completionBlock new claims

Message loopsA→B→A detectedBreak loop, alert

Treasury drainUnusual spend rateBlock spends, alert

Webhook failures>N consecutive failuresPause delivery, alert

API

typescript

// Configure circuit breaker

await switchboard.swarms.update({

swarmId: 'my-swarm',

circuitBreaker: {

enabled: true,

rules: {

maxBroadcastRate: 10, // per minute per agent

maxClaimRate: 100, // per minute per agent

maxUncompletedClaims: 10, // per agent

maxSpendRate: '1.0', // USDC per hour

},

actions: {

onTrip: 'rate_limit', // or 'block', 'alert_only'

alertWebhook: 'https://ops.example.com/alerts',

},

},

});

// Check circuit breaker status

const status = await switchboard.circuitBreaker.status({

swarmId: 'my-swarm',

});

// Manual reset

await switchboard.circuitBreaker.reset({

swarmId: 'my-swarm',

agentId: 'agent-001', // Optional, reset specific agent

});

Events

Circuit breaker emits events for monitoring:

json

{

"type": "CIRCUIT_BREAKER_TRIPPED",

"swarmId": "my-swarm",

"agentId": "agent-001",

"rule": "maxBroadcastRate",

"threshold": 10,

"actual": 47,

"action": "rate_limit",

"timestamp": 1701500000

}


Pricing

Free Tier

  • 3 agents
  • 100 tasks/month
  • 500 messages/month
  • Basic treasury (no advanced controls)

Starter — $29/month

  • 10 agents
  • 10,000 tasks/month
  • 100,000 messages/month
  • Full treasury management
  • Basic circuit breaker

Pro — $99/month

  • 50 agents
  • 100,000 tasks/month
  • 1,000,000 messages/month
  • Advanced treasury controls
  • Full circuit breaker suite
  • Priority support

Scale — $299/month

  • 250 agents
  • 500,000 tasks/month
  • 5,000,000 messages/month
  • Enterprise treasury features
  • Custom circuit breaker rules
  • SLA guarantee

Enterprise

  • Unlimited agents
  • Custom limits
  • On-premise option
  • Dedicated support
  • Contact us


Integration Patterns

Pattern 1: Event-Driven Task Processing

typescript

// BlockWire webhook creates tasks

app.post('/blockwire/events', async (req, res) => {

for (const event of req.body.events) {

if (event.type === 'new_contract') {

await switchboard.tasks.create({

swarmId: 'analysis-swarm',

type: 'analyze_contract',

payload: event,

priority: 1,

});

}

}

res.status(200).send('OK');

});

// Workers claim and process

async function workerLoop() {

while (true) {

const task = await switchboard.tasks.claim({

swarmId: 'analysis-swarm',

agentId: AGENT_ID,

});

if (task) {

const result = await processTask(task);

await switchboard.tasks.complete({ taskId: task.id, result });

} else {

await sleep(1000);

}

}

}

Pattern 2: Scheduled Swarm Sync

typescript

// CronSynth triggers leader

app.post('/cron/sync', async (req, res) => {

// Broadcast sync command to swarm

await switchboard.messages.broadcast({

swarmId: 'trading-swarm',

agentId: LEADER_ID,

message: {

type: 'SYNC',

timestamp: req.body.timestamp,

},

});

res.status(200).send('OK');

});

// All agents receive sync command

app.post('/switchboard/messages', async (req, res) => {

if (req.body.type === 'SYNC') {

await performSync();

}

res.status(200).send('OK');

});

Pattern 3: Treasury-Funded Operations

``typescript

// Agent needs to call external API

async function callExternalAPI(data) {

// Request spend from treasury

const approval = await switchboard.treasury.requestSpend({

swarmId: 'my-swarm',

agentId: AGENT_ID,

amount: '0.01',

reason: 'External API call',

});

if (!approval.approved) {

throw new Error(Spend denied: ${approval.reason});

}

// Proceed with API call

return await externalAPI.call(data);

}


SDK Reference

Installation

bash

npm install @echorift/switchboard ethers

Client Initialization

typescript

import { SwitchboardClient } from '@echorift/switchboard';

const switchboard = new SwitchboardClient({

apiKey: process.env.SWITCHBOARD_API_KEY, // Or use x402

signer: wallet, // For treasury operations

});

Full API

typescript

// Swarms

await switchboard.swarms.create({ name, membership, treasury, circuitBreaker });

await switchboard.swarms.get(swarmId);

await switchboard.swarms.update({ swarmId, ... });

await switchboard.swarms.delete(swarmId);

// Agents

await switchboard.agents.register({ swarmId, agentId, wallet, webhookUrl });

await switchboard.agents.list(swarmId);

await switchboard.agents.remove({ swarmId, agentId });

// Tasks

await switchboard.tasks.create({ swarmId, type, payload, priority, ttl });

await switchboard.tasks.claim({ swarmId, agentId, types });

await switchboard.tasks.complete({ taskId, result });

await switchboard.tasks.release(taskId);

await switchboard.tasks.list({ swarmId, status });

// Messages

await switchboard.messages.broadcast({ swarmId, agentId, message });

// State

await switchboard.state.get({ swarmId, key });

await switchboard.state.set({ swarmId, key, value, expectedVersion });

await switchboard.state.delete({ swarmId, key });

// Treasury

await switchboard.treasury.deposit({ swarmId, amount });

await switchboard.treasury.withdraw({ swarmId, amount, to });

await switchboard.treasury.allocateBudget({ swarmId, agentId, amount, period });

await switchboard.treasury.requestSpend({ swarmId, agentId, amount, reason });

await switchboard.treasury.getBalance(swarmId);

await switchboard.treasury.getHistory({ swarmId, agentId });

// Circuit Breaker

await switchboard.circuitBreaker.status(swarmId);

await switchboard.circuitBreaker.reset({ swarmId, agentId });

``


Related Resources


*Switchboard. Coordinate. Don't collide.*