tomo docs
home github

Internals: history & transfer

History is what Tomo is really about. This page covers how it stores versions cheaply, keeps capture off the latency path, and moves big files without blocking small edits.

The content-addressed store #

Every version goes into a content-addressed store: the file is split into content-defined chunks with FastCDC, each chunk is identified by its BLAKE3 hash, compressed with zstd, and both the chunk bytes and the version metadata (vector clocks, conflict records, the path index) live in a single SQLite database under .tomo/db/.

your file bytes chunks 16·64·256 KiB BLAKE3 content id zstd compress SQLite .tomo/db split hash id already stored? skip — write nothing

file → FastCDC chunks → BLAKE3 ids → zstd → SQLite, with content-addressed dedup

Why an edit costs ~1% #

FastCDC cuts chunk boundaries based on content, not fixed offsets, so inserting or deleting bytes shifts only the chunks around the edit — not every boundary after it (the flaw that sinks fixed-size chunking). With Tomo's parameters — 16 KiB minimum, 64 KiB average, 256 KiB maximum — a 10 MiB file is roughly 160 chunks. A one-character edit rewrites a single chunk; the other ~159 hash to ids the store already holds and are skipped. New bytes stored: about one 64 KiB chunk, well under 1% of the file. The store even tracks the exact count of newly written chunks as a dedup metric, and the same content hash makes an exec-bit-only change (a chmod) store zero new content — only the mode column changes.

Adaptive capture #

How often should a save become a version? Every keystroke is too much; once a minute loses work. Tomo treats it like a congestion controller. Every canonical change enters a per-file staging buffer, and a flush interval decides when it becomes a version. Under light load that interval is a tiny window; under a storm of saves it escalates, coalescing a burst into one checkpoint; when things go quiet it decays back down.

flush interval pressure → 75 ms rung 0 · light 250 ms rung 1 1 s rung 2 5 s rung 3 · storm decays to purity when idle

the capture ladder: escalate under storms, decay back to ~immediate when idle

Two guarantees are load-bearing, and both are property-tested against synthetic storms:

Rung 0's window is a small min_capture_window_ms (default 75 ms) rather than a hard zero, so every real save still becomes a version — it just flushes ~75 ms later, letting a same-path truncate+write pair collapse into its single final state. (In every-change mode the window is a literal 0 ms.)

Chunked, interleaved transfer #

Small edits and giant files share one connection, and a big transfer must never block a quick one behind it. The threshold is 1 MiB:

The dedup is end-to-end: a chunk the peer already has is never resent. Measured on this design: small-file latency stayed under 7 seconds during a sustained 1 GiB transfer under churn (acceptance scenario 11).

Crash safety #

A kill -9 at any instant must never leave a torn file at a real path or a corrupt history DB. Tomo guarantees this structurally:

You can check the store's integrity any time with tomo db check, which re-hashes every recorded chunk against its BLAKE3 id.

The longer game. Content-addressed, deduplicated, versioned history is the groundwork for a git alternative, which is where Tomo is eventually headed. The SPEC has the full design.
Does history grow forever? Yes, today — there is no pruning or GC yet. In practice it grows slowly for source trees: chunks are content-addressed (identical content across versions and paths is stored once) and zstd-compressed, so a long edit history of text files stays small. Big frequently-rewritten binaries are the case to watch. Pruning policy (age/size caps) is on the roadmap; the store is a single SQLite file you can inspect with tomo db check.