Table of Contents
Locked design decisions
| # | Decision |
|---|---|
| 1 | Build/store unit = one OCI image → one composefs descriptor |
| 2 | Composition = rootless overlayfs-over-FUSE in a userns (spiked, works) |
| 3 | Entrypoint = declarative TOML (no code-executing escape hatch; bind.create = true covers state-dir seeding). The one narrow exception is the devshell init — a project script auto-sourced via a fixed helper (#12, Devshells §6). |
| 4 | Dotfile authoring out of scope (lives in cn); dotfile/unit deployment IS in scope — declarative [[files]]/[[units]] in profile.toml, reconciled by apply into store-symlinks (Phase B/C home management) |
| 5 | Decomposition is demand-driven (split a package when a 2nd tool needs it) |
| 6 | Conflicts = main-package-wins (topmost overlay lower) |
| 7 | Source model: package (Containerfile) + entrypoint (TOML) |
| 8 | Pull = anonymous HTTPS (reqwest); Push = S3 (aws-sdk-s3) or --stage-dir |
| 9 | Implementation = single Rust binary; bash retired |
| 10 | Lockfiles: profile.lock pins the realized closure (GC-and-Generations); per-blob verify deferred |
| 11 | Sources (specs: local + git) + caches (binaries) — source add infers the form; per-install URL + recorded provenance (no registry); cache serves the default entrypoint; overrides deep-merge and live under ~/.config/bubblebox. |
| 12 | Devshell = an entrypoint whose binary is a shell (no new top-level concept); stacking = base inheritance via stack_merge (append), distinct from deep_merge (replace); project devshells = git-tracked bubblebox.toml; init hook = constrained auto-source of a project script (the only bend of #3). See Devshells. |
| 13 | Bind destinations must pre-exist in the image; the engine validates and fails fast rather than auto-creating mountpoints. Auto-creation was rejected: the FUSE server is the pinned upstream composefs-fuse serving an immutable EROFS image — creating dirs would mean forking it or prerendering per-run images, for a case fully under the package author's control. (Engine PR #1, 2026-09-24.) |
| 14 | Providers are built-in, files first. State that lives in another tool's database (flatpak installs) is managed by a built-in Rust provider: a profile.toml section, a read-only live query for plan, a diff, and convergent apply. Generations record the declared set, and ownership mirrors [[files]]: remove only what a previous generation declared; never-declared items are reported extras unless prune = true. No TOML-defined command providers (would break #3). Anything a program can layer or read from a drop-in directory is done with [[files]]/[[units]] instead (e.g. dconf: keyfile → file-db under the user-db via DCONF_PROFILE, cn #72). First provider: [flatpak] (engine PR #7, v0.4.0). |