Table of Contents
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Architecture
Repo layout
Cargo.toml # workspace root: [workspace] members + shared deps +
# [profile.release] (lto/strip). default-members = ["cli"].
cli/ # the Rust binary `bubblebox` (the only entry point)
Cargo.toml # deps via workspace = true (clap, serde, toml, anyhow,
# reqwest, aws-sdk-s3, tokio, futures-util)
src/
main.rs # clap dispatch
common.rs # path resolution (mirrors the old bash bubblebox-common)
config.rs # entrypoint TOML schema (serde) + deep_merge/stack_merge
mount.rs # mount/unmount/ensure_mounted (FUSE + PID-tracked staleness)
run.rs # run (single-pkg) + compose-inner (overlay-in-userns) +
# shell (devshells; see Devshells)
build.rs # build (podman build + mkcomposefs); no provenance write
# (the generation manifest records origin)
import.rs # import <oci-ref>: pull an upstream image into a package
# (shares build_descriptor) + synthesize its default
# entrypoint (probes the image for the binary path)
init.rs # init (build the FUSE server via podman rust:bookworm)
export.rs # write ~/.local/bin/<name> wrappers
cache.rs # pull (multi-cache HTTPS) + push (aws-sdk-s3 or --stage-dir)
# + install (pull + deps + default entrypoint + export;
# no provenance write — apply owns that now)
sources.rs # registered SOURCES (specs: Containerfiles + default
# entrypoints) — local path or git clone. build/run resolve
# from these.
caches.rs # registered binary CACHES (HTTPS/S3) + install-target
# resolution + substituters (caches ∪ source channel.tomls).
inspect.rs # `list` (current generation) + `info <pkg>` (digest/origin/
# mount/footprint) — read-only introspection
profile.rs # the declarative layer (GC-and-Generations): profile.toml +
# [[files]]/[[units]] schema, generations (realize/id/
# archive), materialize_bindings, apply/switch/prune,
# wrapper auto-sync
gc.rs # store GC (roots = generations + lock + bindings + live
# mounts; dry-run default)
installed.rs # trimmed to just the `Origin` enum (cache vs source) — the
# per-package `installed/<pkg>.toml` records are RETIRED;
# the generation manifest is the single "what I have"
home.rs # Phase B/C: host-resident file + systemd unit management.
# Hashes file contents into the store (content-addressed
# blobs, same as packages) and symlinks $HOME/<dst> → the
# store blob path. Reconciled by apply alongside packages.
# kind="dir" walks trees into per-file store symlinks.
# See Home-Management-Plan (wiki)
fuse-mount/ # the rootless FUSE server binary (bubblebox-fuse), Rust
bin/ # bubblebox-host-shell — helper baked INTO some images
# (e.g. wezterm), NOT a bubblebox command. Keep.
The bash scripts that used to live here (bubblebox-{build,common,export,init, mount,run,unmount}) are deleted — the Rust binary replaced them. Don't
reintroduce bash for orchestration.
Store & mounts (XDG split)
User-authored config vs regenerable state are deliberately separated:
~/.config/bubblebox/(user-authored — back this up / sync across hosts):profile.toml— INPUTS: desired package set + declared registries +[[files]]/[[units]](the flake.nix analog; see GC-and-Generations)profile.lock— CURRENT realization (the pinned generation; flake.lock analog)sources.toml— registered SOURCES (local paths + git URLs); fallback when profile.toml declares no registriescaches.toml— registered binary CACHES; same fallback ruleentrypoints/<pkg>.toml— user OVERRIDES (the only file users edit)
~/.local/share/bubblebox/(state — regenerable from sources/caches):store/<XX>/<rest>— content store (composefs objects; named by their composefs digest, not plain sha256(content))descriptors/<digest>.cfs— digest-addressed descriptors;descriptors/<pkg>.cfssymlinks → the active digestgenerations/<id>.toml— realized-manifest archive (content-addressed ids)remote-defaults/<pkg>.toml— downloaded default entrypoint (from a cache)sources/<name>/— git source clones (local sources point elsewhere)installed/<pkg>.toml— RETIRED (inert leftovers from older versions; nothing reads them — safe to delete)logs/
- Runtime mounts:
$RUN_DIR/<pkg>(FUSE),$RUN_DIR/.composed/<entrypoint>(overlay),$RUN_DIR/.meta/<pkg>.{pid,desc-mtime}.
Cross-repo relationship
bubblebox is the engine; the packages (Containerfiles + their co-located
entrypoint.toml) live in a separate source repo — for this setup,
~/Code/bubblebox-pkgs, laid out as <pkg>/Containerfile +
<pkg>/entrypoint.toml per package. The cn repo holds only the per-host
profile.toml (the desired package set — a flake.nix analog) and drives
bubblebox via cjust (the cn Justfile): bubblebox-init, bubblebox-build,
bubblebox-apply recipes, all calling {{ bubblebox_bin }} (the release binary
path). When changing the CLI's command surface, expect to update those recipes
in cn.