.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
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
.limwire 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
.limas 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.
# 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 pipeline02
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
.limimage** with content-addressed drops. Same drop = sameDropId= shared across builds. - Successive builds share 90%+ of their drops. The delta between build N and N+1 is a small
.limreferencing 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.
# 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/build03
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
- **
.limimages 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.
# 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-signature04
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.
# 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:1105
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 byBLAKE3 == DropIdverification and demoted.
# 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.