No description
  • Rust 98.5%
  • Shell 1.5%
Find a file
2026-10-15 18:09:48 -04:00
.forgejo/workflows ci: parse release ids with python3 (greedy sed grabbed the wrong id) 2026-09-23 00:06:34 -04:00
bin devshells: composable, stackable dev environments (Phase 4 v1) 2026-07-24 10:58:07 -04:00
cli release: bump version to 0.4.2 2026-10-15 18:04:15 -04:00
fuse-mount release: bump version to 0.4.2 2026-10-15 18:04:15 -04:00
scripts build: pack the portable tarball atomically (temp + rename) 2026-09-22 13:54:32 -04:00
.gitignore feat: install story — bubblebox doctor, portable release build, installer 2026-08-22 00:05:59 -04:00
AGENTS.md docs: wiki is the documentation home; AGENTS.md becomes a brief pointer 2026-09-09 19:32:37 -04:00
bubblebox.toml devshell: add bubblebox.toml (dogfood the devshell feature) 2026-07-24 11:31:20 -04:00
Cargo.lock release: bump version to 0.4.2 2026-10-15 18:04:15 -04:00
Cargo.toml cache: sign and verify index metadata 2026-08-27 10:33:43 -04:00
README.md docs: wiki is the documentation home; AGENTS.md becomes a brief pointer 2026-09-09 19:32:37 -04:00

bubblebox

A userspace, no-sudo package manager for CLI tools — a nix-lite for tools, built on OCI images + composefs.

Each package is a container image whose rootfs is stored content-addressed as a composefs descriptor in ~/.local/share/bubblebox/store. Running a tool mounts that rootfs through a rootless FUSE daemon and drops you into a bwrap sandbox — no root, no daemon, nothing installed system-wide. A declarative profile.toml (the flake.nix analog) pins your desired set of tools, dotfiles, and systemd user units; apply realizes it into a locked, rollback-able generation and syncs ~/.local/bin wrappers.

Status: work in progress, single maintainer, Linux x86_64. Developed on Fedora atomic hosts; expected to run anywhere with the requirements below.

How it works

  • Packages = OCI images → composefs descriptors, deduplicated in a content-addressed store. Recipes live in a source repo: one directory per package with a Containerfile + an optional entrypoint.toml (which binary to exec, what to bind/env — co-located because the author knows what the tool needs).
  • Running = mount the image's rootfs via FUSE + sandbox it with bwrap. Multi-package entrypoints overlay their rootfs read-only at runtime (compose = ["nvim", "rg", "fd"]) so tools share files without vendoring.
  • State is declarative: ~/.config/bubblebox/profile.toml lists what you want; bubblebox apply resolves, pulls/builds, locks digests into profile.lock, archives a generation (rollback target), and reconciles ~/.local/bin wrappers, $HOME dotfiles, and systemd user units.

Requirements

Need Why Package
bubblewrap every sandbox bubblewrap
FUSE 3 mounts each rootfs fuse3
composefs tools descriptor verify / store accounting composefs
util-linux mountpoint (liveness), unshare (composition) usually preinstalled
git git-based package sources usually preinstalled
podman building packages + first-run FUSE-server build; not needed to run pulled packages podman

Unprivileged user namespaces must be allowed (they are by default nearly everywhere). On Ubuntu ≥ 24.04 AppArmor restricts them; bubblebox doctor detects this and prints the exact fix.

Install

From a release tarball (self-contained: both binaries + helpers):

tar xzf bubblebox-v*-linux-x86_64.tar.gz
cd bubblebox-v*-linux-x86_64/
./install.sh            # -> ~/.local/bin, then runs `bubblebox doctor`

From source (no host toolchain needed — builds happen in containers):

git clone <this repo> && cd bubblebox
./scripts/build-portable.sh    # podman rust:bookworm build -> dist/*.tar.gz
# then install from dist/ as above

Quickstart

Declare what you want (~/.config/bubblebox/profile.toml):

packages = ["nvim", "rg", "fd"]

[[source]]
name = "personal"
kind = "local"                    # or kind = "git", url = "git+https://..."
path = "~/Code/bubblebox-pkgs"

Realize it:

bubblebox apply          # resolve + pull/build + lock + wrappers + dotfiles
bubblebox run nvim       # sandboxed, auto-mounted
bubblebox shell --base rust-dev   # or a project devshell: cd proj && bubblebox shell
rg --version             # wrappers make applied roots normal commands

Everyday commands:

Command Does
bubblebox apply [--refresh] realize profile.toml (--refresh: re-resolve floating versions)
bubblebox list / info <pkg> what's applied / one package's state
bubblebox generations list|switch|delete... history, rollback (switch <id-prefix>), pruning
bubblebox gc [--apply] reclaim orphaned store bytes (dry-run default)
bubblebox config cat|edit <pkg> view / override a tool's sandbox spec
bubblebox install <pkg|url> imperative one-off pull + wrapper
bubblebox import <oci-ref> turn any container image into a package (import alpine:3.20 --binary /bin/sh)
bubblebox push <pkg> [--stage-dir D] publish to S3-compatible cache / local dir

Project devshells

Check a bubblebox.toml into a repo — the nix develop analog:

base = "rust-dev"                 # reusable base shell entrypoint
init = "devshell.sh"              # sourced on entry (project file)
compose = ["sqlx-cli"]            # appended onto the base's tools

[sandbox.env]
DATABASE_URL = "sqlite:///work/db.sqlite"
cd ~/Code/my-project && bubblebox shell

Documentation

All documentation lives in the repo wiki — architecture, store/mount model, override layering, gotchas, locked decisions, feature designs (GC/generations, devshells), and status.

  • AGENTS.md — brief agent guide; points at the wiki.