1 Architecture
crussell edited this page 2026-09-09 19:32:21 -04:00
This file contains ambiguous Unicode characters

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 registries
    • caches.toml — registered binary CACHES; same fallback rule
    • entrypoints/<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>.cfs symlinks → the active digest
    • generations/<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.