@toon-protocol/connector
v2.2.0
Published
Connector with BTP support and EVM settlement
Maintainers
Readme
@toon-protocol/connector
ILP connector node for AI agent payment networks. Routes packets, tracks balances, settles on-chain.
See the root README for conceptual overview, network architecture, and Docker deployment.
Install
npm install @toon-protocol/connectorQuick Start
import { ConnectorNode, createLogger } from '@toon-protocol/connector';
const node = new ConnectorNode('config.yaml', createLogger('my-agent', 'info'));
node.setPacketHandler(async (request) => {
console.log(`Received ${request.amount} tokens`);
return { accept: true };
});
await node.start();Deployment Modes
The connector supports two deployment modes via deploymentMode in config:
| Mode | Value | How packets arrive | How packets are sent |
| -------------- | -------------- | ----------------------------- | ------------------------------ |
| Embedded | 'embedded' | setPacketHandler() callback | node.sendPacket() |
| Standalone | 'standalone' | HTTP POST to BLS handlerUrl | HTTP POST to /admin/ilp/send |
When deploymentMode is omitted, it is inferred from localDelivery and adminApi flags.
Embedded Mode (Recommended for Libraries)
Pass a ConnectorConfig object directly for programmatic usage:
import { ConnectorNode } from '@toon-protocol/connector';
import type { ConnectorConfig } from '@toon-protocol/connector';
import pino from 'pino';
const config: ConnectorConfig = {
nodeId: 'connector-a',
btpServerPort: 4000,
healthCheckPort: 8080,
deploymentMode: 'embedded',
adminApi: { enabled: false },
localDelivery: { enabled: false },
peers: [],
routes: [],
environment: 'production',
};
const node = new ConnectorNode(config, pino({ name: 'connector-a' }));
node.setPacketHandler(async (request) => {
if (request.isTransit) {
// Transit notification (fire-and-forget at intermediate hops)
console.log(`Transit: ${request.amount} tokens → ${request.destination}`);
return { accept: true };
}
// Final-hop delivery
console.log(`Delivery: ${request.amount} tokens from ${request.sourcePeer}`);
return { accept: true };
});
await node.start();
// Register peers dynamically at runtime
await node.registerPeer({
id: 'connector-b',
url: 'ws://localhost:4001',
authToken: '',
routes: [{ prefix: 'g.connector-b' }],
evmAddress: '0xConnectorBAddress...', // peer's EVM settlement address
});
// Send a packet
await node.sendPacket({
destination: 'g.connector-b.agent',
amount: 1000n,
executionCondition: Buffer.alloc(32),
expiresAt: new Date(Date.now() + 30000),
data: Buffer.from('Hello'),
});Standalone Mode
For separate-process deployments, configure localDelivery and adminApi:
nodeId: my-connector
btpServerPort: 3000
healthCheckPort: 8080
deploymentMode: standalone
environment: production
localDelivery:
enabled: true
handlerUrl: http://my-bls:3100
timeout: 5000
adminApi:
enabled: true
port: 8081
apiKey: ${ADMIN_API_KEY}
peers:
- id: peer-b
url: ws://peer-b:3001
authToken: ''ConnectorConfig Reference
The constructor accepts either a YAML file path or a ConnectorConfig object:
const node = new ConnectorNode(config: ConnectorConfig | string, logger: Logger);Required Fields
| Field | Type | Description |
| --------------- | --------------- | ------------------------------------------------ |
| nodeId | string | Unique identifier for this connector |
| btpServerPort | number | Port for BTP WebSocket server |
| peers | PeerConfig[] | Peer connector definitions |
| routes | RouteConfig[] | Routing table entries |
| environment | Environment | 'development' | 'staging' | 'production' |
Optional Fields
| Field | Type | Default | Description |
| ----------------- | ----------------------- | ------------- | ---------------------------------------------- |
| deploymentMode | DeploymentMode | inferred | 'embedded' | 'standalone' |
| healthCheckPort | number | 8080 | HTTP health endpoint port |
| logLevel | string | 'info' | 'debug' | 'info' | 'warn' | 'error' |
| adminApi | AdminApiConfig | disabled | Admin REST API settings |
| localDelivery | LocalDeliveryConfig | disabled | HTTP forwarding to BLS |
| settlement | SettlementConfig | — | TigerBeetle accounting config |
| settlementInfra | SettlementInfraConfig | — | EVM settlement infrastructure |
| blockchain | BlockchainConfig | — | Multi-chain EVM config (Base, Arbitrum) |
| security | SecurityConfig | — | Key management backend |
| performance | PerformanceConfig | — | Batching, pooling, parallelization |
| explorer | ExplorerConfig | enabled:3001 | Explorer UI settings |
| mode | string | 'connector' | 'connector' | 'gateway' |
PeerConfig
interface PeerConfig {
id: string; // Unique peer identifier (referenced by routes)
url: string; // WebSocket URL: ws://host:port or wss://host:port
authToken: string; // Shared secret (empty string for permissionless)
evmAddress?: string; // Peer's EVM address for settlement
}RouteConfig
interface RouteConfig {
prefix: string; // ILP address prefix (e.g., 'g.connector-b')
nextHop: string; // Peer ID to forward to
priority?: number; // Higher wins (default: 0)
}ConnectorNode Public API
Lifecycle
| Method | Returns | Description |
| --------- | --------------- | --------------------------------------------------- |
| start() | Promise<void> | Start BTP server, connect to peers, init settlement |
| stop() | Promise<void> | Graceful shutdown of all connections and servers |
Packet Handling
| Method | Returns | Description |
| ---------------------------------- | ---------------------------------------------- | -------------------------------------- |
| setPacketHandler(handler) | void | Register callback for incoming packets |
| setLocalDeliveryHandler(handler) | void | Register callback for local delivery |
| sendPacket(params) | Promise<ILPFulfillPacket \| ILPRejectPacket> | Send ILP Prepare packet |
Peer Management
| Method | Returns | Description |
| ----------------------------------- | --------------------------- | ------------------------ |
| registerPeer(config) | Promise<PeerInfo> | Add peer at runtime |
| removePeer(peerId, removeRoutes?) | Promise<RemovePeerResult> | Remove peer |
| listPeers() | PeerInfo[] | List all connected peers |
Routing
| Method | Returns | Description |
| -------------------------------------- | ------------- | ------------------ |
| listRoutes() | RouteInfo[] | List routing table |
| addRoute(prefix, nextHop, priority?) | — | Add a route |
| removeRoute(prefix) | — | Remove a route |
Balance & Settlement
| Method | Returns | Description |
| ----------------------------- | ----------------------------- | ------------------------- |
| getBalance(peerId) | Promise<PeerAccountBalance> | Query peer balances |
| openChannel(peerId, amount) | Promise<void> | Open EVM payment channel |
| getChannelState(peerId) | Promise<...> | Get payment channel state |
Mode Inspection
| Method | Returns | Description |
| --------------------- | ---------------- | -------------------------------------- |
| getDeploymentMode() | DeploymentMode | Returns 'embedded' or 'standalone' |
| isEmbedded() | boolean | Check if embedded mode |
| isStandalone() | boolean | Check if standalone mode |
BTP Authentication
Permissionless Networks (Default)
Default mode for open networks where security is at the ILP layer:
peers:
- id: peer-b
url: ws://peer-b:3001
authToken: '' # Empty = permissionlessPrivate Networks
Disable permissionless mode and use shared secrets:
BTP_ALLOW_NOAUTH=false
BTP_PEER_PEER_B_SECRET=secret-tokenpeers:
- id: peer-b
url: ws://peer-b:3001
authToken: secret-tokenPer-Hop Notification
Intermediate connectors can fire non-blocking notifications to a BLS for transit packets:
localDelivery:
enabled: true
handlerUrl: http://my-bls:3100
perHopNotification: true| | Transit (isTransit: true) | Final-Hop (isTransit omitted) |
| ---------------- | --------------------------- | ---------------------------------- |
| When | Packet passing through | Packet addressed to this connector |
| BLS response | Ignored (fire-and-forget) | Drives ILP fulfill/reject |
| Blocking | No | Yes |
| Use case | Logging, analytics | Payment acceptance, business logic |
Accounting Backend
Default: In-Memory Ledger
Zero dependencies. Persists to JSON snapshots on disk.
| Variable | Default | Description |
| ---------------------------- | ----------------------------- | ------------------------- |
| LEDGER_SNAPSHOT_PATH | ./data/ledger-snapshot.json | Snapshot file path |
| LEDGER_PERSIST_INTERVAL_MS | 30000 | Persistence interval (ms) |
Optional: TigerBeetle
High-performance double-entry accounting. Falls back to in-memory if connection fails.
| Variable | Required | Description |
| ------------------------ | -------- | --------------------------------- |
| TIGERBEETLE_CLUSTER_ID | Yes | TigerBeetle cluster identifier |
| TIGERBEETLE_REPLICAS | Yes | Comma-separated replica addresses |
Settlement Infrastructure
EVM payment channels on Base L2 (and optionally Arbitrum):
| Variable | Description |
| ----------------------------- | ------------------------------------------------------------- |
| SETTLEMENT_ENABLED | Enable automatic settlement (default: true) |
| SETTLEMENT_THRESHOLD | Balance threshold to trigger settlement |
| SETTLEMENT_POLLING_INTERVAL | Polling frequency in ms (default: 30000) |
| BASE_L2_RPC_URL | Base L2 RPC endpoint |
| EVM_PRIVATE_KEY | Private key (dev only — use KMS in production) |
| M2M_TOKEN_ADDRESS | ERC-20 token contract |
| TOKEN_NETWORK_REGISTRY | Payment channel registry contract |
| KEY_BACKEND | Key management: env | aws-kms | gcp-kms | azure-kv |
| NETWORK_MODE | Auto-configure chain settings: testnet | mainnet |
Multi-Chain Support
Configure per-chain settings via blockchain config:
const config: ConnectorConfig = {
// ...
blockchain: {
base: {
enabled: true,
rpcUrl: 'https://mainnet.base.org',
chainId: 8453,
},
arbitrum: {
enabled: true,
rpcUrl: 'https://arb1.arbitrum.io/rpc',
chainId: 42161,
},
},
};Explorer UI
Built-in real-time dashboard for packet flow, balances, and settlement monitoring.
| Variable | Default | Description |
| ------------------------- | --------- | ------------------------ |
| EXPLORER_ENABLED | true | Enable/disable explorer |
| EXPLORER_PORT | 3001 | HTTP/WebSocket port |
| EXPLORER_RETENTION_DAYS | 7 | Event retention period |
| EXPLORER_MAX_EVENTS | 1000000 | Maximum events to retain |
Endpoints:
| Endpoint | Description |
| ----------------- | -------------------------------------------- |
| GET /api/events | Query historical events (supports filtering) |
| GET /api/health | Explorer health status |
| WS /ws | Real-time event streaming |
Admin API
REST endpoints for runtime peer/route management and ILP packet sending.
| Endpoint | Description |
| ------------------------------ | --------------------- |
| GET /admin/peers | List all peers |
| POST /admin/peers | Add a new peer |
| DELETE /admin/peers/:peerId | Remove a peer |
| GET /admin/routes | List routing table |
| POST /admin/routes | Add a route |
| DELETE /admin/routes/:prefix | Remove a route |
| POST /admin/ilp/send | Send ILP packet |
| GET /admin/balances/:peerId | Query peer balances |
| GET /admin/channels | List payment channels |
| POST /admin/channels | Open payment channel |
Security
| Variable | Description |
| ----------------------- | -------------------------------------------------------- |
| ADMIN_API_KEY | API key (required in production unless IP allowlist set) |
| ADMIN_API_ALLOWED_IPS | Comma-separated IPs/CIDRs |
| ADMIN_API_TRUST_PROXY | Trust X-Forwarded-For (default: false) |
CLI Commands
npx connector setup # Interactive onboarding wizard
npx connector start -c config.yaml # Start connector
npx connector health -u http://localhost:8080 # Check health
npx connector validate config.yaml # Validate config fileExported API
Classes: ConnectorNode, ConfigLoader, ConfigurationError, ConnectorNotStartedError, RoutingTable, PacketHandler, BTPServer, BTPClient, BTPClientManager, AdminServer, AccountManager, SettlementMonitor, UnifiedSettlementExecutor, IlpSendHandler
Types: ConnectorConfig, PeerConfig, RouteConfig, SettlementConfig, SettlementInfraConfig, LocalDeliveryConfig, LocalDeliveryHandler, LocalDeliveryRequest, LocalDeliveryResponse, SendPacketParams, PeerRegistrationRequest, PeerInfo, PeerAccountBalance, RouteInfo, RemovePeerResult, IlpSendRequest, IlpSendResponse, AdminSettlementConfig, ChannelOpenOptions, ChannelMetadata, PaymentRequest, PaymentResponse, PaymentHandler, PacketSenderFn, IsReadyFn, ILPPreparePacket, ILPFulfillPacket, ILPRejectPacket
Utilities: createLogger, createPaymentHandlerAdapter, computeFulfillmentFromData, computeConditionFromData, validateIlpSendRequest, generatePaymentId, mapRejectCode, validateResponseData, REJECT_CODE_MAP
Package Structure
src/
├── core/ # ConnectorNode, PacketHandler, payment handler, local delivery
├── btp/ # BTP server and client (WebSocket peers)
├── routing/ # Routing table and prefix matching
├── settlement/ # EVM settlement, payment channels, account manager
├── http/ # Admin API, health endpoints, ILP send handler
├── explorer/ # Embedded telemetry UI server and event store
├── wallet/ # HD wallet derivation for EVM keys
├── security/ # KMS integration (AWS, Azure, GCP)
├── config/ # Configuration schema, loader, and validation
├── cli/ # CLI commands (setup, start, health, validate)
└── utils/ # Logger, OER encodingTesting
npm test # Unit tests
npm run test:acceptance # Acceptance testsLicense
MIT — see LICENSE.
