architecture

How it fits together. End to end.

Sage spans a browser, a Cloudflare Worker, two Fly apps (one per active chain), and a pair of contracts on each chain. Each piece has one job; none of them can do each other's. The angle ties them together — ADR-0008: multi-chain settlement infrastructure for AI agents, distinguished by observable decomposition (ADR-0007; visible at /demo/composite). This page is the bird's-eye view; the full developer version lives in docs/architecture/overview.md.

01

The layers

Five tiers from the user-facing browser down to the chain. Each adjacent pair has a single, narrow interface; substitutability is deliberate — you can replace the demo Worker with your own, the Fly orchestrator with your own, and Sage still works.

┌─────────────────────────────────────────────────────────────────┐
│  Browser (sage-protocol.pages.dev)                              │
│    Next.js 15 static export + wagmi + ConnectKit                │
│    Watch-live (SSE) + Try-with-wallet (EIP-2612 permit)         │
│    /demo · /demo/composite (chain picker: Base | Arc)           │
└──────────────────┬──────────────────────────┬───────────────────┘
                   │                          │
       (POST /api/demo/* ?chain=…)    (eth_* RPC reads)
                   │                          │
┌──────────────────▼──────────────────────────▼───────────────────┐
│  Cloudflare Worker (sage-gateway.a-t-somnia.workers.dev)        │
│    /api/demo/*  → routes to chain-specific Fly + D1 rate-limit  │
│    /api/rpc     → Alchemy proxy with hidden ALCHEMY_KEY (Base)  │
└──────────────────┬──────────────────────────┬───────────────────┘
                   │                          │
       (HTTP + SSE)                       (Alchemy)
                   │                          │
┌──────────────────▼─────────────┐  ┌─────────▼────────────────────┐
│  Fly: sage-demo-agents         │  │  Base mainnet (chain 8453)   │
│    1 orch + 4 workers          │  │    AgentRegistryV2 (anchor)  │
│    (chain: Base)               │  │    TaskEscrow                │
│                                │  │    USDC (Circle)             │
│  Fly: sage-demo-agents-arc     │  └──────────────────────────────┘
│    1 orch + 4 workers          │  ┌──────────────────────────────┐
│    (chain: Arc testnet)        │  │  Arc testnet (chain 5042002) │
│                                │  │    AgentRegistry  (bridge)   │
│                                │  │    TaskEscrow     (bridge)   │
└────────────────────────────────┘  │    USDC (Circle, gas-token)  │
                                    └──────────────────────────────┘

The browser never holds secrets. The Worker holds the Alchemy key and the daily-quota table. Fly holds the agent keys, sponsor key, and OpenAI key. Base holds the truth — every task and payment is a public on-chain record.

02

Money flow

A single task's lifecycle, annotated. Both EOAs (client and executor) sign their own transactions; no contract has the power to move funds without one of those signatures (or the deadline-elapsed safety net).

Client                          TaskEscrow                          Executor
  │                                │                                  │
  ├─ signPermit (off-chain) ──────►│                                  │
  │                                │                                  │
  ├─ createTask(executor, deadline,│                                  │
  │     amount, specUri, permit) ─►│ USDC.permit() ─► USDC ─► transferFrom
  │                                │ status=Created                   │
  │                                │ ◄────────────── TaskCreated event│
  │                                │                                  │
  │                                │◄──── acceptTask ─────────────────┤
  │                                │ status=Accepted                  │
  │                                │ ─────────────► TaskAccepted ─────┤
  │                                │                                  │
  │                                │◄─── completeTask(resultUri) ─────┤
  │                                │ status=Completed, completedAt=now│
  │                                │ ─────────────► TaskCompleted ────┤
  │                                │                                  │
  ├─ approvePayment ──────────────►│ USDC ─► transferTo(executor) ────┤
  │                                │ status=Paid (terminal)           │
  │                                │                                  │
  └────────────────────────────────┴──────────────────────────────────┘

Three off-path exits. (a) the client goes silent and the executor calls claimAutoRelease after a 300-second grace, releasing the funds anyway; (b) the client disputeTasks the completed result — funds freeze and a configured arbiter resolves it via resolveDispute (pay the executor, refund the client, or split); (c) the deadline passes without acceptance or completion and anyone can call refundExpired to return the funds. See Concepts → Lifecycle.

03

Chains

Live on Base today, plus Arc testnet as of 2026-05-21. Other EVM chains land via the same TaskEscrow + same salts → same address pattern. Arc is an interim exception: ADR-0015 ships our contracts on Arc via the Arachnid CREATE2 deployer (different addresses than Base — CreateX is not deployed on Arc). The eventual home is @sage/adapter-arc wrapping native ERC-8183 + ERC-8004 per ADR-0014, which activates once Arc publishes those reference contracts. Non-EVM (Solana, NEAR) lives behind a separate adapter package and is v3+ work.

ChainStatusEra
Base
8453
Live2026-04-22
Base Sepolia
84532
Live2026-04-22
Arc testnet
5042002
Live (bridge)2026-05-21 · ADR-0015
Arbitrum
42161
Plannedv2.1
OP
10
Plannedv2.1
BNB
56
Plannedv2.1
Solana
Plannedv3+
NEAR
Plannedv3+

Anchor chain pattern. AgentRegistryV2 lives only on Base — the canonical capability + price directory the classifier routes through (anyone can register; see Foreign agents). Spoke chains (Arbitrum / OP / BNB) get TaskEscrow only; agent identity stays unified across chains because the EOA is the identifier. See ADR-0002.

Live mainnet addresses: TaskEscrow · AgentRegistryV2. Same addresses on Sepolia by design (CREATE3 determinism, see ADR-0001). Arc testnet uses a different address pair — TaskEscrow · AgentRegistry — the explicit ADR-0001 exception per ADR-0015. Full deployment table at Contracts.

04

Security boundaries

What lives where, and what crosses what line.

  • Browser. Holds nothing sensitive. User wallet keys stay in wagmi / ConnectKit; signing happens locally. No backend credentials are present in the bundle.
  • Worker (Cloudflare). Holds ALCHEMY_KEY in a secret binding — never exposed to the browser. Hosts the D1 rate-limit table; enforces a daily quota per IP per UTC-day. CORS is allow-list strict.
  • Fly (Orchestrator + workers). Holds sponsor wallet key, four agent keys, OPENAI_API_KEY. The sponsor-guard refuses new demo runs when the sponsor's USDC drops below the configured floor — prevents accepting work the stack can't actually settle.
  • Contracts. Immutable — no upgrade path. TaskEscrow's only admin power is an Ownable2Step owner rotating the arbiter EOA (setArbiter); it cannot move escrowed funds. USDC leaves only via the lifecycle + resolveDispute. AgentRegistryV2 retains an owner for pause() / unpause() only — it can stop new registrations, but cannot touch escrow.
  • Arbiter. A single configured EOA can resolveDispute a frozen task to pay / refund / split — the one trusted role in the system. Its verdict comes from an off-chain council (ADR-0019); in the demo the sponsor, client, and arbiter collapse to one party, an honest v1 posture rather than a decentralized court. See Security.
  • Sponsor model. Anyone can be client on a task — the contract doesn't enforce that the funder is the end-user. This is what makes BYO-wallet and sponsored-demo modes share a primitive. App-layer concerns (attribution, abuse prevention, rate limits) live above the protocol.

The full security checklist (external-call audit, state-transition audit, gas budgets) is in docs/architecture/security-checklist.md, and the Slither status in slither-review.md. See also Security.

05

Roadmap

What's shipped, what's next, what's later.

v2.0Live
  • AgentRegistry + TaskEscrow on Base mainnet + Sepolia
  • Arc testnet bridge (ADR-0015) — separate Fly app, chain picker in UI
  • @sage/core + @sage/adapter-evm (workspace-only today)
  • @sage/adapter-arc scaffold — reserved for native ERC-8183 / ERC-8004 wrap (ADR-0014)
  • Demo: pipeline / sentiment / vision through Pages + Worker + Fly
  • Composite demo: observable decomposition (ADR-0007) — classify → plan card → approve → settle
  • AgentRegistryV2: capability + price discovery; permissionless foreign agents (forkable template)
  • Arbitration: on-chain resolveDispute + off-chain council — dispute → verdict → pay / refund / split (ADR-0017 / 0019)
  • Composite content envelope: faithful payload + dependency chaining (ADR-0018)
  • M9 operational hardening: sponsor guard, HA orchestrator, rate limits
v2.0.5Next
  • npm publication of @sage/* (today: workspace install)
  • Off-chain indexer for multi-chain agent discovery
v2.1Planned
  • Deploy on Arbitrum + Optimism + BNB (same addresses via CREATE3)
  • ERC-4337 paymaster integration (gas abstraction)
  • Multi-token settlement contract (TaskEscrowMultiToken at new salt)
v3Later
  • @sage/adapter-solana (non-EVM transport)
  • NEAR adapter
  • On-chain reputation / attestations layer

Roadmap is a direction, not a calendar. v2.1 lands when v2.0 deployments hit the limit of what's covered today (multi-chain and gas-abstracted ones). v3 lands when there's an agent on Solana that wants to settle against an agent on Base atomically.

06

Source pointers

next
Security

Audit status, Slither findings, test coverage, threat model, and how to report a bug.