Skip to content

CosmoOS 1.0.8

Choose a tag to compare

@github-actions github-actions released this 21 Oct 12:00
· 254 commits to main since this release
2acce0c

Upgrades can be fully undone

From this release on, cosmo-upgrade takes a snapshot of the root
before it writes anything -- a boot environment, be-<release> --
and pairs it with the boot set it ran on, in \cosmo\be on the system
partition. cosmo-upgrade --rollback then returns both halves: the boot
set at once, and the root -- packages, /etc and the base system,
byte for byte as they were -- at the reboot it starts itself.

  • What you wrote is kept. Files added or changed under /root,
    /home and /var/log since the environment was taken are copied,
    before any service starts, into /var/lib/cosmo/rollback/<the replaced tree>/. If they cannot all be copied, the replaced tree stays held
    (it cannot be deleted) and the next boot tries again.
  • The replaced tree is an environment too: rolling back again goes
    forward to it. cosmo-upgrade --list shows the environments and the
    set each one pairs with; the newest two are kept, and --prune
    deletes older ones.
  • A crash at any step boots. The intent is recorded in \cosmo\be
    before anything moves; whatever the machine boots afterwards -- the
    newer kernel on the older root, or the older kernel on the newer one --
    cosmo-upgrade --repair finishes what was meant. --status says when
    a repair is needed.
  • Refused, with nothing written: an upgrade on a root without room
    for the environment (the sentence names what is needed and what is
    free), while a rollback of the root is pending, and when the root is
    not the current set's.

There are no environments for v1.0.7 and earlier, and the upgrade to
v1.0.8 takes none.
Those releases keep no snapshots, and their
cosmo-upgrade refuses a system partition that records environments,
which a rollback to one of them would leave behind. The first upgrade
that can be fully undone is the one made from v1.0.8. Rolling back
from v1.0.8 to v1.0.7 returns the boot set and the base system, as
before; packages pkg upgrade changed stay changed.

Package transactions

pkg install, upgrade, remove and rollback are each one
transaction: pkg snapshots the root first and deletes the snapshot when
it is done. If the machine stops in the middle -- pkg install a b with
a in place and b not -- the next boot rolls the root back to the
moment the transaction began, and reboots once to do so. pkg refuses
before changing anything when the root lacks the room.

pkg rollback NAME returns one package to the version installed
before the current one, with its files; the last two versions of each
package are kept in /var/cache/pkg/kept.

While \cosmo\prev holds v1.0.7 or older -- right after upgrading from
it -- pkg takes no snapshot and says so (no transaction: ...): the
first snapshot raises the root to cosmofs format 12, which those kernels
cannot mount. The same rule keeps the upgrade to v1.0.8 from taking an
environment.

Snapshots and rollback from the shell

cosmo-snap takes snapshots of the installed root, lists what each one
holds alone, shows what changed since (cosmo-snap diff), rolls the
system back to one, and makes writable clones. A rollback keeps the tree
it replaces as a snapshot, applies at the next mount, and holds that
snapshot -- it cannot be deleted -- until the boot completes. The guide's "Snapshots and rollback" page has the steps.

Fixes

  • A write-back that fails partway keeps what it wrote. On a full
    disk, a file closed while its last write-back failed could leave
    blocks no file named (fsctl check: leaked) and, for a file
    overwritten in place, free blocks the file still named. The inode is
    now stored after every write-back, failed or not.
  • A foreground job owns the terminal from its first instruction. A
    shell started from cosmo-sh could stop itself with SIGTTIN before
    cosmo-sh handed it the terminal.

Known

pkg install -f NAME=VERSION to an older version says it is
"upgraded"; it is a downgrade (backlog PKG-9).

Rolling back and then forward leaves upgrades refused. After
cosmo-upgrade --rollback to v1.0.7 and v1.0.7's own cosmo-upgrade --rollback forward again, the root's record of its base system
(/var/lib/cosmo-upgrade/root-set) still names v1.0.7's set. This
release trusts that record: it refuses every later upgrade ("the root
holds another set's base system than the current set's (a rollback half
done)"), and its own --repair finds nothing to do. Nothing on the
system is wrong but the record. Run CosmoOS 1.0.9's cosmo-upgrade --repair once, from that release's signed cosmo-upgrade package, then
upgrade: see the v1.0.9 notes
and the guide's upgrading page.

Upgrading from v1.0.7

cosmo-upgrade --fetch v1.0.8, reboot, then pkg update && pkg upgrade. No boot environment is taken by this upgrade (above); the
next one is.

Tested: on every change and on both architectures, make test-upgrade
(the published v1.0.3 upgraded to this build and back; then
test-pkgtx's killed transactions and full root, and test-be: an
upgrade, pkg upgrade and a changed /etc rolled back byte for byte,
the operator's files kept, and a crash after each step), and make test-snapshot (a broken system rolled back, three kills during a
rollback, a clone).

About CosmoOS

CosmoOS 1.0.8: the 1.0 roadmap completed. CosmoOS installs to a disk, reboots into it, is used through BusyBox ash, keeps files across reboots, reaches the network by name, accepts an ssh login, and has a verified boot chain and sound randomness, on x86-64 and AArch64 under QEMU.

Start with the user guide. Each architecture's files: cosmoos-ARCH.img (the signed live image), its UEFI loader, kernel.elf, boot.tar, cmdline, the signed manifest, cosmo-upgrade and its signed package. SHA256SUMS.sig is SHA256SUMS's detached signature with the release key below (scripts/modsign.py verify-detached, or cosmo-verify on an installed system); check the files with sha256sum --ignore-missing -c SHA256SUMS.

Release signing key

The loader in these images verifies the kernel, boot archive and command line against this Ed25519 key (and the kernel trusts it for modules and packages):

  • public key: d2c778bcb38775790b6b6e843631bd2e76d6a29ec52296ece6e2d67c1bef41f8
  • key id: 36ae7bb4947896ab (the first 8 bytes of SHA-512 of the key; the boot log prints it: verified boot, key 36ae7bb4947896ab)
  • in the repository: tools/keys/release-2026.pub

Toolchain

Built and tested in the project's CI images (ci/Dockerfile), pinned by digest:

  • toolchain: ghcr.io/vkuttyp/cosmoos-ci@sha256:f6aee1470b952bdb426b8d55cfcccb734c9c1f4dac50c89edefba4e8be3be385
  • litmus: ghcr.io/vkuttyp/cosmoos-ci@sha256:c8b16f9c652cd99e4a557c08adc451528cc9cd36ba0e9e37b2973feffb15a5a9
  • clang 1:19.0-63
  • lld 1:19.0-63
  • llvm 1:19.0-63
  • qemu-system-x86 1:10.0.13+ds-0+deb13u1
  • qemu-system-arm 1:10.0.13+ds-0+deb13u1
  • ovmf 2025.02-8+deb13u1
  • qemu-efi-aarch64 2025.02-8+deb13u1
  • mtools 4.0.48-1
  • python3 3.13.5-1
  • clang-19 1:19.1.7-3+b1
  • lld-19 1:19.1.7-3+b1
  • llvm-19 1:19.1.7-3+b1

Third-party sources

Fetched and checked against these SHA-256 digests (ports/busybox/busybox.mk, ports/dropbear/dropbear.mk, ports/openssh/openssh.mk, ports/mbedtls/mbedtls.mk):

Source Version SHA-256
BusyBox 1.37.0 3311dff32e746499f4df0d5df04d7eb396382d7e108bb9250e7b519b837043a4
musl 1.2.5 a9a118bbe84d8764da0ea0d28b3ab3fae8477fc7e4085d90102b8596fc7c75e4
compiler-rt 19.1.7 c12b6e764202c615c1a3af9a13d477846878757ae0e29e5f8979215a6958fffc
dropbear 2026.94 e098034a843699200c8c977a991fff73159735bf795d5f72ef672c41a6b1ae81
OpenSSH portable (sftp-server only) 10.6p1 a9dc9565dffe8640f64d863cd29a32bc4a3dbdec0566a7fc44c5d6ee767d5f39
mbedTLS 3.6.7 a7e8bcbec0e6f761b4af24f25677626b35f762f68eef79c08677a363212d11f6
CA certificates (curl's extract of Mozilla's) 2026-09-25 a41b5d356aea97a529fe27e0f7316d2f9d946d75927476cf9cf1b90637d00505

Reproducibility

Each architecture was built twice from the tag v1.0.8, in two fresh containers of the toolchain image above, with the release key; every file below was identical in both:

  • x86_64: two builds, 10 files each, identical; cosmoos-x86_64.img sha256 b8fcb78fb088847bd0c650d1814415e20be86437b091704079a62435a68a5282
  • aarch64: two builds, 10 files each, identical; cosmoos-aarch64.img sha256 02e33a872c0d76cd23f4f80ebc09445f19694c469aabb03b06b58b2194ff22c8

To rebuild: check out the tag, run the toolchain image above, and make ARCH=<arch> SIGNING=release MODSIGN_KEY=<key> KEYRING_PUBS=tools/keys/release-2026.pub release. Without the private key, a build with your own key differs in what the key reaches: the key ring compiled into the kernel and loader, the signatures on modules, packages and the manifest, and the archive and image that hold them.

SHA256SUMS

b8fcb78fb088847bd0c650d1814415e20be86437b091704079a62435a68a5282  cosmoos-x86_64.img
115ef94c7916d2ba01ed3127701506beb2cc8199f6835a4e4489966fa34ffb67  cosmoos-x86_64-BOOTX64.EFI
f3c02a3b09d120ccdaba2ebb9d4bb7ced18dc555e5b338399d2e0861c3fdd291  cosmoos-x86_64-kernel.elf
829c5c632a62c5a1ce0de459bca8d2409ba8f6d2e096f53e9250da78e68dbcc1  cosmoos-x86_64-boot.tar
7a77470be4312a22db40932c6cd8ddcd6d42a29b07101cbbe135cbfa1fc1aa14  cosmoos-x86_64-cmdline
31c126b5c87115fb2748066194fc3e61bebd1b0b2b9fb858c7ad568105f6b548  cosmoos-x86_64-manifest
5752a899f9a7c7ea8f7836a9dd723ae70c12dd9187a4e0ab7048d57fd75056f4  cosmoos-x86_64-cosmo-upgrade
43e701eb448e992c5d29988b788a8429417c8e909fbbeb623f8c3970fc2119a9  cosmoos-x86_64-cosmo-upgrade.cpk
02e33a872c0d76cd23f4f80ebc09445f19694c469aabb03b06b58b2194ff22c8  cosmoos-aarch64.img
0d00053d85654789deaf9fa0edc5cb8231d8d142140c1198bcb38cfedc8ff87e  cosmoos-aarch64-BOOTAA64.EFI
6946cae370e62bc093b6d2b50e823db108daded3c8c9ccc6fdfdff976ca6b702  cosmoos-aarch64-kernel.elf
4241b632e933b27d93207f690cbbb7c0da164e1854611b5e736ff80e843e58eb  cosmoos-aarch64-boot.tar
7a77470be4312a22db40932c6cd8ddcd6d42a29b07101cbbe135cbfa1fc1aa14  cosmoos-aarch64-cmdline
95962f05c37942724276698823e02ee95f19f3757e5a018ca0789dacb6ab5c1a  cosmoos-aarch64-manifest
70ca5968498f87b5fe64582fffbf8c690b352dd8515e3a9874858334ae56613d  cosmoos-aarch64-cosmo-upgrade
855e4132dcef40ea42ff05e9a196a5a14a4ff16df946162ba7703028c58d0ff3  cosmoos-aarch64-cosmo-upgrade.cpk

Known limitations

What CosmoOS 1.0 does not do, or does differently from what you may
expect. Each item names where it is recorded; the work that remains
after 1.0 is in docs/plan.md, and the full list of known
gaps in the deferred-work inventory.

Machines

  • QEMU is the tested platform. x86-64 on q35 and AArch64 on virt
    are tested by CI; there is no physical hardware matrix. Since v1.0.1
    the AArch64 image boots on Parallels Desktop 27 on Apple Silicon, and
    since v1.0.3 installs on its NVMe disks and has a network there; its
    SATA disks are not seen (Tested platforms). Out of scope for 1.0
    (roadmap): GPU and display beyond a text console (on
    the firmware's framebuffer, or a virtio-gpu adapter at the display's
    own size: no 3D, cursor or display interface for programs), hotplug, NUMA, more than 64 CPUs, huge pages, live migration,
    RAID-Z, eBPF, and the Intel VMX backend on hardware.
  • Randomness from the weakest source last. The pool seeds from the
    CPU's random instructions, the firmware's EFI_RNG_PROTOCOL or a
    virtio-rng; with none of them (Parallels Desktop) a timing-jitter source
    seeds it, credited conservatively (docs/kernel/security/design.md §6).
    random.jitter=off on the command line turns that off, and the pool
    then never seeds there. The commands in this guide add -device virtio-rng-pci.
  • The clock is stepped, not slewed. The wall clock is set at boot
    from the RTC or, with none (Parallels), the firmware's time. Since
    v1.0.4 root can set it (date -s, hwclock -w where there is an RTC)
    and ntpd -q steps it once from a server; there is no NTP daemon,
    because adjtimex is not implemented, and no time zone.
  • No Wi-Fi, Bluetooth or sound.

Boot and security

  • Verified boot covers the loader's three files (kernel, boot archive,
    command line). The root filesystem is not verified; the loader is
    not signed for UEFI Secure Boot; there is no TPM measurement
    and no rollback protection for manifests or modules (inventory
    2.9).
  • No KASLR: the kernel is at a fixed address. User-space ASLR covers
    the stack, heap, mmap and PIE executables; a non-PIE (ET_EXEC)
    program loads at its fixed address.
  • One user, root. Privileges, not uids, separate what processes may
    do (Privileges). A root session holding fewer
    privileges can still rewrite files in /etc that a later boot runs
    with all of them (M5 report, inventory 2.9).

Shell and commands

  • The commands are the BusyBox applets listed in the shell
    page
    : no ping6; no hard links; no /sys.
  • /proc has the process directories and a fixed set of system files
    (meminfo, stat, loadavg, uptime, mounts, version,
    cpuinfo). A process has no nice value and no user/system split of its
    CPU time; ps's TT column is a pair of Linux device numbers (5,1
    for the console), since devices here have none of their own; and a
    process's environ is what exec gave it, not what it has since written
    over (inventory §13).
  • The terminal driver honours the common modes (canonical input, echo,
    signals, raw mode, window size); most other termios flags and control
    characters are accepted and ignored (inventory 1.4).
  • No swap.

Networking

  • wget's certificate authorities are fixed in the image
    (/etc/ssl/certs/ca-certificates.crt, as built); nothing updates them,
    and there is no certificate revocation check
    (Networking).
  • The address comes from DHCP. There is no runtime tool to set one by
    hand; /etc/resolv.conf is written once per boot, not on lease
    renewal; the DHCP client does no ARP probe or DECLINE, no INIT-REBOOT,
    no RELEASE, and ignores classless routes (option 121) (inventory 2.5).
  • IPv6 is link-local; there is no IPv6 routing beyond the link, no IP
    fragmentation, and no TCP SACK, window scaling or timestamps
    (inventory 2.5).
  • ssh is dropbear with key login only (Ed25519 or RSA), root only;
    no port forwarding (ssh).

Storage

  • cosmofs files are capped at 516128 blocks of 4 KiB (about 1.97 GiB),
    64516 blocks (252 MiB) when encrypted; a larger write fails with
    EFBIG.
  • A read-only mount still writes the replay of an interrupted free or
    orphan record, as a journalling filesystem replays its journal.
  • A rollback to a snapshot takes effect at the next boot (or the next
    mount of another filesystem), not in the running system
    (Snapshots and rollback); a clone of a clone, and a
    snapshot of a clone, are not possible; no RAID-Z, no resilvering
    (backlog CFS-9, CFS-13).
  • A root disk that appears late (USB storage) is not waited for: init
    stays on the live root and says so.

Packages

  • A network repository is fetched whole, file by file: no mirrors, no
    delta updates. Its index's freshness is only as good as the clock, which
    is stepped by hand or by ntpd -q, with no NTP daemon
    (Packages).
  • cosmo-upgrade --rollback returns packages and /etc only to a boot
    environment, which upgrades made by v1.0.8's cosmo-upgrade and later
    take; past an upgrade made by v1.0.7 or older it returns the boot set and
    the base system only (Upgrading). Only
    files under /root, /home and /var/log written after the upgrade
    are kept across a rollback; the root is rolled back at the next boot,
    never while it runs; one pool.

Linux programs

  • Static and PIE Linux binaries for x86-64 and AArch64 run under the
    Linux personality; it is a subset of Linux's system calls, so a
    program that needs a missing one gets ENOSYS. Dynamically linked
    musl programs run through musl's own linker, /lib/ld-musl-$ARCH.so.1,
    with libc.so and dlopen; glibc's has not been
    tried. A library with text relocations is refused (W^X); build it
    -fPIC. Native CosmoOS programs stay static.
    No containers or desktop software (docs/plan.md).