Docs

Agents

spaceturtle

The connector, by hand

The operator surface

A token that reads what you were paid, and a key that moves it.

How you inspect a running node and how you move its money. It mounts only when [operator] is configured, and merges onto client_edge_addr: there is no second port and no second listener.

The one read you will do most: what you were paid.

curl -s -H "Authorization: Bearer $(cat data/operator-bearer-token)" http://127.0.0.1:3000/claims | jq
connector.toml
[operator]
bearer_token_file = "/app/data/operator-bearer-token"
write_keys_file   = "/app/data/operator-write-keys"

Each setting is exactly one of a literal or a path. The file forms are the deployed forms, because a fleet's config files are committed to a public repository and a literal cannot be.

Read and write are different authorities

  • Reads take Authorization: Bearer <token> and nothing more.
  • Writes take an RFC 9421 HTTP Message Signature from an ed25519 key on write_keys, with the body bound by an RFC 9530 Content-Digest.
  • A bearer token is never sufficient to move value. Read authority must not confer write authority.
  • Every write is attributable and individually revocable. A shared secret is neither.
  • An accepted write cannot be replayed. Signatures carry created and expires, and an accepted signature is remembered until its own expiry.
  • A surface with neither half authenticated refuses to start.

write_keys holds only public halves. The private half lives with whoever is calling and never on the node.

Reads

EndpointAnswers
GET /peersThe peerings this node holds, config and runtime alike.
GET /routesThe full routing table, with each row's source.
GET /routes/leasedTTL-bound pushed routes that lapse on their own.
GET /routes/peersThe durable runtime peer-route table.
GET /channelsEvery channel this node knows, with deposits and status.
GET /claimsThe claim journal: what you have been paid, and by whom.
GET /identityThis node's operator-facing identity.
GET /audit-logEvery accepted write, with the key that made it.
GET /metricsPrometheus text: toon_packets_total, toon_packets_rejected_total, toon_fees_earned_total, toon_settlement_total.

There is no unauthenticated metrics path and no dedicated health route. What a container healthcheck probes is GET /ilp/identity on the client edge, which is free and answers only once the config loaded, every settlement backend connected and the router is serving.

Writes

EndpointDoes
POST /packetsOriginate a packet from this node.
POST /peersEstablish a peering from a URL. DELETE /peers/:id removes it.
POST /routes/peersWrite a durable runtime route. DELETE /routes/peers/:prefix removes it.
POST /routes/leasedPush a TTL-bound route that lapses on its own.
POST /channelsOpen an outbound x402 channel toward a counterparty, on its published terms.
POST /channels/:id/fundTop up an outbound channel by an increment.
POST /channels/:id/withdrawStart a withdrawal (EVM) or close (Solana); call again once due to finish.
POST /channels/:id/landLand the latest voucher held on an inbound channel now.

The watchers land your inbound vouchers on their own; land is the lever for planned maintenance. POST /channels journals the channel before it spends, so a retry after an error resumes that channel rather than opening a second. Channel operations answer 503 when no settlement backend is configured. Runtime peers and routes survive a restart, and are refused outright when they would collide with a row the config file owns.

Signing a write

The signature covers exactly three components, @method, @path and content-digest, with alg="ed25519" and keyid set to the signer's own public key in hex. sign-write.sh is the shipped signer: bash and openssl, no other dependency.

docs/operators/sign-write.sh -k operator-write.key -X POST -p /peers \
    -b "$BODY" -u https://your-node.example

Omit -u and it prints only the three headers, for a caller assembling its own request. Signing a write is the worked example.

The dashboard

GET /dashboard is the operator's own view of all of the above on one page the node serves: packet traffic and rejects by code, fees earned, inbound and outbound claims, peerings and channels, every route with its source, and the audit log. It needs no token to load, because it holds nothing. Paste the bearer token in and it reads; paste an operator key in and it can peer, write a runtime route or lease one, signing each write in your browser. The key stays in the tab's memory, never stored, never sent. Reach it the way you reach /metrics on that box: on a fleet, an SSH tunnel to client_edge_addr.

Operating it

Logs are structured JSON on stdout. Every line emitted while handling a packet carries the same correlation_id, and because that value is invariant across hops, the same id appears in every connector that handled it. RUST_LOG=debug for more.

Releases. A release builds the image, cuts a dated handle and opens a GitHub Release. It does not deploy: a node repository pins the connector image it runs, by release handle, in its own deploy bundle. Because the binary and a box's mounted TOML are a matched pair in both directions, adding a required config key is a breaking deploy: land the config first, then bump the pin.

Devnet and mainnet. Devnet settles on Base Sepolia and Solana devnet, with test funds from the devnet faucet. The live node that serves toon.ar.io settles on Base mainnet and Solana mainnet-beta. The connector repository's own production tier is a named, empty tier: no fleet machine, no fleet key.

Written from connector/docs/guide/operator-surface.md and operations.md. The rules are numbered in docs/protocol/operator-spec.md.