The protocol

One request → one response per QUIC bidirectional stream; the stream finish delimits each message. All messages are canonical DAG-CBOR. The wire is untrusted — integrity comes from BLAKE3, authenticity from signatures.

Handshake

On connect, peers exchange a NodeInfo:

NodeInfo { agent, protocol: "verse/0.2", role, node_id, capabilities, cpu_cores }

A peer is reached by PeerAddr { node_id, host, port } — “dial keys, not IPs.” Identity is the key; the address is just a hint.

Requests

RequestWho handles itPurpose
Helloanyannounce / ask who you are
Publish(Record)registrypublish a signed ServiceCard
Discover { interface }registryfind providers
GetServiceCardproviderreturn your card
GetQuote { job_id }providerquote a job
RunJob { pact, job, source }providerrun; return a Receipt
Verify { receipt, source }verifierreplay; return a Verdict
GetBlob { cid }anyserve a blob (caller re-verifies)
Generate { prompt, … }modelnative LLM inference
MintPubkey / Issue / Spend / Balancemintthe economic island
GrantPayout(SignedSpend)minttreasury grant payout

Responses

NodeInfo, Cards, ServiceCard, Quote, Receipt, Verdict { ok, detail }, Blob, Pubkey, BlindSigs, Balance, Text, Ok, Error.

Fetching a blob

A node never trusts a remote’s bytes. fetch_blob(source, cid) pulls bytes over QUIC and re-hashes them: if blake3(bytes) != cid, it’s rejected. This is why the protocol guarantees hold over an untrusted network.

Connection pool

One QUIC connection per peer is reused; each request is a cheap new stream, not a new handshake. This alone took throughput from ~320 to ~4,000 jobs/s.

Continue to Primitives.