Architecture
Big picture
composefs has two halves: a writer that turns a directory tree into an EROFS metadata image, and a mount helper that stacks that image under overlayfs at runtime. The writer lives in liblcfs (the libcomposefs/ directory) and is driven by the mkcomposefs tool. The mount path lives in the same library and is driven by mount.composefs. Both sides agree on a small composefs-specific header that prefixes the EROFS image (src/libcomposefs/lcfs-erofs.h:18).
Components
liblcfs writer core (libcomposefs/lcfs-writer.c)
Builds and walks the in-memory tree. lcfs_build recursively reads a directory into a tree of lcfs_node_s nodes (src/libcomposefs/lcfs-writer.c:1507). lcfs_write_to is the public entry point that validates options, allocates a write context, and dispatches to the EROFS serializer (src/libcomposefs/lcfs-writer.c:389). lcfs_compute_tree does a breadth-first walk that assigns inode numbers, fixes directory link counts, sorts extended attributes into canonical order, and tracks the minimum mtime (src/libcomposefs/lcfs-writer.c:176).
EROFS serializer (libcomposefs/lcfs-writer-erofs.c)
The largest file. lcfs_write_erofs_to clones the input tree, rewrites it for overlayfs, computes inode layout, then writes the header, EROFS superblock, inodes, shared xattrs, and data blocks (src/libcomposefs/lcfs-writer-erofs.c:1385). Inode sizing and NID (node ID, the EROFS inode locator) assignment happen in compute_erofs_inodes (src/libcomposefs/lcfs-writer-erofs.c:635).
Mount helper (libcomposefs/lcfs-mount.c)
lcfs_mount reads the image header, checks the magic, and dispatches to the EROFS-over-overlay path (src/libcomposefs/lcfs-mount.c:635). lcfs_mount_erofs_ovl mounts the EROFS image, falling back to a loop device when needed, then stacks overlayfs on top (src/libcomposefs/lcfs-mount.c:573).
On-disk format headers (libcomposefs/lcfs-erofs.h, libcomposefs/erofs_fs.h)
lcfs_erofs_header_s is the composefs-specific header written before the EROFS superblock (src/libcomposefs/lcfs-erofs.h:18). erofs_fs.h carries the kernel EROFS on-disk structures.
Tools (tools/)
mkcomposefs creates images from a directory or a dump file (src/tools/mkcomposefs.c:1476). mount.composefs is the mount helper invoked by mount -t composefs (src/tools/mountcomposefs.c:100). composefs-info and composefs-dump inspect images; cfs-fuse offers an optional FUSE (Filesystem in Userspace) mount.
How a request flows
Creating an image with mkcomposefs <dir> <out.cfs>:
mkcomposefsbuilds the tree withlcfs_build, deliberately clearing the digest and inline flags and settingLCFS_BUILD_NO_INLINEso digesting and inlining can be parallelized afterwards (src/tools/mkcomposefs.c:1687, call atsrc/tools/mkcomposefs.c:1692).lcfs_buildreads the directory withreaddir, recurses into subdirectories, loads each entry into a node, and links it to its parent withlcfs_node_add_child(loop atsrc/libcomposefs/lcfs-writer.c:1542, recursion atsrc/libcomposefs/lcfs-writer.c:1575, attach atsrc/libcomposefs/lcfs-writer.c:1596).The tool computes digests and, if
--digest-storewas given, fills the content-addressed store (src/tools/mkcomposefs.c:1699,src/tools/mkcomposefs.c:1702).It sets
options.format = LCFS_FORMAT_EROFSand callslcfs_write_to(src/tools/mkcomposefs.c:1714,src/tools/mkcomposefs.c:1718).lcfs_write_tovalidates options, creates the context, and callslcfs_write_erofs_tofor the EROFS format (src/libcomposefs/lcfs-writer.c:419).lcfs_write_erofs_toclones the tree, rewrites it for overlayfs, runslcfs_compute_tree, computes shared xattrs and inode layout, then writes header, superblock, inodes, and data (src/libcomposefs/lcfs-writer-erofs.c:1403onward).
The mirror operation, mount.composefs <image> -o basedir=<store> <mnt>, opens the image and calls lcfs_mount_fd (src/tools/mountcomposefs.c:255), which reaches lcfs_mount, checks the header magic against LCFS_EROFS_MAGIC, and hands off to lcfs_mount_erofs_ovl (src/libcomposefs/lcfs-mount.c:653).
Key design decisions
The central decision is that composefs stores no data of its own; the EROFS image is metadata only, and file content lives in an external content-addressed store referenced through trusted.overlay.redirect (src/README.md:14-49, src/libcomposefs/lcfs-internal.h:50). This is what lets unrelated images share content on disk and in the page cache (src/README.md:70-77).
The second is reproducibility. The writer always clones the input tree before mutating it for EROFS, so the caller's tree is never altered (src/libcomposefs/lcfs-writer-erofs.c:1403). It aggregates the minimum mtime across the tree and uses that as the EROFS build_time, so the same input produces the same bytes (src/libcomposefs/lcfs-writer.c:225, used at src/libcomposefs/lcfs-writer-erofs.c:1444).
The third is verification. fs-verity digests of content files are stored in trusted.overlay.metacopy, so overlayfs verifies content at use time, and the image itself can be sealed with its own fs-verity digest passed as a mount option (src/README.md:78-95).
Extension points
composefs is a library plus tools rather than a plugin host. The integration surface is the C API in libcomposefs/lcfs-writer.h (build a node tree, then lcfs_write_to) and the mount.composefs helper. Language bindings build on these: a Rust crate (composefs-rs) and Go wrappers in containers/storage that shell out to mkcomposefs (src/README.md:174-185).