IPFS chunking defaults
Every parameter atfs uses to chunk a large blob into a UnixFS DAG, fully documented — and the recommended match for a pinning application.
Every blob atfs stores gets a blessed CID the moment it is stored. This
CID is CIDv1, raw multicodec, sha2-256 multihash, and it is canonical. It
also travels the IPFS network as a single Bitswap block, but only up to MaxRawBlockSize. A larger blob additionally gets a UnixFS DAG built over
the same bytes, so peers can fetch it block-by-block. The DAG’s root is a
second, chunker-dependent CID. atfs reports it as ipfsRoot, alongside
the blessed cid, everywhere a dev.atfs.file reference appears.
When a DAG is built
Threshold: 1 MiB (MaxRawBlockSize)
A blob at or under this size needs no DAG: its ipfsRoot is exactly its
blessed cid. A larger blob gets a UnixFS DAG, and ipfsRoot names that
DAG’s root instead.
Chunking parameters
atfs builds a large blob’s DAG exactly as ipfs add --cid-version=1 --raw-leaves --chunker=size-262144 would:
- Chunker: fixed-size, 256 KiB (262144-byte) chunks — every leaf but the last is exactly this size.
- Leaves: raw blocks (
--raw-leaves) — each leaf is the blessed-format raw CID of its own byte range, not a UnixFS-wrapped node. - Layout: balanced.
- Max links per node: 174 — kubo’s own unconfigurable default, not a value atfs sets itself.
- CID version: CIDv1 throughout — dag-pb multicodec for the root and interior nodes, raw multicodec for leaves, sha2-256 multihash either way.
Note
This dual-CID scheme is atfs’s own addition for content too large for one Bitswap block. atproto itself never chunks. Even Bluesky’s large videos are raw-CID blobs served over HTTP or CDN, not IPFS.
First root wins
Chunking is deterministic content addressing: the same bytes, chunked the
same way, always produce the same leaf CIDs and the same root CID. An
application that chunks a file with these parameters computes the same ipfsRoot atfs would compute for the same bytes. It can do this before
ever contacting an instance.
atfs never serves two roots for one blob, though. The first root
committed for a blob’s bytes is the root that instance keeps. This holds
however that root was committed: built by atfs’s own chunker, or adopted
from a caller’s dev.atfs.file reference through dev.atfs.repo.pinFile.
The pinFile response reports the root actually served, in its own ipfsRoot field. That field echoes the request’s ipfsRoot only in two
cases. Either the request named the root already committed, or no root
was committed yet and the request’s root was adopted as-is.
Recommendation
An application that intends to pin content to an atfs instance should
chunk it with the same recipe atfs uses. That recipe is ipfs add --cid-version=1 --raw-leaves --chunker=size-262144, not a library’s own
defaults. Two consequences follow directly from “first root wins” above:
- If the same bytes were already uploaded or pinned on that instance, a
matching
dev.atfs.filereference computes theipfsRootalready committed there. The pin then resolves against existing content, instead of asking the instance to adopt an unfamiliar shape. - If the content is new to the instance, a reference built with matching
parameters becomes the committed root as-is. The
ipfsRootthe pinFile response returns is then guaranteed to equal the one the request sent. Nothing needs reconciling.
A reference chunked some other way still works. It gets adopted as a
foreign DAG shape, as long as nothing is committed for that content yet.
The returned ipfsRoot can still differ from the one the reference
named. This happens when the content was already committed under atfs’s
own chunking, by an earlier upload or pin. Matching the defaults above
keeps an application’s own computed root the same as the one atfs
actually serves.