index
In-place migration utility that converts an OSTree-backend bootc system
(e.g. Bluefin) into a ComposeFS-backend bootc system (e.g. Dakota), without
reinstalling and without losing /home, /var, /etc customizations,
flatpaks, container storage, or user accounts.
Migrate Bluefin β Dakota (quick start)β
The common case: Bluefin stable (btrfs) β Dakota stable. Five steps. Your old OSTree deployment stays in the boot menu as a fallback the whole time.
β οΈ Back up anything you can't afford to lose first. This rewrites how your system boots. It preserves
/home,/var,/etc, flatpaks, container storage, and user accounts β but treat it as risky until you've rebooted and confirmed everything works. It's reversible until you runcommit(step 5).
1. Get the migrator. Download the latest prebuilt binary (x86_64; for arm64
swap in aarch64-unknown-linux-gnu):
Release-naming note. This repo was renamed from
bootc-migrate-composefstobootc-migrateafter the v0.2.0 release. The released tarballs, the binary inside them, and the container image still carry the oldbootc-migrate-composefsname β the commands below reflect what v0.2.0 actually ships. The next release will publish under the newbootc-migratename.
curl -fsSL -o bmc.tar.gz \
https://github.com/tuna-os/bootc-migrate/releases/latest/download/bootc-migrate-composefs-x86_64-unknown-linux-gnu.tar.gz
tar xzf bmc.tar.gz
sudo install -m755 bootc-migrate-composefs /usr/local/bin/bootc-migrate
β¦or pull the container image
A minimal image ships the same binary, useful when GitHub Releases is rate-limited/blocked, or to COPY --from= it into another Containerfile:
podman create --name bmc-extract ghcr.io/tuna-os/bootc-migrate-composefs:latest
podman cp bmc-extract:/usr/local/bin/bootc-migrate-composefs .
podman rm bmc-extract
sudo install -m755 bootc-migrate-composefs /usr/local/bin/bootc-migrate
β¦or build from source (needs Rust)
git clone https://github.com/tuna-os/bootc-migrate
cd bootc-migrate
cargo build --release
sudo install -m755 target/release/bootc-migrate /usr/local/bin/
2. Dry-run β makes no changes, just checks your system is ready:
sudo bootc-migrate \
--target-image ghcr.io/projectbluefin/dakota:stable --dry-run
3. Migrate (~5β25 min depending on cache/network):
sudo bootc-migrate \
--target-image ghcr.io/projectbluefin/dakota:stable
4. Reboot β the new composefs entry is the default. If anything looks wrong, pick the old Bluefin / OSTree entry in the boot menu to get straight back.
sudo systemctl reboot
5. Confirm, then make it permanent:
cat /proc/cmdline | grep -o 'composefs=[0-9a-f]*' # confirms composefs boot
sudo bootc-migrate commit # one-way; removes the OSTree fallback
β οΈ Note: Phase 4 copies
/varto the composefs side. After migration, the two/vartrees are independent β changes you make on the composefs side won't be reflected if you roll back to OSTree (and vice versa). Commit only when you're satisfied with the new system.
That's it. For flags, rollback, troubleshooting, and the full phase-by-phase
breakdown, see Usage β end-to-end walkthrough.
On Bluefin LTS (XFS) or systems with LVM / LUKS / a dedicated /var
partition, the tool handles those automatically β see
docs/filesystem-support.md.
Status: CI-validated, released, and proven on real hardware. Four E2E scenarios β btrfs, ext4, LUKS+XFS, and LVM-on-LUKS with a dedicated
/varβ run in CI on every push tomain(migration, commit, deep-clean, andbootc status/upgrade --checkall green). Prebuilt binaries are on the Releases page. Don't point this at a machine you can't reinstall, but the core path is stable.
Interactive wizard (TUI)β
Prefer a guided walkthrough over flags? Run the tool with no --target-image
(or tui explicitly) to launch a terminal wizard that walks through target
image selection, options, a plain-English review of what's about to happen,
and a live phase-by-phase progress view with scrollable logs:
sudo bootc-migrate tui

The wizard defaults to --dry-run and only builds the equivalent CLI
invocation shown on the Review screen β it doesn't need root just to browse;
root is required once you press Enter to actually run a migration.
Architectureβ
flowchart TB
%% ββ Source: what we migrate from ββββββββββββββββββββββββββββ
subgraph SRC["Source · OSTree-backed Bluefin"]
direction TB
S_USR["<b>/</b> — OSTree hardlink farm<br/>/usr/etc · /ostree/repo object store"]
S_ETC["<b>/etc</b><br/>live, 3-way-merge source"]
S_VAR["<b>/ostree/deploy/<n>/var</b><br/>user state"]
S_BOOT["<b>/boot/loader/entries</b><br/>GRUB BLS · ostree-*"]
end
%% ββ The migration tool: six phases, one command βββββββββββββ
subgraph BIN["bootc-migrate · 6 phases (0–5)"]
direction TB
P0["<b>Phase 0 · Preflight</b><br/>ESP size · NVRAM · reflink"]
P1["<b>Phase 1 · OSTree import</b> (optional)<br/>reflink objects → composefs store"]
P2["<b>Phase 2 · OCI pull</b><br/>target image → composefs store"]
P3["<b>Phase 3 · EROFS seal</b><br/>build · seal · capture config digest"]
P4["<b>Phase 4 · Stage deploy</b><br/>3-way /etc merge (sealed mount)<br/>symlink prune · identity-DB union<br/>/var copy · .origin (tini)"]
P5["<b>Phase 5 · Bootloader</b><br/>sd-boot from sealed mount<br/>BLS entries on ESP · NVRAM"]
P0 --> P1 --> P2 --> P3 --> P4 --> P5
end
%% ββ Target: what we migrate to ββββββββββββββββββββββββββββββ
subgraph DST["Target · ComposeFS-backed Dakota"]
direction TB
D_CFS["<b>/composefs/</b><br/>images/ (EROFS) · objects/ · streams/"]
D_STATE["<b>/state/deploy/<verity>/</b><br/>etc/ · <verity>.origin"]
D_VAR["<b>/state/os/default/var</b><br/>bind-mounted as /var by initramfs"]
D_BOOT["<b>/EFI/Linux/bootc_composefs-<verity>/</b><br/>vmlinuz · initrd<br/>/loader/entries/*.conf · systemd-bootx64.efi"]
end
%% ββ Data flows across the lanes βββββββββββββββββββββββββββββ
S_USR -- "ostree object reflinks" --> P1
P2 --> D_CFS
S_ETC -- "current / old / new merge" --> P4
P4 --> D_STATE
S_VAR -- "verbatim copy<br/>(containers · flatpaks · machine-id)" --> D_VAR
P3 -- "sealed config digest<br/>(not rootfs verity)" --> P4
P3 --> P5
P5 -- "copy from sealed mount" --> D_BOOT
P5 -. "efibootmgr · Linux Boot Manager" .-> NVRAM(["UEFI NVRAM"])
RUN["<b>Booted Dakota</b><br/>/ = composefs overlay (RO)<br/>/etc writable ← state/<br/>/var writable ← state/os/default/var"]
DST -. "reboot → systemd-boot → kernel<br/>cmdline composefs=<verity>" .-> RUN
%% ββ Lane colours ββββββββββββββββββββββββββββββββββββββββββββ
classDef src fill:#e3f0ff,stroke:#3b82c4,color:#0b2545;
classDef bin fill:#fff4e0,stroke:#d9920b,color:#5a3a00;
classDef dst fill:#e4f7e7,stroke:#3ca34a,color:#06311a;
classDef run fill:#f3e8ff,stroke:#8b5cf6,color:#2e1065;
class S_USR,S_ETC,S_VAR,S_BOOT src;
class P0,P1,P2,P3,P4,P5 bin;
class D_CFS,D_STATE,D_VAR,D_BOOT dst;
class RUN,NVRAM run;
Key insight: Phase 3 runs bootc internals cfs oci seal which prints the
sealed manifest's config digest. Phases 4 and 5 pass that sealed config
digest (not the rootfs verity) to bootc cfs oci mount β the overlay then
exposes real file content for /etc, kernel, initrd, systemd-boot, and kernel
modules, eliminating the need to re-stream OCI layers at runtime.
What it doesβ
Six phases (numbered 0β5 to match the console output), run as one command:
- Phase 0 β Preflight β free-space, reflink/CoW, UEFI, NVRAM-writable, ESP capacity.
- Phase 1 β OSTree import (optional) β reflinks existing OSTree file
objects into the composefs object store so the pull in Phase 2 is mostly
dedup. Skipped with
--skip-import. - Phase 2 β OCI pull β
bootc internals cfs oci pullof the target bootc image into the composefs store. - Phase 3 β EROFS seal β builds and seals the EROFS image, capturing the sealed config digest that Phases 4 and 5 mount.
- Phase 4 β Stage deploy β 3-way
/etcmerge (read from the sealed mount, no registry streaming), identity-DB line-union, dangling/usr/*symlink pruning,/homeβ/var/homecompatibility for native-home targets,/varpreservation (copy or in-place dedicated subvolume), and.origin(boot_digest, manifest_digest) written via tini. - Phase 5 β Bootloader β copies
systemd-bootx64.efifrom the sealed mount to the ESP (no registry streaming), verifies that the target kernel resolves the current wireless devices, writes BLS entries (including a durable stateroot mount argument for a dedicated/var), and registersLinux Boot Managerin UEFI NVRAM. The original GRUB entry is left as a rollback escape hatch.
After a successful reboot into the composefs entry, bootc-migrate commit removes the OSTree fallback and makes composefs permanent.
Usage β end-to-end walkthroughβ
Before you start. This tool rewrites bootloader state and either reuses or copies the entire
/var. Don't run it on a machine you can't reinstall in a pinch. Until you runcommit, it's reversible β but a fresh backup is still cheap insurance.
1. Decide your targetβ
The migration takes a --target-image β the composefs-backed bootc image
you want to end up on. Today the validated path is Bluefin β Dakota:
ghcr.io/projectbluefin/dakota:stable # default target
If you're migrating a different OSTree-backed system (Aurora, Silverblue),
point --target-image at the composefs-flavored equivalent.
2. Check readiness with a dry-runβ
sudo bootc-migrate \
--target-image ghcr.io/projectbluefin/dakota:stable \
--dry-run
Things to confirm in the report:
Booted OSTree backend: Yesβ required; ifNothe tool refuses to run.UEFI Boot Mode: Yes+NVRAM writable: Yesβ required for the systemd-boot path; on BIOS-only or locked NVRAM pass--bootloader grub2.ESP Free Space: β₯ 150 MBβ we copysystemd-bootx64.efifrom the target image onto the ESP.Reflink (CoW) Support: Yesβ btrfs and XFS both support reflink.ComposeFS free space: β₯ 1.1 Γ ostree_repo_sizeβ the composefs object store is built by reflinking your existing OSTree objects.
Optionally, preview what Phase 4's /etc merge will see before running it:
sudo bootc-migrate etc-drift
Lists every path where your live /etc has diverged from the OSTree factory
default (added/modified/removed/type-changed), read-only. Useful for
spotting a stale customization you no longer need before it carries forward
into the migrated system.
3. Run the migrationβ
sudo bootc-migrate \
--target-image ghcr.io/projectbluefin/dakota:stable
Expect ~5β10 minutes on warm caches, ~15β25 minutes on a cold pull. Six phase headers (0β5) print as it goes:
| Phase | What's happening | Why it might take a while |
|---|---|---|
| 0 β Preflight | Same checks as --dry-run | seconds |
| 1 β OSTree import (optional) | Reflinks existing OSTree file objects into the composefs object store so Phase 2 mostly dedups | tens of seconds to a few minutes; skip with --skip-import |
| 2 β OCI pull | bootc internals cfs oci pull of the target image | minutes (network-bound) |
| 3 β EROFS image | Builds + fs-verity-signs the composefs metadata image | seconds |
| 4 β Stage deploy | 3-way /etc merge (from sealed mount), dangling-symlink prune, identity-DB line-union, /var copy or dedicated-subvolume preservation, .origin file written | ~1 minute |
| 5 β Bootloader | Copies systemd-boot from mounted image, writes BLS entries, registers NVRAM | ~30s |
When it ends with === MIGRATION COMPLETED === the on-disk state is
ready. Reboot:
sudo systemctl reboot
4. Validate the composefs bootβ
Log in (your existing accounts and SSH keys still work) and check:
cat /proc/cmdline # must contain composefs=<hex>
bootc status # should report the composefs deployment
bootc status --json | jq .status.booted.composefs # non-null
Spend a day on it. Run your usual workflow β flatpaks, dnf, containers,
homebrew, GNOME extensions, whatever. Everything that lived under /home,
/var, and /etc on Bluefin should be where you left it. If something
is missing or broken, you can roll back (see below).
A login banner (/etc/motd.d/85-bootc-migrate) reminds you to run
commit on every login until you do, so a live migration doesn't sit
forgotten in this dual-boot state indefinitely. It clears itself once
commit runs β or once undo runs, since at that point there's nothing
left to commit.
5. Make it permanent (one-way)β
Once you trust the new system:
sudo bootc-migrate commit
This removes the OSTree fallback from the ESP, drops GRUB2 boot artifacts, and reclaims ~14 GiB of OSTree object store. The systemd-boot entry becomes the sole default with timeout 0.
Flagsβ
| Flag | Purpose |
|---|---|
--dry-run | Print every action; touch nothing |
--skip-import | Skip phase 1 (faster when target image is mostly new content) |
--bootloader grub2 | Stay on GRUB2 instead of installing systemd-boot |
--skip-preflight | Bypass preflight checks (don't, unless you know exactly why) |
--force | Proceed past non-fatal warnings |
Move system Steam into Flatpak Steamβ
After installing and launching com.valvesoftware.Steam once, its per-user
data can absorb a system Steam installation without re-downloading games:
bootc-migrate system-to-flatpak-steam --dry-run
bootc-migrate system-to-flatpak-steam
Run it as the desktop user, without sudo, after closing Steam and all
Steam games. It uses filesystem renames onlyβnever a recursive copyβto move
steamapps, userdata, and config from ~/.local/share/Steam into Flatpak
Steam's data directory. The pre-existing Flatpak versions and both library
registries are retained in a timestamped rollback directory under
~/.var/app/com.valvesoftware.Steam/.
The command deliberately leaves the native Steam runtime files and unrelated
non-Steam folders such as ~/Games alone. Add a Flatpak filesystem override
for ~/Games separately if Steam shortcuts need it.
Rollback / recoveryβ
Until you run commit, the migration is reversible. The previous OSTree
deployment stays bootable:
- Phase 5 only adds the systemd-boot composefs entry; it never deletes the
existing
/boot/loader/entries/ostree-*.conffiles. - The original
/ostree/deploy/<n>/deploy/<commit>.0/rootfs and/ostree/deploy/<n>/var/stay on disk. - Phase 4 either copies
/vartostate/os/default/varor, for a dedicated filesystem/Btrfs subvolume, reuses it in place through a persistent BLS mount argument. The original data is not deleted during migration. - We push
Linux Boot Manager(systemd-boot) to the front of NVRAMBootOrderbut theFedorashim entry (which boots GRUB β OSTree) remains listed.
Automatic rollback subcommandβ
To return to the original OSTree deployment directly from the command line:
sudo bootc-migrate rollback --reboot
# or via the universal re-base CLI:
sudo bootc-rebase rollback --reboot
This verifies prerequisites, re-orders UEFI BootOrder so the OSTree entry (Fedora/GRUB) takes top priority, and reboots immediately into the OSTree deployment.
Manual firmware recoveryβ
If the system fails to boot into composefs or NVRAM state is interrupted:
- Power on; tap the firmware boot-menu key (commonly F12, F8, or Esc).
- Pick the
Fedoraentry. GRUB will show the originalostree:0menu. - Boot it. You land on the pre-migration system with its
/varand/etcintact.
Or, from a working composefs login, one-shot:
sudo efibootmgr -v | grep -E 'Fedora|Linux Boot Manager'
sudo efibootmgr --bootnext <Boot####-of-Fedora>
sudo systemctl reboot
Pre-migration diagnostic snapshots and logs are automatically recorded under /var/log/bootc-migrate/ on every run (preflight-*.json and migration.log) so boot configuration can be manually reconstructed if NVRAM is ever wiped.
After running bootc-migrate commit, the OSTree fallback is removed
from the ESP and rollback becomes a fresh install. The E2E test exercises the
full round-trip (composefs β OSTree β composefs) on every run.
What's preservedβ
Validated end-to-end (21+ assertions per run; see tests/run-e2e.sh):
- /var data β
/var/lib/*,/var/log/*,/var/cache/*, containers, flatpak system installs, machine-id, hidden dirs and symlinks - User homes β
/var/home/<user>/, dotfiles, project trees, SSH keys (with.sshmode preserved so StrictModes still accepts your keys), wallpapers, GNOME extensions, dconf user db, glib gsettings keyfile, homebrew Cellar, per-user flatpak installs. When an OSTree source uses/home -> /var/homeand the target has a native/home, both absolute path spellings remain valid through an ordered compatibility bind mount. - /etc state β
/etc/sudoers.d/*,/etc/hostsedits, customsshd_config.d/*, custom config files added under/etc/, in-place edits to image-shipped files (/etc/hostname),/etcsymlinks - Accounts β
/etc/passwd,/etc/shadow,/etc/groupline-union merged so users you added survive and users the target image needs (messagebus, polkitd, β¦) get added
What's intentionally not carried forward:
- OSTree/rpm-ostree state markers (
.updated,.rpm-ostree-shadow-mode-fixed2.stamp) - GRUB2 config files (
grub2.cfg,grub2-efi.cfg,/etc/grub.d/) β the target uses systemd-boot - Source-image
/etcfiles the target image removed (e.g.sshd_config.d/40-redhat-crypto-policies.confwhich references/etc/crypto-policies/paths Dakota doesn't ship)
Troubleshootingβ
| Symptom | Likely cause | Fix |
|---|---|---|
| Phase 0 refuses with "System is not booted into an OSTree deployment" | You're already on composefs (or a non-bootc system) | Nothing to do |
| Phase 2 fails with ENOSPC mid-pull | /sysroot/composefs is tight on the 1.1Γ heuristic | Free space or grow the partition, then rerun |
Post-reboot cat /proc/cmdline shows ostree= not composefs= | Firmware ignored the new NVRAM entry, or OVMF loaded Fedora\shim instead | Use firmware boot menu to pick Linux Boot Manager; if that fails, fall back to OSTree and report the firmware quirk |
bootc status says "No manifest_digest in origin" | You're on an old build of this tool | Update to main β version info is on the first line of the migration log |
| SSH key auth broken post-migration | Permissions changed during /var copy | Boot OSTree fallback and chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys |
| GNOME boots but session settings (wallpaper, accent) look wrong | dconf database needs recompile | dconf update as your user, or log out + back in |
| Phase 5 refuses because the target kernel has no module alias for a wireless device | The image omits the driver for Wi-Fi hardware present on the source system | Fix or update the target image. Use --force only when alternate networking is available and losing Wi-Fi is acceptable |
| Migration went wrong and you want to undo it | Something failed mid-migration | Run sudo bootc-migrate undo (removes composefs boot artifacts, keeps object store) or sudo bootc-migrate undo --full (full cleanup including object store); then reboot into OSTree |
Requirementsβ
- Booted on an OSTree-backed bootc system (Bluefin, Aurora, Silverblueβ¦)
- UEFI firmware with writable NVRAM (for the systemd-boot path; GRUB2 fallback works on BIOS)
- Btrfs or XFS sysroot with reflink/CoW support
- ESP with β₯150 MB free
- β₯
1.1 Γ ostree_repo_sizefree on/sysroot/composefs(no reflink: 1.5Γ) - Outbound registry access for
bootc internals cfs oci pull(Phase 2 fetches the target image; Phases 4β5 read artifacts from the sealed mount, so no runtime registry access is needed after Phase 2)
Buildingβ
cargo build --release
Drops a single binary at target/release/bootc-migrate.
Requires Rust 1.85+ and a Linux host with libxkbcommon-dev.
End-to-end testsβ
A QEMU-based E2E harness lives in tests/run-e2e.sh. It installs Bluefin
into a disk image, runs the migration against a registry mirror of the
Dakota target image, reboots, and validates the full round-trip.
sudo ./tests/run-e2e.sh
Overridable via env: BASE_IMAGE, TARGET_IMAGE, DISK_SIZE,
FILESYSTEM, SKIP_SETUP. The CI matrix runs four scenarios: btrfs (default), XFS+ext4-loopback, LUKS+XFS+crypt,
and LVM-on-LUKS with a dedicated /var.
Layoutβ
A Cargo workspace with three crates (see ROADMAP.md for why):
crates/bootc-migrate-coreβ the capability library everything else is built from: phases, preflight/readiness,/etcmerge (mergetc), OSTree object scan, registry streaming, transaction (commit/undo), target-image capability scan (scan), cross-base UID/GID remap (remap), UEFI boot-entry audit (boot_audit), DE detection (de_detect) and config stash/restore (de_migrate), types.crates/bootc-migrateβ the protected MVP binary described above. CLI surface (clap),commit/undo/rollbacksubcommands, the TUI wizard. Its four E2E cells are untouchable regression gates β this binary's behavior doesn't change as new capability lands inbootc-rebase.crates/bootc-rebaseβ the universal re-base engine binary; see below.tests/run-e2e.shβ QEMU E2E harness exercising both binaries.
bootc-rebase β the universal re-base engineβ
bootc-migrate above does one proven thing: OSTree β ComposeFS.
bootc-rebase is the generalization β a routing table over
backend Γ strategy that will eventually cover every bootc re-base shape
(same-backend image swaps, cross-backend conversions, bootloader changes,
cross-distro-family moves, desktop-environment switches). It's newer and less
battle-tested than the MVP binary; treat subcommands marked skeleton or
read-only below as previews, not yet full features.
cargo build --release -p bootc-rebase
| Subcommand | What it does | Status |
|---|---|---|
scan <image> | Registry-streamed capability probe of a target image β composefs/ostree readiness, fs-verity requirement, transient root/etc, bootloader payload, desktops, base OS identity, sysusers, initramfs flavor, filesystem expectation, and a Compatible: YES/NO verdict with reasons. --json for machine output. | Done |
rebase --target-image <image> | Re-base the running system, routing on --source-backend/--target-backend through the strategy table below. --plan prints the route, selected phases, and bootloader policy, then exits without touching the system. | Implemented for ostreeβcomposefs (the MVP pipeline), composefsβcomposefs (image swap), and ostreeβostree (native bootc switch, with cross-base UID/GID remap when host and target disagree on distro family β pass --accept-cross-base to proceed past the report) |
rollback [--reboot] | Re-order UEFI BootOrder back to the previous deployment. | Done |
boot-entries [--json] [--interactive] [--rename-branding] [--apply] [--undo] | Enumerate and classify UEFI boot entries: dead (loader path missing), generic-label, duplicate, firmware-managed, plus which are protected and why. Dry-run by default β a bare invocation is the read-only audit. --interactive opens a checklist (protected entries are unselectable), --rename-branding proposes renaming the booted entry to PRETTY_NAME, --apply writes the result to NVRAM after a typed confirmation and a restorable snapshot, and --undo replays that snapshot. | Audit and the cleanup planner are done and unit-tested (protections, the last-bootable-entry guard, and the "every entry looks dead β the ESP is wrong" refusal). The efibootmgr mutation path and the checklist's event loop have no automated coverage β no E2E cell mutates NVRAM β and need real-hardware/VM UEFI validation before they are trusted (#31) |
de-migrate stash|restore | Move a user's desktop-environment config (GNOME dconf/gnome-shell, KDE kdeglobals/plasma, COSMIC, niri, XFCE) into or out of a stash directory around a cross-DE re-base β union of paths per issue #68, never deletes. --run-hooks executes pre-switch.d/post-switch.d scripts with REBASE_FROM_DE/REBASE_TO_DE/REBASE_STASH_DIR/REBASE_HOME set. --dry-run previews without touching anything. | Done. Also runs automatically inside rebase --de-migrate; this subcommand remains the manual escape hatch for images shipping several desktops (which detection refuses to guess between) |
rebase --de-migrate | Detects the desktop environment the target image ships (registry-streamed session files, session binaries, and display-manager default session β no podman pull) and the one this host runs. When they differ, stashes every human account's outgoing DE config before staging and re-exposes any stash a previous re-base in the other direction left behind, running the pre-switch.d/post-switch.d hooks around each. | Done, unit-tested; off by default β a re-base never touches per-user desktop state unless asked to. Not yet covered by a cross-DE E2E cell |
migrate-bootloader --to systemd-boot | GRUB2 β systemd-boot conversion, standalone of a backend re-base. | Not implemented β the subcommand exists and always refuses; only the pure BLS-entry/kernel-arg/entry-token logic it will use has landed. Live ESP populate + NVRAM cutover + the kernel-install resync hook (without which a flipped system would silently boot stale kernels) are deliberately deferred pending explicit sign-off and a dedicated E2E cell β see #65 for the full implementation plan |
rebase's routing table (crates/bootc-rebase/src/routing.rs is the single
source of truth the CLI consults before touching anything):
| From β \ To β | ostree | composefs |
|---|---|---|
| ostree | OstreeDeploy (native bootc switch) | CoreMigration (this repo's proven phase 0β5 pipeline) |
| composefs | planned, not implemented | ImageSwap |
Roadmapβ
Full milestone plan, current status per issue, and design decisions live in ROADMAP.md β start there for "what's next" and "why did we choose X over Y."
Contributingβ
Contributions are welcome β see CONTRIBUTING.md for setup and
REVIEW.md for the code-review expectations. Run just check (clippy,
rustfmt, unit tests, shellcheck) before opening a PR. AI-assisted contributions
should follow AGENTS.md.
Licenseβ
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.