Перейти к основному содержимому

Layer 6 · The dig RPC — the machine interface

Canonical reference: hub services/retrieval/src/bin/bootstrap.rs (the canonical rpc.dig.net server); dig-node/src/lib.rs (the node profile). The §21 REST routes are its transport sibling.

The dig RPC is the single, agent-consumable network surface of the protocol — the only way content is read. JSON-RPC 2.0 over HTTPS POST to a single endpoint. There is no CDN.

Machine-readable specs (regenerated from the implementation)

Both OpenRPC documents are generated from one source of truth and are intended to be CI-diffed against live server responses, so an agent can drive the protocol without scraping prose. The verified browser client dig-client-wasm is pinned by an SRI digest.

1 · Transport envelope

POST https://rpc.dig.net
Content-Type: application/json
{ "jsonrpc": "2.0", "id": <id>, "method": "<name>", "params": { … } }
  • A single request object OR a non-empty batch array (each element dispatched independently, re-collected into a response array) — bootstrap.rs:515-525.
  • params is always by-name; id is echoed.
  • Any well-formed JSON body → HTTP 200; success/error rides in the JSON-RPC envelope.
  • CORS *, no credentials (content is public ciphertext); OPTIONS → 204 (bootstrap.rs:468).
This is the PUBLIC READ tier

rpc.dig.net and this network profile ARE the anonymous, browser-reachable public read tier of the dual-transport model: no client certificate, CORS-*, read-only, decoy-on-miss, and integrity self-verified client-side against the chain-anchored root. The peer / write / config / control surface (peer exchange, DHT, PEX, availability-for-sync, PUSH/WRITE, node control) lives on the separate mTLS peer/control tier and is never reachable here — see the tier map.

2 · Method catalogue (network profile)

The canonical server implements (bootstrap.rs:453-461):

MethodReturns
dig.getContenta chunk object of one resource's ciphertext
dig.getProofthe REAL sync inclusion proof + execution-proof status
dig.getProofStatusa REAL execution-proof job by id
dig.getCapsule (alias dig.getModule)the whole .dig for (store, root)
dig.getManifestthe public discovery manifest resource
dig.getMetadatathe plaintext metadata manifest (no proof, never encrypted)
dig.listCapsulesthe confirmed capsule list (discovery metadata)
dig.listCollectionItemsan NFT collection's items resolved to their current on-chain owner + royalty + CHIP-0007 metadata (paginated)
dig.getCollectioncollection-level facts (creator DID, item count, uniform royalty) for a set of NFT launcher ids
dig.health, dig.methodsservice / capability discovery (authoritative for agent self-describe)

Unknown method → -32601.

Identifiers

All lower-case hex on the wire.

IdentifierFormMeaning
store_id64 hexthe CHIP-0035 singleton launcher id
retrieval_key64 hexSHA-256(urn) — the only URN-derived value sent to a node; the AES key is derived client-side and never transmitted
root64 hex | "latest" | absenta generation root; "latest"/absent → newest confirmed generation. root_is_pinned gates caching — only an explicit concrete root is immutable/cacheable
capsule identity<store_id>:<root>one immutable generation

3 · The chunk wire object

Every byte method returns this object (rpc_chunk_result, bootstrap.rs:650-675):

FieldTypeMeaning
ciphertextb64 stringthis window's bytes
total_lengthuintfull resource ciphertext length (pre-windowing)
offsetuintwindow start
lengthuintthis window's byte length
completebooloffset + len >= total_length
next_offsetuint | nullnext offset, or null when complete
inclusion_proofb64 | nullmerkle proof of the whole resource, relayed verbatim. Present every window on getContent/getManifest; empty/null on getCapsule
chunk_lensuint[]per-chunk ciphertext lengths of the full resource. First window only (offset == 0); empty ⇒ single chunk
program_hash64 hexSHA-256(.dig bytes) — the on-chain program identity
root64 hexthe resolved generation root
There is NO decoy field on the wire

A non-present resource yields the .dig's own indistinguishable, non-verifying response (the decoy, same shape) — the client discovers non-presence only by inclusion-proof failure and/or decryption failure. The absence of a decoy field is regression-locked (bootstrap.rs:2830); the in-process node additively tags source ("local"/"remote") but never a decoy.

4 · Streaming + client contract

  1. Loop dig.getContent with offset, length = 3 MiB until complete || next_offset == null; reassemble by total_length.
  2. Keep the first inclusion_proof and the first chunk_lens.
  3. verifyInclusion(ciphertext, proof, root) against the CALLER-supplied chain-anchored root — the host is never the trust anchor (see Verification).
  4. Split the reassembled ciphertext by chunk_lens and AES-256-GCM-SIV-open each chunk.

Range / window math

length is clamped to 3 MiB (RPC_MAX_CHUNK), then the requested range is 64-KiB up-aligned (align_range): start rounded down to 64 KiB, end up to the next 64-KiB boundary − 1; rejected if the aligned span > 16 MiB. So a window may be up to 3 MiB + ~64 KiB — document the effective size, not the imprecise "3 MiB" cap (range.rs:38-61). Alignment bounds the cache-key range cardinality (anti-amplification). Full-200 slice semantics (no HTTP 206).

5 · Error model

Standard JSON-RPC -32700 / -32600 / -32601 / -32602 / -32603, plus the protocol-specific:

CodeMeaning
-32004Resource not available at the requested root — a genuine infra miss (no host seed, module absent in both buckets, bad magic, oversize, a wasmtime trap, an undecodable envelope). Returned by getContent/getProof/getCapsule/getManifest/getMetadata. Distinct from a content miss, which is an indistinguishable decoy and never an error.
-32005Root not chain-anchored — the requested or served generation is not the store's current on-chain root. dig.getContent on a node that enforces the root pin resolves the CHIP-0035 singleton's on-chain root live (never trusting the serving node) and serves against it or fails closed: an explicit root that is not the on-chain root, an unreachable chain, or a store with no confirmed generation all return this code rather than serving an unverified generation. Omit root to take the chain tip.
-32006Peer unreachable (node profile) — no connection to the named peer could be established: every NAT-traversal strategy failed, or the peer is not on this network. Returned by the peer methods dig.getPeers / dig.announce / dig.getNetworkInfo.
-32007Range not satisfiable (node profile) — the requested byte range lies outside the resource, or is otherwise unsatisfiable. Returned by dig.fetchRange, the streaming byte-range / multi-source content fetch.

See the full error catalog.

6 · Proof surface

dig.getProof always returns the REAL synchronous inclusion_proof + program_hash + root. The execution_proof (risc0) is read-only / job-based: null with execution_proof_status = "request_via_control_plane" unless a proof_id (requested via the gated hub /v1 control plane) resolves a job. dig.getProofStatus polls the real job. Never a mock receipt on the wire. See inclusion vs execution proofs.

7 · Node profile

The local dig-node / dig-companion that the DIG Browser runs in-process (FFI) is rpc.dig.net-compatible but implements a different, smaller subset (dig-node/src/lib.rs:1121-1297):

  • Of the byte methods, only dig.getContent (local-first: cached .dig → §21.9 whole-store sync → proxy upstream). Everything else proxies or returns -32601. Before serving from ANY source, the node resolves the store's on-chain root and pins the served generation to it (or fails closed with -32005) — so a compromised upstream/host can never choose which generation is served. A rootless request resolves to the chain tip; an explicit root must equal it.
  • Plus node-only methods the security model depends on:
    • dig.getAnchoredRoot — resolves the CHIP-0035 on-chain head via coinset.org (lib.rs:721-743); the trusted root for mandatory root-pinning.
    • dig.stage — compiles a local folder into a capsule .dig in-process (lib.rs:768-904).
    • cache.*getConfig/setCapBytes/clear/listCached/removeCached/fetchAndCache (lib.rs:1143-1231).

An agent gates on dig.methods rather than assuming one uniform surface — hence two OpenRPC documents (network + node).

The node profile also carries the peer network methodsdig.getPeers, dig.announce, and dig.getNetworkInfo — which expose peer discovery + the node's NAT-traversal posture over JSON-RPC (adding -32006 PEER_UNREACHABLE); dig.getAvailability + dig.listInventory, the batch pre-fetch check of whether a peer holds a store / root / capsule; and dig.fetchRange, the streaming byte-range fetch behind multi-source download: it streams only a requested [offset, length) of a resource or capsule, with per-range merkle integrity so a downloader fans ranges across confirmed holders and verifies each independently (adding -32007 RANGE_NOT_SATISFIABLE).