Unified payment execution layer for autonomous agents on Stellar
Three payment protocols exist on Stellar for agent-to-service transactions: x402 (Coinbase), MPP charge (Stripe/Stellar), and MPP session channels (stellar-experimental). Each has a separate SDK, a separate integration path, and no discovery mechanism. The one-way-channel Soroban contract is unaudited and has no safe integration path. Every agent team hardcodes payment logic per endpoint.
import { RouteDockClient } from '@routedock/routedock'
const client = new RouteDockClient({ wallet, network: 'testnet' })
const result = await client.pay('https://provider.example.com/price')
// result.mode → 'x402' | 'mpp-charge' | 'mpp-session' (selected automatically)One SDK. One function call. The mode is selected from the provider's routedock.json manifest. The agent writes nothing else.
graph LR
Agent --> ModeRouter
ModeRouter --> x402Client
ModeRouter --> MppChargeClient
ModeRouter --> MppSessionClient
x402Client -->|mainnet| OZFacilitator["OZ Facilitator"]
x402Client -->|testnet| LocalFacilitator["Local Facilitator"]
OZFacilitator --> Stellar
LocalFacilitator --> Stellar
MppChargeClient --> StellarSAC["Stellar SAC transfer"]
StellarSAC --> Stellar
MppSessionClient --> ChannelContract["one-way-channel\nSoroban contract"]
ChannelContract --> Stellar
x402Client --> ProviderA["Provider A\n/price"]
MppChargeClient --> ProviderA
MppSessionClient --> ProviderB["Provider B\n/stream/orderbook"]
| Layer | Mechanism | Enforcement Point |
|---|---|---|
| Application | voucher monotonic check | packages/sdk/src/client/MppSessionClient.ts:currentCumulative |
| Application | manifest schema validation (AJV draft-07) | packages/sdk/src/client/ModeRouter.ts |
| Database | monotonic cumulative trigger | supabase/migrations/*_sessions.sql:enforce_monotonic_cumulative() |
| Database | RLS on sessions table | supabase/migrations/*_rls.sql |
| Contract | daily USDC cap policy | contracts/agent-vault/src/lib.rs:__check_auth |
| Contract | endpoint allowlist policy | contracts/agent-vault/src/lib.rs:__check_auth |
| Contract | session key expiry | contracts/agent-vault/src/lib.rs:__check_auth |
| Contract | one-way-channel signature verification | CCK4XOW3YKQUEZFONUTINKMSNW7SNMRQZURME5U3UP7E6WNGK7UHUCAH |
The one-way-channel Soroban contract (stellar-experimental/one-way-channel) has NOT been audited. RouteDock wraps it with safe defaults (17280-ledger refund window, durable session store with monotonic invariant, DB-level trigger enforcement). Production mainnet use should await a formal audit.
Audit Status:
- Shortlisted auditors: OtterSec, Hacken, Trail of Bits
- SCF Audit Bank application: Submitted
The agent-vault contract emits structured events that indexers and Stellar Expert can attest to without parsing tx state changes.
| Event | Topics | Data | When |
|---|---|---|---|
payment_authorized |
(Symbol, payer: Address, payee: Address) |
(amount: i128, asset: Address, daily_cumulative: i128) |
Each successful auth pass in __check_auth |
session_settled |
(Symbol, channel_id: Address, payee: Address) |
(payer: Address, cumulative_amount: i128, voucher_count: u32) |
Server calls record_session_settlement after channel close |
stellar events --network testnet --start-ledger <LEDGER> --contract-id <VAULT_ID>All produced by a single autonomous agent run against two live provider services. No mocks.
| Type | Tx Hash | Explorer |
|---|---|---|
| x402 settlement | 5f603387807faacdc02c71efb74b26091b1be67740f74dfd581d23d643e2db64 |
view |
| Channel open (deploy) | 6ceba32ba2cfd7f3145090c2e6f741db65ae4e4116f3204f2c3173b5266b98ff |
view |
| Channel close (50 vouchers settled) | 234dcbb34cfb7a086f17474f57cacaa9edee8bc8dee873e8f2b851abc0a29a20 |
view |
| Policy rejection | NO TX — daily cap enforced locally before any broadcast | — |
50 interactions. 2 on-chain transactions. The channel close settled the cumulative amount for all 50 vouchers in a single Soroban invocation.
Contracts deployed on testnet:
- Agent vault:
CAX5IDLC2XHGQSEA2YN3LPLZ7EXLMRXYX3HFJGKFXS6B7OQXBKWO44LT - One-way channel:
CCK4XOW3YKQUEZFONUTINKMSNW7SNMRQZURME5U3UP7E6WNGK7UHUCAH
npm install @routedock/routedockgit clone https://github.com/winsznx/routedock && cd routedock
pnpm install
cp apps/provider-a/.env.example apps/provider-a/.env
# Fill in STELLAR_PAYEE_SECRET, SUPABASE_URL, SUPABASE_SERVICE_KEY
pnpm --filter @routedock/provider-a devFull demo (both providers + agent): see docs/AGENT_RUN_CHECKLIST.md.
Mainnet rollout: see docs/MAINNET_DEPLOYMENT.md.
| Example | What it shows |
|---|---|
examples/streaming-orderbook-agent |
Opens an MPP session to Provider B's /stream/orderbook, consumes 100 voucher-backed orderbook updates, prints best bid/ask, spread, and mid price, then closes the session and logs the settlement tx hash. |
Run it with:
cd examples/streaming-orderbook-agent
pnpm install
pnpm startServer returns HTTP 402. Agent signs a Soroban auth entry for a USDC SAC transfer. On mainnet, the OpenZeppelin facilitator verifies and broadcasts. On testnet, the provider runs a local facilitator (same x402 V2 protocol). One request, one settlement.
Agent sends a payment intent via the Stellar MPP charge protocol. USDC transfers natively via the SAC — no facilitator, lower fees. Server verifies and returns the response.
Agent deposits USDC into a stellar-experimental/one-way-channel Soroban contract. For each unit of data consumed, the agent signs an off-chain ed25519 commitment (no RPC call, no transaction fee). The channel settles with one on-chain close transaction for the cumulative amount.
If a server crashes mid-session, the agent can reclaim deposited funds using the dispute resolution API. Three methods handle the recovery flow:
session.requestRefund()— initiates the refund process on the channel contract, starting the refund window (default 17,280 ledgers ≈24h)session.settleWithLatestVoucher()— server-side counter-mechanism to settle the cumulative amount before the refund window expires, using the highest signed vouchersession.getDisputeStatus()— returns the channel state:'open','in-refund-window','refundable', or'settled'
Raises: RouteDockDisputeError, RouteDockChannelStateError, RouteDockRefundWindowError
Deterministic, manifest-driven:
- Sustained access requested +
mpp-sessionavailable → session mpp-chargeavailable → charge (lower fees, no facilitator)mpp-sessionavailable → x402 (facilitator-backed)- Nothing available → throw
Every provider serves /.well-known/routedock.json. The SDK fetches and validates it (JSON Schema, AJV) before every call. Agents never hardcode payment logic.
{
"routedock": "1.0",
"name": "Stellar DEX Price Feed",
"modes": ["x402", "mpp-charge"],
"network": "testnet",
"asset": "USDC",
"asset_contract": "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA",
"payee": "G...",
"pricing": {
"x402": { "amount": "0.001", "per": "request" },
"mpp-charge": { "amount": "0.0008", "per": "request" }
},
"endpoints": { "price": "GET /price" },
"tags": ["price", "stellar", "dex", "orderbook"]
}The Supabase providers table indexes manifests with pg_trgm trigram search — agents query by capability, not by URL.
import { routedock } from '@routedock/routedock/provider'
app.use('/price', routedock({
modes: ['x402', 'mpp-charge'],
pricing: { x402: '0.001', 'mpp-charge': '0.0008' },
asset: 'USDC',
assetContract: process.env.USDC_ASSET_CONTRACT,
payee: process.env.STELLAR_PAYEE_ADDRESS,
payeeSecretKey: process.env.STELLAR_PAYEE_SECRET,
network: process.env.STELLAR_NETWORK,
facilitatorApiKey: process.env.OPENZEPPELIN_API_KEY, // mainnet only
manifest,
}))import { Hono } from 'hono'
import { routedockHono } from '@routedock/sdk/provider/hono'
const app = new Hono()
app.use('/price', routedockHono({
modes: ['x402', 'mpp-charge'],
pricing: { x402: '0.001', 'mpp-charge': '0.0008' },
asset: 'USDC',
assetContract: process.env.USDC_ASSET_CONTRACT,
payee: process.env.STELLAR_PAYEE_ADDRESS,
payeeSecretKey: process.env.STELLAR_PAYEE_SECRET,
network: process.env.STELLAR_NETWORK,
facilitatorApiKey: process.env.OPENZEPPELIN_API_KEY, // mainnet only
manifest,
}))
export default appOne middleware. Handles x402, MPP charge, and MPP session. Serves routedock.json. Verifies payments. Settles on-chain.
routedock/
├── packages/sdk/ # @routedock/routedock — npm
├── packages/mcp-server/ # @routedock/mcp-server — MCP server for LLM agents
├── apps/web/ # Next.js 16 dashboard + landing — Vercel
├── apps/provider-a/ # Express price endpoint (x402 + MPP charge) — Railway
├── apps/provider-b/ # Express orderbook endpoint (MPP session) — Railway
├── contracts/agent-vault/ # Soroban contract account — Stellar
├── agent/ # Reference autonomous agent
└── supabase/ # Schema + RLS + trigram indexes + Realtime
| Capability | Detail |
|---|---|
| x402 settlement (testnet) | Local ExactStellarFacilitatorScheme — no third-party dependency |
| x402 settlement (mainnet) | OZ hosted facilitator at channels.openzeppelin.com/x402 with Bearer auth |
| MPP charge settlement | Server-side broadcast via @stellar/mpp pull mode |
| MPP session: off-chain vouchers | 50 vouchers verified — each signed as ed25519 commitment, no on-chain tx per voucher |
| MPP session: on-chain close | Single Soroban close(amount, signature) settles the cumulative amount |
| Contract account policy enforcement | __check_auth daily cap rejects overspend at the Soroban level |
| Dashboard Realtime | Supabase postgres_changes subscriptions on sessions and tx_log |
| Discovery registry | providers table with pg_trgm trigram indexes for fuzzy capability search |
| npm package | @routedock/routedock@0.1.0 |
| MCP server | @routedock/mcp-server — LLM agent integration via Model Context Protocol |
| Network support | `STELLAR_NETWORK=testnet |
| Service | URL | Status |
|---|---|---|
| npm package | @routedock/routedock |
published |
| Agent vault | CAX5IDLC2XHGQSEA2YN3LPLZ7EXLMRXYX3HFJGKFXS6B7OQXBKWO44LT |
live (testnet) |
| Channel contract | CCK4XOW3YKQUEZFONUTINKMSNW7SNMRQZURME5U3UP7E6WNGK7UHUCAH |
live (testnet) |
| Dashboard | routedock.xyz | live |
| Provider A (price endpoint) | api-a.routedock.xyz | live |
| Provider B (orderbook endpoint) | api-b.routedock.xyz | live |
For a detailed walkthrough of the architecture, protocol conformance, security model, and design decisions, see the Submission Brief.
RouteDock is a chain-agnostic agent payment protocol with Stellar as its canonical home. The protocols it unifies aren't Stellar-bound — x402, which RouteDock wraps, was designed multi-chain and already settles on EVM.
The Multi-Chain Roadmap sketches a ChainAdapter interface with the existing Stellar clients as the reference implementation and EVM (via x402) as the first extension target — keeping the single-call agent experience (client.pay(url)) identical across chains. The plan is additive: the manifest's chain field is optional and defaults to stellar, so every existing provider and agent keeps working.
MIT