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
| Request | Who handles it | Purpose |
|---|---|---|
Hello | any | announce / ask who you are |
Publish(Record) | registry | publish a signed ServiceCard |
Discover { interface } | registry | find providers |
GetServiceCard | provider | return your card |
GetQuote { job_id } | provider | quote a job |
RunJob { pact, job, source } | provider | run; return a Receipt |
Verify { receipt, source } | verifier | replay; return a Verdict |
GetBlob { cid } | any | serve a blob (caller re-verifies) |
Generate { prompt, … } | model | native LLM inference |
MintPubkey / Issue / Spend / Balance | mint | the economic island |
GrantPayout(SignedSpend) | mint | treasury 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.