Layer 7 · Content replication — the flywheel
Canonical references:
dig-node(the composition root that wires the loop),dig-dht(the Kademlia provider DHT andfind_providers),dig-gossip(the signed holdings-announce wire, opcode 222),dig-download(the multi-source, merkle-verified fetch),dig-store-cache(the bounded on-disk capsule cache), anddig-store(the.digcapsule format). This page is the ecosystem-wide spec for how content spreads; the peer network is the transport it rides, and verification & provenance is the integrity gate every stage defers to.
DIG has no origin server and no CDN. A capsule is available because some set of nodes holds it, and that set is not curated by anyone — it grows wherever the content is actually read. This page specifies that mechanism.
The thesis: a read makes a holder
Every stage below feeds the next, and the last stage feeds the first:
connect → discover → fetch + verify → cache → announce → (someone else discovers you) → …
The consequence is that availability tracks demand. A capsule nobody reads is held by its publisher. A capsule read widely is held widely, close to where it is read, with no operator deciding so and no coordination step. Churn heals the same way: a departed holder's provider record expires and is retracted, while every fresh read mints a new holder.
Two properties make the loop safe rather than a rumour mill, and both are enforced, not assumed:
- Every peer is untrusted. A holder claim is a claim. Content is accepted because it verifies against the merkle root and the chain-anchored generation, never because of who served it. A false claim buys the liar one failed dial.
- A stranger cannot decide what this node stores. Caching and announcing are side effects of a node's own reads. Serving another peer causes neither.
Stage 1 · Connect
A node opens mutually-authenticated links to peers before it can discover or fetch anything. Peer identity is the hash of the TLS public key — peer_id = SHA-256(TLS SubjectPublicKeyInfo DER) — and the dial follows the ordered NAT-traversal ladder, preferring a direct path and relaying only as a last resort.
This stage is specified in full on the peer network page. Everything below rides that same authenticated transport; there is no unauthenticated discovery or fetch traffic.
Stage 2 · Discover — who holds this capsule
Discovery answers one question: which peers hold content key X? It has two halves, a durable one and a real-time one, and they carry different guarantees.
2a · The provider DHT — durable, network-wide
dig-dht is a Kademlia DHT whose values are provider records: "peer P holds content key K, reachable at these addresses, until expires_at". The content key is derived from the store id, generation root, and (for a resource) the retrieval key — the derivation and the four DHT methods are specified under the DHT.
find_providers(content) is a distributed iterative lookup: the node queries the α closest contacts it knows, recurses toward peers closer to the key, converges on the k closest, and returns the union of the holder records it collected. A locally-held record is a fast path, never the whole answer.
Two properties are load-bearing:
- Discovered records are hearsay. A record learned during a lookup was asserted by some peer about some other peer, and nothing authenticated it. Such records are used on the fetch path — where a wrong candidate costs one failed dial, because the merkle bind catches it — and are never re-served in answer to another node's lookup and never republished. Only records this node holds authoritatively (the mTLS-verified announcer's own, or a signature-checked ingest) are served or published.
- Records are TTL'd and republished. A holder refreshes its records before they expire; a holder that stops refreshing drops out of discovery on its own. That is how the network forgets a node that went away without saying so.
2b · The holdings announce — real time
The DHT alone converges only as fast as the next PUT and the next TTL. dig-gossip opcode 222, HoldingsAnnounce, adds the real-time half: a signed, batched statement of add/remove deltas to a peer's holdings, flooded across the gossip pool, which upserts and removes provider records at receiving nodes within seconds.
The announce is signed with the announcer's TLS leaf key over the dig:holdings:v1 preimage, and acceptance is fail-closed. The wire contract — the preimage, the batch cap, and the five acceptance checks — is normative and specified on the peer network page.
Because an inbound announce mutates another node's view of who holds what, the ingress is guarded over threat classes rather than individual attacks:
| Class | What stops it |
|---|---|
| Forged attribution — naming another peer as a holder, or as not a holder | The provider identity is taken from the signature alone. No code path accepts a caller-supplied provider id, so an announcer can only ever speak about itself. |
| Identity re-spelling — one signer wearing many names | The peer id is canonicalized once, up front; every later comparison, map key and sink argument uses that single form. |
| Amplification — one cheap message causing expensive work | The ingress performs no egress: it never re-broadcasts, dials, probes, or fetches. Its cost is bounded local map work. |
| Flood and Sybil eviction — pushing an honest holder out of a full provider set | Two token buckets at one chokepoint, keyed differently (per announcing provider, and per transport sender). A rejected announcement charges neither, so the limiter cannot itself be weaponised. |
| Replay — resurrecting a retracted record | Two independent barriers: a per-provider monotonic sequence watermark, and a bounded-freshness check on the signed timestamp. Either alone is escapable; a removal in particular carries no expiry of its own. |
| Self-poisoning — replaying a node's own announce back at it | An announce attributed to the receiving node's own peer id is dropped. Only a node decides what it holds. |
| Unbounded state — the guard tables becoming the denial of service | A rejected announcement allocates nothing; admitted entries are capacity-bounded and evicted LRU. |
2c · This is not the hop-by-hop ask
Two different discovery mechanisms exist and they must not be confused.
| Provider DHT + holdings announce (this page) | Recursive hop-by-hop ask | |
|---|---|---|
| Shape | An iterative Kademlia lookup: the seeker itself queries progressively closer peers and collects records. | A transitive ask: a peer that lacks the content asks its own peers, holding each hop's request open until its subtree answers. |
| What comes back | Provider records — holder identities and addresses. The seeker then dials a holder itself. | An answer routed back along the hops, either the bytes or the holder's dial address. |
| Who carries the bytes | Never the discovery path. The fetch is a separate, direct connection to a holder. | Optionally the hops themselves. |
| Cost borne by | The seeker. | The relaying intermediaries, which is why it needs its own bounds. |
They have different trust and topology properties. A statement about one is not a statement about the other.
Stage 3 · Fetch and verify
With a holder set in hand, dig-download fetches the content — from multiple sources where they exist, by byte range, resumable — and verifies as it goes. The client-to-node resolution ladder governs which node a client asks in the first place; this stage governs what happens once bytes are moving.
Verification is not a post-hoc check on the assembled result:
- Per-range integrity. Each fetched range carries a merkle proof; a reassembly drawn from several peers is bound to one committed generation root, so no mixture of sources can forge a resource.
- The proof must be complete. The verifier requires that the named resource leaf is the proof's leaf, that the proof folds to its root, and that the root is the download's committed generation root. A half-specified binding — a proof with no root, or a root with no proof — fails closed.
- A whole capsule self-verifies on install. A capsule fetch carries no per-resource proof; its integrity comes from the capsule format's own structure.
- The root is anchored on chain. A generation is only ever served as current when its root equals the chain-anchored tip. The worst a stale or attacker-chosen root can achieve is caching a real but older generation — never fabricated content.
Verified is not the same as safe. A capsule that verifies is exactly the bytes its publisher committed; it is still content from a stranger, and it is treated as untrusted input by everything that renders or executes it. Integrity is a statement about authorship, not about intent — see the self-defending module for how served content is confined.