Entries

An Entry is a canonical-CBOR map describing one directory member. The member's unix metadata (the recorded subset of lstat, §7.2) lives here, in the parent — content blocks are pure content:

a.txt and b.txt hold the same bytes with a different mode/owner ⇒ still one content block. Every stat field lives in the parent, so:

  • listing with metadata = 1 block read
  • chmod = rewrite this dir + spine, content untouched
  • dedupe = by content alone, metadata never interferes

#7.1 Type

Required field t — one of:

tmember type
"f"regular file
"d"directory
"l"symbolic link
"c"character device
"b"block device
"p"fifo
"s"socket

#7.2 Stat fields

All optional, and valid on every type except as noted (ino is invalid on "d"). There is no default-omission magic: an absent field means "not recorded", never an implied default value. Writers MUST NOT invent defaults; readers MUST preserve the distinction between absent and any concrete value.

The recorded set is a deliberate subset of POSIX stat: access and status-change times (atime, ctime), birth time, the containing filesystem's device number, and link counts are volatile or derived and are not recorded.

fieldtyperule
mintegerpermission bits, 0 … 0o7777 (type bits live in t).
mtintegermtime seconds; MAY be negative; §2.3 range.
mtnintegermtime nanoseconds, [1, 10^9) on the wire. Zero nanoseconds is canonically absent: writers MUST omit the field when 0, and readers MUST reject an encoded 0 (one value, one encoding). MUST NOT appear without mt.
uid, gidinteger≥ 0, ≤ 2^32 − 1.
un, gntextowner/group names (tar style), ≤ 256 UTF-8 bytes each, no U+0000.
xmapxattrs: name (text, 1–255 UTF-8 bytes, no NUL) → byte string ≤ 65536 bytes. Binary-safe.
inointeger≥ 0; hardlink group id (see below). Invalid on "d" — POSIX forbids hardlinked directories.

Sub-second times. mt/mtn follow the POSIX timespec convention: mt is the floor of the timestamp in whole seconds and mtn the non-negative nanosecond remainder — a time of −0.5 s is mt: −1, mtn: 500000000.

Hardlink groups. The scope of an ino group is a single root's tree: entries belong to one group when they share an ino value and are reachable within the same tree. ino values carry no meaning across roots or stores. All entries of one group MUST be identical maps — same type, content (sz/d/r), mode, ownership, times, xattrs — differing only in the name and directory that reach them: they are one inode, and an inode has one stat. The link count is the group size, computed by consumers, never stored. This is a whole-tree invariant, deliberately not checkable block-locally (a shared subtree cannot see its siblings), so it is checked only at materialization or full-tree validation. Consumers MUST treat a violation as an error — never resolve it by picking one variant.

#7.3 Per-type fields

  • "f"sz (integer ≥ 0) REQUIRED. The content is exactly one of:

    • d: inline bytes, with length(d) == sz — used when the whole file is one chunk of length ≤ the inline threshold (§9);
    • r: a Ref — raw-codec when the file is a single chunk (the referenced block's length MUST equal sz), dag-cbor-codec pointing at an "f" node (whose own sz MUST equal the entry's sz) when multi-chunk;
    • neither d nor r, if and only if sz == 0 (the empty file).

    d and r together MUST be rejected.

  • "d"r REQUIRED: a dag-cbor-codec Ref to a "d" node.

  • "l"tgt REQUIRED: the link target, text, 1–4096 UTF-8 bytes, no U+0000. No block. A writer ingesting a native symlink whose target is not valid UTF-8 MUST reject it (§6); a consumer materializing tgt MUST treat it as an untrusted path (§16).

  • "c", "b"rdev REQUIRED: [major, minor], a 2-element array of integers ≥ 0. No block.

  • "p", "s" — no extra fields. No block.

A field that is not a stat field (§7.2) and not valid for the entry's type (e.g. tgt on a file, sz on a symlink, any unknown key) MUST be rejected.

unfs  A filesystem you can put in any key–value store.