Skip to content
LimniFS

.scenarios · what is limnifs for

Five lakes.
Five uses.

One format, five workloads. The same .lim image that packages a Ruby app for a desktop user also serves as an OCI-style container layer, an IPFS-pinnable DAG, a decades-deep archival record, and a tiny CI delta. The format doesn't pick a winner — it makes them the same shape.

.audience-map

The five scenarios below come from theLimniFS design doc §12. Each audience forces a specific feature into the format: tebako drives the minimal core; CI drives deltas and range streaming; containers drive the kernel path; archival drives EC and DMS; IPFS drives multihash identity and CAR interop. Read the ones that match your workload; skim the others.

01

.lim images for packaging tools

Software packaging.

The problem

Packaging tools need a filesystem image format that is content-addressed, compressed, delta-friendly, and GPL-free. Existing options (DwarFS, SquashFS) are either GPL-3 locked, monolithic, or lack crypto, deltas, and distribution. Tools need a format they can adopt without licensing or architectural compromise.

How LimniFS solves it

  • LimniFS is a **standalone filesystem format** — the .lim wire format. It has its own spec, its own CLI (limni), its own reader and writer. No dependency on any specific packaging tool.
  • Any packaging tool can adopt .lim as its image format. Tebako is one example — it adopts LimniFS as a backend in its libtfs adapter layer, alongside the legacy DwarFS reader.
  • Identity is content-addressed — the same app tree, pressed twice, produces the same ManifestRoot. Reproducible builds are structural.
  • Apache-2.0 / MIT licensed. No GPL-3 anywhere. Tools can embed, fork, or reimplement without licensing risk.
SOURCE./my-appapp.rbGemfileGemfile.lockconfig/lib/native/limni limn.LIMruntime+ gems+ appmy-app.limb3:...mount / runEND USERany platform$ limni runlinux x86macOS armwindowsembeddedLimniFS is independent. Downstream tools (e.g. tebako) adopt .lim as their image format.
.lim images for packaging tools
# create a .lim image with LimniFS's own CLI
limni limn ./my-app -o my-app.lim

# verify content-addressed identity
limni verify my-app.lim

# mount for local use
limni mount my-app.lim /mnt/app

# downstream tools (e.g. tebako) consume .lim images
# via their own adapter layers — LimniFS is format, not pipeline

02

successive builds · tiny deltas · HTTP range

CI/build artifact distribution.

The problem

Every CI run produces a multi-gigabyte artifact. Most of it is identical to yesterday's build — but you ship the whole thing every time because tarballs don't diff. Bandwidth bills scale with full image size × number of builds × number of consumers.

How LimniFS solves it

  • **Each build is a .lim image** with content-addressed drops. Same drop = same DropId = shared across builds.
  • Successive builds share 90%+ of their drops. The delta between build N and N+1 is a small .lim referencing the base.
  • Clients mount the delta as an overlay (Tier 1, no data movement). Only the changed drops are fetched.
  • HTTP range streaming fetches exactly the slice requested — no full-image download for a one-file read.
BUILD N2.1 GiB100% of dropsbuild-N.limBUILD N+12.1 GiB+1% changedbuild-N+1.limdiffDELTA21 MiB1% of N+1HTTP rangeclient 1client 2client 3...CLIENT MOUNTmount build-N.lim --overlay delta-N-to-N+1.limfetches only the 1% new — the overlay resolves the rest from cached build NSame content = same DropId = shared across builds. Deltas shrink to the changed tail.
successive builds
# CI: produce build N+1
limni limn ./dist -o build-N+1.lim

# compute the delta against build N
limni delta build-N.lim build-N+1.lim -o delta-N-to-N+1.lim

# clients apply the delta as an overlay; only new drops are fetched
limni mount build-N.lim --overlay delta-N-to-N+1.lim /mnt/build

03

composefs · signed roots · kernel path

Container-adjacent images.

The problem

OCI images are not content-addressed at the right granularity. SquashFS has no signed roots. composefs requires metadata in a specific format. None of them compose: signing, content addressing, range streaming, and kernel-native mounting all live in different ecosystems.

How LimniFS solves it

  • **.lim images are content-addressed at the drop level** (small files dedup) and Merkle-rooted at the image level (signed identity).
  • The metadata layer is a deterministic Merkle B-tree — naturally aligned with composefs's verified-mount model.
  • Optional sigstore signature bundle over the ManifestRoot; the kernel verifies before mounting.
  • Slabs cache locally; the locator layer fills the cache from any registry, mirror, or P2P source.
REGISTRYmy-app:latestmy-app:v1.2sidecar:v3base:v1pull.LIMsignedMerkle rootverifiedsigstore ✓composefsKERNELverified mountEROFS metadatadrops from cacheno daemonLOCAL CACHEslab cache filled by locator layer; reused across imagessame drop in two images = same cache entry (content-addressed)Signed Merkle root + kernel-native verified mount. composefs-aligned by design.
composefs
# publish a signed .lim to a registry
limni push my-app.lim registry.example.com/my-app:latest --sign fulcio

# mount via the kernel-native path (Linux 6.x with composefs)
limni mount my-app.lim /mnt/app --kernel-path composefs --verify-signature

04

erasure coding · DMS escrow · cold-tier ratio

Archival & compliance.

The problem

You must keep records for decades. Drives fail. Custodians leave. Storage classes are expensive. Compression that was good enough at write time is wasteful ten years later — but re-encoding breaks every existing reference.

How LimniFS solves it

  • **Per-slab Reed-Solomon erasure coding** (k of k+m) keeps data readable even when whole storage backends disappear.
  • **Shamir k-of-n DMS escrow** splits the image key across custodians; the image stays readable if any k of them cooperate.
  • **Hypolimnion deepening** re-compresses cold drops in the background (zstd, lzma, brotli per Seine class). Identity never changes; references stay valid.
  • Manifest carries EC + DMS metadata inline — the format itself encodes the durability story.
.LIMrecordsEC k=4, m=2DMS Shamir 3/5S3 coldstandardRS shardGlacierdeep archiveRS shardIPFS pindistributedRS shardtapeair-gappedRS shardDMS ESCROW · SHAMIR 3-OF-5key split across 5 custodians; any 3 cooperate to reconstructsurvives 2 custodians disappearing; metadata-only; core reader ignores unless askedEC for availability. Shamir for key continuity. Deep codecs for ratio. Identity forever.
erasure coding
# archive with redundancy + escrow
limni archive records/ -o records.lim \
  --ec k=4,m=2 \
  --dms shamir:k=3,n=5 --custodians alice,bob,carol,dan,eve \
  --tier hypolimnion --codec zstd:19

# ten years later, deepen again with newer codecs (identity unchanged)
limni deepen records.lim -o records-v2.lim --codec brotli:11

05

multihash identity · CAR interop · P2P locator

IPFS / decentralized distribution.

The problem

You want to distribute without a central server. Nodes come and go. Some lie. Verification must not require a trusted party. Existing formats either don't content-address (SquashFS), or content-address at the wrong granularity (IPFS UnixFS — one block per file, no small-file packing).

How LimniFS solves it

  • **DropId = BLAKE3(plaintext)** is multihash-compatible. Every drop is pinnable as an IPFS CID.
  • Slabs batch many drops into one object — the Taobao TFS lesson. No per-block overhead.
  • Overlay chains pin as DAGs; the manifest references base slabs at their CIDs.
  • Optional limni-p2p:// locator races mirrors in parallel; lying locators are caught by BLAKE3 == DropId verification and demoted.
.LIMDropId =BLAKE3(plaintext)b3: multihashnode AEUnode BUS-Enode CASnode DUS-Wnode ESAnode FOCslab → IPFS CID ·manifest → DAG root ·overlay → chain of roots
multihash identity
# export a .lim as an IPFS CAR (one DAG, pinnable anywhere)
limni export-car my-app.lim -o my-app.car

# import a CAR back into a .lim (preserves identity)
limni import-car my-app.car -o my-app.lim

# pin every slab of an image to your local IPFS node
limni pin --ipfs my-app.lim

.which-lake-are-you

Pick one. Or all five.

The format doesn't care. The same .lim works for every scenario above.