blobarchive.net

docs / walkthrough · updated 2026-08-01

Heads, shared blobs, and retention

BlobArchive separates three questions that are often conflated:

  1. What data exists? Exact blob bytes, addressed by CID.
  2. Which blobs belong to a logical archive? A head and its source manifest.
  3. Which of those bytes must this node keep? Local pin policy.

That separation lets one block participate in many heads without being stored many times.

One blob, two identifiers

An EIP-4844 blob is exactly 131,072 bytes. BlobArchive stores it as one raw IPFS block, without UnixFS wrapping or chunking.

How BlobArchive uses IPFS explains how those blocks are discovered, transferred over Bitswap, and retained by pins.

The blob has two useful names:

Neither can be derived from the other without the blob, so index entries store the explicit mapping. With verify: full, a follower checks both the CID and the KZG-derived versioned hash before serving the blob.

A head is an arithmetic index

Each finalized head covers a slot range beginning at a fixed origin. Slots are grouped into power-of-two windows called segments. A segment contains the ordered versioned-hash-to-CID entries for its window.

The current segment is rewritten as new rows arrive. When coverage crosses its boundary, that segment is sealed and becomes immutable. Its CID is appended to a small directory tree. Finding a slot is arithmetic on the segment number, not a scan through history.

head root
  |
  +-- directory pages
  |     +-- sealed segment -> blob CIDs
  |     +-- sealed segment -> blob CIDs
  |     `-- empty window
  |
  `-- open segment -> current blob CIDs

An empty covered slot is different from an uncovered slot. That three-valued model matters to clients: “there was no selected blob here” can be a stable answer; “this archive has not covered the slot” cannot.

Many heads share the payload

all is the identity filter: every finalized blob. A chain head is a versioned-hash-level filter over the same stream. A slot can contain blobs from several rollups, so filtering whole slots would be incorrect.

Index nodes differ between heads, but payload blocks are shared by CID. The index overhead is tiny relative to 128 KiB blobs. Adding an Arbitrum- or Base-specific head to a node already retaining all does not duplicate that chain’s blob bytes.

The same is true across mutable and finalized generations. A block remains live as long as any current pin reaches it.

Retention is local policy

A head’s signed root says what the writer operator published. It does not force a follower/replica operator to fund the writer’s retention policy.

An embedded follower supports three policies per head:

PolicyWhat remains locally available
fullThe complete selected DAG
windowThe complete index plus blob bytes in a trailing time window
noneThe complete index, without a promise to retain payload bytes

The pin reconciler adds the new desired pins before removing retired ones. Garbage collection marks the union of every head’s pins, so shared blocks survive whenever at least one policy still needs them.

A standalone Kubo replica is intentionally narrower: every selected head is retained in full. It commits one named recursive generation pin in Kubo and keeps the previous committed generation until the replacement is complete.

Online collection

The embedded store uses an online epoch collector. At a short cut it snapshots the pinned reachability set M and begins recording every application-touched multihash T. Sweep may delete only blocks outside M ∪ T.

Normal reads and writes continue during the expensive mark and sweep. A separate integrity scrub validates stored bytes; a successful reclamation pass does not pretend to be a full-store corruption scan.

Read the data-structure illustrations for the exact DAG shapes and operations guide for GC, scrub, backup, and recovery.