# unfs: A Content-Addressed Filesystem Data Format

> 

**Version 0 — Working Draft**

## Abstract

This document specifies **unfs**, a data format for storing
filesystem trees — files, directories, symlinks, devices, and their unix
metadata (the recorded field set of [§7](/spec/entries)) — as an immutable, content-addressed graph of blocks in any
key–value store, together with a byte-stream interchange format ("packs" —
a deterministic profile of the IPLD CARv1 archive format, [§12](/spec/pack-format)) for
moving such graphs between stores.

The format uses a single codec — a strict subset profile of IPLD DAG-CBOR,
so every block is readable and link-traversable, and every pack readable
and transportable, by generic IPLD tooling —
a single configurable hash function per store, and stores all recorded unix
metadata inline in directory entries. It is fully deterministic: the same logical content
produced with the same parameters yields byte-identical blocks and identical
references, regardless of how input was sliced, buffered, or ordered.

This specification is self-contained. An implementation can be written from
this document and validated against the test vectors in [Appendix A](appendix-a-test-vectors) without
reference to any existing implementation.

## 1. Introduction

### 1.1 Data model at a glance

A store contains exactly two kinds of things:

- **Blocks** — immutable byte strings addressed by their hash. Blocks form a
directed acyclic graph (DAG).
- **Named refs** — mutable pointers binding a human-readable name to a block
reference. Names are the only roots of the graph.

<mermaid code="graph LR
  subgraph names["names (mutable)"]
    main["refs:heads:main"]
    backup["refs:backup"]
  end
  subgraph blocks["blocks (immutable, hash-addressed)"]
    root["&quot;r&quot; root"]
    entry["Entry {t:&quot;d&quot;, r}"]
    dir["&quot;d&quot; dir"]
    readme["&quot;README.md&quot; {t:&quot;f&quot;, sz,m,mt,uid,gid, d}<br/>inline bytes, no block"]
    big["&quot;big.bin&quot; {t:&quot;f&quot;, sz, r}"]
    link["&quot;link&quot; {t:&quot;l&quot;, tgt}"]
    sub["&quot;sub&quot; {t:&quot;d&quot;, r}"]
    subdir["&quot;d&quot; dir"]
    fnode["&quot;f&quot; file node<br/>c: [[sz,r-raw],[sz,r-raw], …]"]
    chunk1["raw chunk"]
    chunk2["raw chunk"]
  end
  main -->|n| root
  backup -->|n| dir
  root -->|e| entry
  entry -->|n| dir
  dir --> readme
  dir --> big
  dir --> link
  dir --> sub
  sub -->|n| subdir
  big -->|n| fnode
  fnode -->|r| chunk1
  fnode -->|r| chunk2
">



</mermaid>

Edge labels: `n` = dag-cbor (node) CID link, `r` = raw CID link.

Three properties fall out of this shape and drive the rest of the
specification:

- **Metadata lives in the parent's entry, not the child's block** ([§7](/spec/entries)).
Changing a file's mode rewrites the directory that names it; the content
blocks are untouched. Two files with identical bytes but different
modes/owners share one content block.
- **Every edit rewrites only the spine** from the root to the changed entry
([§8](/spec/directories-and-paging), [§11](/spec/path-semantics)). Every untouched subtree carries over by reference. Old roots
remain valid and readable — snapshots are free.
- **A block is stored and deduplicated by its multihash alone, while
traversal identity is the full CID** — content codec + multihash
([§4](/spec/refs)). This single asymmetry must be respected by every
deduplication set in a conforming implementation.

### 1.2 Design goals

In priority order: unix fidelity, small size, performance, simplicity.

### 1.3 Roles and trust model

This specification distinguishes **writers** (produce blocks, refs, and
packs) and **readers** (consume them). Writers within one store are assumed
cooperative (trusted to emit only conforming blocks); they need not be
exclusive — the concurrency properties of each operation are given in [§15.3](/spec/operational-considerations),
and only garbage collection requires exclusive access ([§15.2](/spec/operational-considerations)). Readers MUST
treat all input — blocks fetched from a store, pack bytes, ref values — as
potentially foreign or hostile, and MUST enforce every validation rule in
this document ([§13.2](/spec/conformance)).

### 1.4 Scope and non-goals

In scope: block encodings, the entry/node data model, chunking algorithms,
the key–value storage mapping, the pack interchange format (a CARv1
profile, [§12](/spec/pack-format)), and conformance requirements for readers and writers.

Out of scope: transport protocols, access control, encryption,
multi-writer coordination, and any particular host-language API.
