Everything in Unix is a file, except for the filesystem itself. Until now.
C4 is a file management system designed to simplify distributed systems. It decouples file storage and transport from organization and metadata by providing a standardized content-derived identifier, and using these identifiers in place of file content in a simple, readable, text-based filesystem format.
$ echo "hello" | c4
c447Fm3BJZQ62765jMZJH4m28hrDM7Szbj9CUmj4F4gnvyDYXYz4WfnK2nYRhFvRgYEectEXYBYWLDpLo6XGNAfKdtThat 90-character string is a SMPTE ST 2114:2017 identifier — an international standard built on SHA-512. Run it on any machine, in any language, ten years from now. Same input, same ID. Unlike a UUID, which is assigned arbitrarily and requires coordination to be meaningful, a C4 ID is discovered from the data itself. If two people have a file with the same ID, they have identical copies of the same file.
C4 extends this idea to entire filesystems. c4 id produces a c4m
file — a plain-text description that captures the identity of every
file and directory in a tree:
$ c4 id ./project/
-rw-r--r-- 2026-03-18T22:22:57Z 13 README.md c44iCq6un9W47x7ydjJSWp4arMJ...
drwxr-xr-x 2026-03-18T22:22:57Z - src/ -
-rw-r--r-- 2026-03-18T22:22:57Z 66 main.go c43Q4j81SxGkV9FhbeW23YrMTj6...
drwxr-xr-x 2026-03-18T22:22:57Z 66 src/ c44nbgL6nkBWsEBDCUCr4LufsjVhJt...
c45k2Jd... ← the final line is the tree's IDEach line looks like ls -l with a content ID at the end — and the
output streams: file lines print the moment they're hashed
(directory aggregates fill in via a refinement patch at the end,
because a folder's ID is the fingerprint of its contents). The final
line is always the whole tree's ID. Need machine-independent identity?
c4 id -q -m c projects modes and timestamps away, so the same bytes
give the same ID on any machine, clock, or umask.
A c4m file is just text. Pipe it, grep it, diff it, email it. The
format is designed to compose with awk, sort, grep, and the
rest of the Unix toolkit — no special tools required, though C4
provides some that are useful.
brew install mrjoshuak/tap/c4Pre-built archives for macOS, Linux, and Windows: c4toolkit releases
go install github.com/Avalanche-io/c4/cmd/c4@latest
go install github.com/Avalanche-io/c4sh@latestpip install c4py # Python
npm install @avalanche-io/c4 # TypeScript / JavaScriptSee c4toolkit for the full suite and version matrix.
The install brings c4-farm (and c4-agent on macOS and Linux), and
c4 farm ... runs it. Join a studio's farm from any machine with only
c4 installed, with no Docker, kubectl or cluster credentials:
c4 farm login https://farm.studio.example # sign in once
c4 farm submit render shot010.c4m # plan, estimate, submit
c4 farm ls # the queue, in run order
c4 farm watch JOB # progress, deadline, slack
c4 farm get JOB ./out/ # the output, every ID checkedOr make a farm on this machine, which is the one command that needs Docker:
c4 farm upc4 <name> runs any c4-<name> on PATH the way git finds its plugins;
c4 help lists the tools installed. The farm itself is
c4-operator and its
console is c4-hud; their
READMEs take it from here.
Know what changed. Snapshot a directory, come back later, compare:
c4 id ./deliverables/ > monday.c4m
# ... work happens ...
c4 diff monday.c4m ./deliverables/Build history. Append diffs to a c4m file to create a version chain:
c4 id ./project/ > project.c4m # snapshot
c4 diff project.c4m ./project/ >> project.c4m # append changes
c4 log project.c4m # see what changed
c4 patch -n 1 project.c4m # recover the original stateSnapshot into the store. One command, one ID back. The ID prints
only after everything it names is on stable media — if it printed,
you can get it back, even after kill -9 or power loss:
c4 id -s ./final/ # snapshot; streams the listing,
# ends with the durable ID
SNAP=$(c4 id -s -q ./final/) # script capture: THE ID, one line
c4 cat "$SNAP" # the root listing
c4 cat "$SNAP"/src/parser.go # extract one file, verified
c4 log # every snapshot the store holdsThe store can be a local directory, an S3-compatible object store, or both. Content goes in once, comes out by ID.
Make a directory match a description. Declarative — you say what
the result should be, C4 figures out how to get there. Dry run by
default; --force snapshots the directory first, so every restore is
undoable:
c4 restore "$SNAP" ./dir/ # dry run: print the plan
out=$(c4 restore --force "$SNAP" ./dir/)
undo=$(printf '%s\n' "$out" | sed -n 1p)
c4 restore --force "$undo" ./dir/ # undo — which prints its own undoNothing starts until all required content is confirmed available, and the undo handle is journaled before the first destructive operation.
Work with Unix tools. One entry per line, fields in predictable positions:
awk '{print $NF}' project.c4m | sort | uniq -d # find duplicates
grep '\.exr ' project.c4m | wc -l # count EXR filesMerge two trees. Combine branches, deliveries, or any two c4m files into one:
c4 merge branch-a.c4m branch-b.c4m > merged.c4mConflicts (same path, different content) are reported. Non-overlapping entries are combined.
Re-scan fast. A prior full description is a reuse guide: files whose size and mtime are unchanged (and safely older than the guide's scan — git's racy-index rule) keep their recorded IDs without being re-read:
c4 id ./project/ > full.c4m # full description once
c4 id -c full.c4m -q ./project/ # fast re-scan (trusted mtimes)
c4 id --verify -c full.c4m ./project/ # audit: full re-hash, report
# changes hidden under unchanged
# size+mtime| Command | What it does |
|---|---|
c4 id |
Describe files, directories, or c4m files; snapshot with -s |
c4 restore |
Make a directory match a description (dry run by default) |
c4 cat |
Retrieve a store object by ID or ID/path, verified |
c4 diff |
Compare two states (directories, c4m files, or store IDs) |
c4 patch |
Resolve patch chains to c4m text (never touches directories) |
c4 merge |
Combine two or more trees |
c4 log |
List the store journal, or a chain's sections |
c4 split |
Split a patch chain |
c4 explain |
Human-readable narration of what a command would do |
c4 paths |
Convert between c4m format and plain path lists |
c4 intersect |
Find common entries between two c4m files |
Nothing is written anywhere without -s (or restore --force), and
-s writes only inside the store. c4 <path> is a read-only
shortcut for c4 id <path>; c4 -s <path> snapshots. echo "data" | c4 prints the ID of stdin (nothing stored); echo "data" | c4 -s
stores it. Every verb's --help is its reference page.
A c4m file is a complete filesystem description in plain text. Each line has permissions, timestamp, size, name, and C4 ID. Readable, editable, diffable, composable.
A 10,000-entry c4m file is about 1.4 MB. It describes the identity of every file in the tree regardless of how large those files are. The description is the lightweight handle; the content is the heavy thing it refers to.
import "github.com/Avalanche-io/c4"
id := c4.Identify(strings.NewReader("hello"))
fmt.Println(id)
// c447Fm3BJZQ62765jMZJH4m28hrDM7Szbj9CUmj4F4gnvyDYXYz4WfnK2nYRhFvRgYEectEXYBYWLDpLo6XGNAfKdtC4 IDs are 90-character base58 strings — SHA-512 with a c4 prefix.
URL-safe, filename-safe, double-click selectable.
Zero external dependencies. Go 1.16+.
- C4 Framework Universal Asset ID (video)
- C4 ID Whitepaper
- CLI Reference
- Getting Started
- FAQ — design decisions (SHA-512 permanence, c4m format, store scaling)
C4 is part of a cross-language ecosystem. Every tool reads and writes the same c4m format and produces identical C4 IDs:
| Tool | Language | What it does |
|---|---|---|
| c4 | Go | CLI — identify, snapshot, restore, diff |
| c4sh | Go | Shell — cd into c4m files, browse, copy |
| c4py | Python | Library — scan, diff, verify, store |
| c4git | Go | Git filter — version large files |
| c4ts | TypeScript | Browser + Node.js — zero dependencies |
| c4-swift | Swift | Apple platforms — Sendable, Codable |
| libc4 | C | Embed in any application |
See c4toolkit for the full suite, install options, and version matrix.
Apache 2.0