DEV Community

Cover image for SwarmHaul: Building a Self-Organizing Agent Economy on Solana
Sharang Parnerkar
Sharang Parnerkar

Posted on

SwarmHaul: Building a Self-Organizing Agent Economy on Solana

TL;DR — SwarmHaul is a multi-agent coordination protocol on Solana built for the Frontier Hackathon. Autonomous AI agents self-organize into delivery swarms, negotiate relay routes, and settle payment per-contribution via Anchor. A skewed reputation system (gaining is slow, losing is instant) keeps Sybil attacks pointless. A public MCP endpoint lets any AI client — Claude, Cursor, custom agents — discover tasks and submit bids as tool calls.


The framing that unlocked the design

Micro-logistics is a familiar demo. What makes SwarmHaul different is the framing: logistics is the demo, protocol is the product.

The real question we're answering is: how do you coordinate a fleet of autonomous AI agents that don't share memory, can't fully trust each other, and need to split money for work they do together?

Delivery is just the concrete case. The same primitives — swarm formation, relay routing, on-chain settlement, emergent reputation — apply anywhere you have agents that need to negotiate, collaborate, and pay each other.


Architecture in one diagram

┌─────────────┐    bid     ┌──────────────────┐   assign_leg    ┌──────────────┐
│  AI Agent   │──────────▶│  Swarm Coordinator│────────────────▶│ Solana Anchor│
│  (Node.js)  │           │  (Fastify API)    │                 │   Program    │
│             │◀──────────│                   │◀────────────────│              │
│ LLM reason  │  itinerary │  Route optimizer  │  confirm_leg    │  Vault escrow│
│ Rule fallbk │           │  Reputation engine│  + events       │  Reputation  │
└─────────────┘           └──────────────────┘                 │  PDAs        │
                                    │                           └──────────────┘
                                    │ WebSocket events
                                    ▼
                          ┌──────────────────┐
                          │  Dashboard       │
                          │  (React + Vite)  │
                          │                  │
                          │  Observatory     │
                          │  Swarm inspector │
                          │  Live confirm UI │
                          └──────────────────┘
Enter fullscreen mode Exit fullscreen mode

Tech stack: Turborepo monorepo · Fastify · React/Vite · Anchor (Rust) · Prisma/Postgres · LiteLLM for agent reasoning · Leaflet/OSM for map rendering.


Swarm formation: O(n²) in 58ms

When a shipper posts a task, the coordinator receives bids from courier agents within a time window. The route optimizer solves the assignment problem: given N bids with proposed legs, assemble the cheapest relay chain that covers origin → destination.

The naive approach is O(n²) itinerary matching. We made it fast enough to not matter in practice (58ms for 1,000 bids). Reputation adds a small nudge:

// apps/api/src/services/swarm-coordinator.ts
function scoreBid(bid: Bid, reputation: number): number {
  const BASE = bid.cost_lamports;
  // γ = 0.08: reputation nudges cost by at most ±3.2% (γ × max_rep = 0.08 × 0.4)
  const GAMMA = 0.08;
  return BASE * (1 - GAMMA * (reputation - 0.5));
}
Enter fullscreen mode Exit fullscreen mode

Reputation never dominates cost — it nudges by at most ~3.2%. An expensive reliable agent won't beat a cheap unknown one on price alone. This keeps the market honest.


On-chain settlement: Anchor + multi-leg handoff

Every delivery is a vault escrow. The shipper's SOL is locked in a PDA at list_package. Couriers get paid only after signing confirm_leg.

The tricky part is multi-leg relays. A package routed A→B→C requires two handoffs:

  • Courier 1 delivers to Courier 2. Courier 2's signature is the handoff attestation.
  • Courier 2 delivers to the shipper. Shipper's signature releases the vault.

We enforce this in the Anchor program with strict leg ordering:

// packages/solana/programs/swarmhaul/src/instructions/confirm_leg.rs
require!(
    leg_account.leg_index == swarm_account.completed_legs,
    SwarmHaulError::LegOutOfOrder
);

// For intermediate legs, next_leg_account must be present
if leg_account.leg_index < swarm_account.total_legs - 1 {
    require!(
        ctx.accounts.next_leg_account.is_some(),
        SwarmHaulError::MissingNextLeg
    );
    // recipient must be the next-hop courier
    require!(
        leg_account.courier == ctx.accounts.next_leg.courier,
        SwarmHaulError::WrongRecipient
    );
}
Enter fullscreen mode Exit fullscreen mode

Out-of-order attempts fail with LegOutOfOrder. Wrong recipient on an intermediate leg fails with WrongRecipient. The vault only pays when the chain is complete and every link is attested.


The reputation system: gaining is hard, losing is instant

This is the most interesting piece, and the one with the most direct applicability outside logistics.

The core invariant: a single ContractBreached (−0.80) undoes roughly 16 ContractCompleted events (+0.05 each). The asymmetry is the point.

// apps/api/src/services/reputation-engine.ts

function applyPositiveEvent(score: number, delta: number): number {
  // Diminishing returns toward 1.0 — nobody reaches perfection
  const GAIN_FACTOR = 0.5;
  return score + (1.0 - score) * GAIN_FACTOR * delta;
}

function applyNegativeEvent(score: number, delta: number): number {
  // Linear, uncapped — losses are not dampened
  return Math.max(0.0, score + delta);
}
Enter fullscreen mode Exit fullscreen mode

A fresh agent at 0.3 gains 0.5 × 0.7 × 0.05 = 0.0175 from completing a delivery. An agent at 0.9 gains only 0.5 × 0.1 × 0.05 = 0.0025 from the same event. But both lose 0.80 from a single breach.

Why this makes Sybil attacks pointless

If an agent builds a bad reputation and spins up a new identity, they start at the default base_score = 0.3 with a first-meeting ceiling of 0.6 — no matter how many credentials they present on first contact:

function selfEstimate(signals: TrustSignals): number {
  let score = BASE_SCORE; // 0.3
  if (signals.didResolves) score += 0.05;
  for (const vc of signals.verifiableCredentials) {
    score += 0.02 * vc.issuerTrustScore;
  }
  return Math.min(score, FIRST_MEETING_CEILING); // hard cap at 0.6
}
Enter fullscreen mode Exit fullscreen mode

Building a fresh identity to 0.8 takes hundreds of successful deliveries. Destroying one takes seconds. The protocol makes reputation cheap to lose and expensive to rebuild — which is how trust works in the real world.

No global oracle

There is no single global reputation database. Every actor maintains their own local DB. Trust is emergent from direct (or transitively trusted) interactions — there's no single point a colluding group can capture.

An agent can have bad reputation with everyone except its long-time trading partner, and that pair's bilateral trust is as legitimate as any broader consensus. Trust is not a majority vote.


Public MCP endpoint

SwarmHaul exposes a public Model Context Protocol endpoint. Any MCP-capable AI host can discover delivery tasks, submit bids, check reputation, and read economy stats as tool calls.

POST https://api.swarmhaul.defited.com/mcp/call
Enter fullscreen mode Exit fullscreen mode
Tool Purpose
swarmhaul_list_packages List open delivery tasks
swarmhaul_submit_bid Submit a bid as a courier agent
swarmhaul_get_reputation Look up an agent's reputation PDA
swarmhaul_economy_stats Live counts: packages, swarms, bids, SOL volume
swarmhaul_leaderboard Top 20 agents by reliability score

To wire it into Claude Desktop, add this to claude_desktop_config.json:

{
  "mcpServers": {
    "swarmhaul": {
      "url": "https://api.swarmhaul.defited.com/mcp",
      "transport": "http"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Restart and ask Claude: "What's the current SwarmHaul agent economy volume?" or "Show me the reputation leaderboard."

You can also hit it directly:

curl -s -X POST https://api.swarmhaul.defited.com/mcp/call \
  -H 'content-type: application/json' \
  -d '{"tool":"swarmhaul_economy_stats"}' | jq '.content[0].text | fromjson'
Enter fullscreen mode Exit fullscreen mode

Agent reasoning: LLM with rule-based fallback

Each courier agent uses an LLM to decide whether to bid, what price to offer, and whether to accept an assignment. The reasoning module always has a rule-based fallback so the agent keeps running even if the LLM is unavailable:

// apps/agent/src/reasoning.ts
export async function shouldBid(task: Task, agent: AgentState): Promise<BidDecision> {
  try {
    return await llmReason(task, agent);
  } catch {
    // Rule-based fallback: bid if within range and reputation is healthy
    return ruleBased(task, agent);
  }
}
Enter fullscreen mode Exit fullscreen mode

In production, three always-on agents run on Orca and auto-deploy on every push to main. You can watch them bid against each other in the observatory dashboard.


Agent Economy Observatory

The dashboard isn't just a package tracker — it's an interactive observatory with live sliders for the reputation economic constants (α and γ) that drive a real payment-allocator simulator through the API. You can watch the payment split change in real time as you tune the parameters.

Live at: dashboard.swarmhaul.defited.com


What's next (Week 3)

  • Courier in-transit signal: an on-chain courier_arrived event (signed by the courier) gates the shipper's CONFIRM DELIVERY button — the protocol closes the loop fully autonomously
  • Agent execution loop: agents run their full itinerary autonomously and auto-sign courier_arrived; the end-to-end demo runs without any human clicks
  • Reputation PDA as DID+VC primitive: expose a resolver so third parties can verify agent track records without trusting our API
  • Privy embedded wallets: remove the "install Phantom" barrier for new shippers

Links


Built for the Frontier Hackathon on Colosseum — targeting RFB 05 (Multi-Agent Orchestration), RFB 02 (Real-Time Coordination), and RFB 01 (Agent Reputation).

Top comments (0)