Releases: Letdown2491/kuma-linux
Release list
kuma v0.20.0
Fixed
kuma vm's disks carry the declaration's shell again when kuma runs inside a distrobox. The one podman call invmthat spawned a private process did not escape the container kuma itself runs in, so inside one it never found the tag it was asked about, and every failure of that call is silent by design, so the fallback read as "no shell" and nothing could tell. It goes through the same escape every other podman call takes, which is where it always should have been.- A name capture cannot declare is refused when it is named, before it is echoed anywhere.
kuma capturenarrows to names you type, and its dry run repeats those names inside the suggested command, the one action in the JSON document an agent is invited to run. A name the declaration could never hold, one carrying shell metacharacters for instance, rode into that suggestion as itself and was refused only later, by the write it would have broken. The names now pass the same alphabet the two lists capture writes enforce, before anything runs. - The widen that opens a staged directory to root pruned its credentials with a
-pathpattern, and a glob pattern is what that is: a*,?or[in the path to a credential (TMPDIR is yours to set) would not match the file it named, the prune would miss, and the password hash or restore secret would be widened world-readable for the length of the run. The pattern is now the escaped literal path, and a test runs the realfindagainst a directory named to try it.
Changed
kuma capture --json's dry run says"dry_run": true, like every other gated verb's. It shipped without the field since the verb was born, so an agent reading the document had to infer a preview from"written": false. The document is otherwise unchanged.kuma switchandkuma rollbackdry runs print the command they preview in the same→form every other dry run uses, rather than leaving it implicit in the prose.kuma diffno longer runsbrew listto see what is installed: the Cellar directory says the same thing, and reading it saves the spawn on every run. The barekumaprobe already read it that way.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.20.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 241dc76bdd146409a88cdd1ac6e452af74ff5e27.
kuma v0.19.0
Added
kuma installcan name the sizes it used to decide for you.--esp 1Gand--boot 4Gset how big the EFI system partition and/bootare, and the interview asks for both with the defaults shown, so a person who does not care presses enter twice. The shape does not move: still three partitions, the root still takes what is left, and encryption still changes what the root holds rather than the disk. A size below what its partition is for is refused with the reason rather than a number (256M for the ESP, 1G for/boot), and the swapfile questions are measured against what the named sizes left over, so a disk that fits the defaults can be too small for what was asked, and the refusal says so with the arithmetic. The dry run prints the resolved sizes in its layout, the JSON surface carries them the same way, and the command it hands over keeps the flags it was given. Naming nothing changes nothing: the defaults are the sizes every kuma machine has been installed with so far.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.19.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 701432742e9a92c47338aeb353215be1f1b11fa7.
kuma v0.18.0
Added
-
A machine with a swapfile closes its lid into suspend-then-hibernate. Hibernate stops being something only a menu can ask for:
kuma install --swapandkuma hibernatenow also point the lid at suspend-then-hibernate, so a laptop asleep in a bag hibernates before the battery dies instead of draining out. On battery nothing times it: the machine hibernates on the firmware's own low-battery alarm, which knows the battery better than any setting could. Takes effect on the reboot the resume kernel arguments already demand;kuma hibernate --offtakes it away with the rest.kuma doctorgrades the lid beside hibernate: a machine that can hibernate but whose lid only suspends fails, and a lid setting with no swapfile behind it warns. -
A hung desktop shell no longer suspends into an unlocked session. The boot-time guard already ended a session whose shell had died; a shell that hangs rather than exits passed it, because a process existing is not a process locking. The guard now asks the shell, over the session bus it owns from its first moment, and ends the session when nothing answers twice: the machine sleeps showing a greeter either way, but only one of them was showing your work.
-
Every machine boots under a splash, and an encrypted one asks for its passphrase through it. plymouth is base layer now, with a vendored spinner theme (spinner_alt, GPL-3.0, credited in
assets/CREDITS.md): desktop boots show a spinner instead of boot text, and the LUKS prompt draws as a themed prompt with bullets instead of dracut's bare question. Nothing to configure, and a machine that declares no desktop keeps its textual boot. The image builds its initramfs with plymouth in it duringkuma build, so the splash arrives with the rebuild, not with the machine's next kernel update.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.18.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 551eb8dd872d5083d3d793f356efeaa6b8dc1611.
kuma v0.17.0
Fixed
-
A credential file is no longer executed as root.
kuma install --restorewrites a repository password to/var/lib/kuma/secrets/restore.env, and the first-boot restore used to read it with a shell, which runs whatever is on the right-hand side. A value like$(...)executed as root before anybody logged in. Both that unit andkuma backupnow parse the file instead. If your repository password contains a$, a backtick, a quote or a backslash, kuma will now refuse it by name, because those characters meant different things to different readers of the same file. A repository created before this release was encrypted with the expanded value, so change its password withrestic passwdbefore rewriting the file. -
kuma doctorno longer reports a working swapfile as missing. The hibernate check reads the swapfile throughsudo, and a declined or unpromptablesudograded the same as a broken file. It now says it could not ask. This was invisible in a terminal, wheresudoprompts, and reliable for anything readingdoctor --json. -
Nothing suspends into an unlocked session. The desktop shell owns idle lock, the lock keybind and lock-before-suspend, and it used to be started in a way that could not restart it. It now runs supervised, and a machine whose shell is gone ends the session rather than sleeping with the desktop on screen. A shell that hangs rather than exits is still not covered.
-
The desktop locks on idle again. Every kuma niri machine has shipped an idle lock at 15 minutes and screen-off at 16 that never armed: the shell needs each idle behavior to name an
action, and kuma set only a timeout, so both were dropped at startup. The config validated,noctalia config export mergedshowed both timeouts, and the machine never locked. It says so once in the journal (idle behavior 'lock' ignored: needs an action), which is where this was found. Rebuild and reboot to arm it; if you have set idle behaviors of your own in~/.local/state/noctalia/settings.toml, check each one names an action.kuma doctorgrades this now, from the shell's journal, since that is the only place a refused behavior is reported. -
A mounted disk image is refused again.
kuma install --disk <file>claimed to check whether the image was already mounted and could not:lsblkrefuses a file path. It resolves the loop device now. -
The install no longer widens your password hash. For two process spawns, the account hash and the backup password were readable by every local account on the machine.
Changed
-
kuma doctorsays when your desktop is running something other than what the image set. Settings you change in the shell are yours and the image will not overwrite them, but kuma cannot read that file, sokuma diffnever mentioned it. Doctor now names the keys and points atnoctalia config export merged. -
kuma doctorfails a desktop whose shell is drawing its own defaults. The shell reads kuma's config only because the service that starts it hands over the path, and one that comes up without it has a different bar, no palette taken from the wallpaper, and a first-run wizard. Doctor reads the running process rather than any file, because every file on such a machine still says the right thing. -
kuma buildis about four seconds faster. It deleted the image it replaced by sweeping the whole store; it deletes that one image now. -
SECURITY.md says that
sshdis enabled on every image and that the firewall lets it through, which means the account's password answers a prompt on port 22. That was true before and undocumented. Turn it off with[services] disable = ["sshd.service"]if you do not want it. -
The examples added Trayscale, and the niri example removed TextEditor. A machine rebuilt from an updated example gains Trayscale, and a niri one loses TextEditor, because convergence removes what it installed. Keep TextEditor by naming it in your own declaration.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.17.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from fb231dfefe8d1df37d647c89cf65583f52de3da8.
kuma v0.16.0
Added
- kuma's own verbs are in your launcher. Eight entries: edit the declaration, show drift, review proposals, system health, check for updates, rebuild, roll back, snapshots. Type
kumainto whatever launcher the session shipped and they come back. They are ordinary.desktopfiles, so there is no plugin to install and nothing to configure, and they are on the COSMIC desktop as well as the niri one.
Each opens a terminal window and holds it open after the verb exits, so output you were meant to read is still there. Press enter to close it.
- The niri desktop is one shell instead of eight programs. Noctalia draws the bar, notifications, wallpaper, OSDs, idle, lock screen, night light and a control centre that owns wifi, bluetooth, audio and brightness. waybar, mako, fuzzel, wob, swaybg, swayidle, swaylock and wlsunset are gone from the image, along with
kuma menuand the icon theme built for it.
Mod+D opens the shell's launcher, which lists your applications and kuma's verbs together, Mod+Ctrl+V opens clipboard history and Mod+Ctrl+W the wallpaper picker; Mod+Shift+/ lists every bind. Wifi and bluetooth are in the control centre rather than in a GTK window from another desktop, and its header holds lock, log out, suspend, reboot and shut down.
Kuma configures it from the image: its own bar layout and fonts, its wallpaper, no first-login welcome screen, and two things noctalia ships disabled that would have been regressions to inherit, locking on idle and night light.
-
kuma menuis gone. Everything it offered that was kuma's is in your launcher as a desktop entry, on COSMIC as well as niri, which the menu never reached. Everything it offered that was a device setting belongs to the shell's control centre.kuma cleanremoves the launch-count cache it left in your home. -
kuma editopens the declaration this machine is actually using, in$EDITORand otherwise nano, vim or vi.kuma edit --printprints the path it resolved without opening anything, which is the answer to "which kuma.toml am I editing" when a./kuma.tomlin the current directory is outranking~/.config/kuma/kuma.toml.
Changed
- The desktop follows one palette. On the niri desktop, kitty and GTK3 applications now take their colours from the palette noctalia is showing, on every change and again at login. That palette comes from the wallpaper by default, so changing the wallpaper changes the terminal, thunar, pavucontrol and nm-connection-editor with it; switch the shell to a built-in palette and they follow that instead. The image ships
adw-gtk3-themeand GTK3 applications now use it rather than Adwaita.
This includes the terminal's sixteen ANSI colours. A palette generated from a wallpaper maps all of them into one hue family, so a diff's + and - come out as tints of the same colour; a palette picked by name keeps real hues. The generated colours arrive as ~/.config/kitty/themes/noctalia.conf and ~/.config/gtk-3.0/noctalia.css: delete them and their include lines to keep the image's fixed palette.
Fixed
-
A VM disk logs you into the shell its image declares.
kuma vmgave its convenience account a bash login whatever[system].shellsaid, so a declaration readingshell = "fish"produced a VM that handed back bash.kuma installalready honored it. Rebuild the disk to pick it up. -
niri's Important Hotkeys overlay says what the keys do. The binds kuma splices into the session carried no titles, so the overlay that opens on first login named them by their command lines, one of which was an entire
sh -cpipeline. The media keys are hidden from it now and the rest are named.Super+Alt+Sis gone with them: it toggled a screen reader this image has never shipped, and a key that does nothing is worse than no key at all when the thing it claims to start is a screen reader.
Known limits
-
Kuma cannot see the settings you change from the desktop. The shell writes them to
~/.local/state/noctalia/settings.toml, which wins over the config kuma bakes into the image and which the image will never overwrite. Nothing in kuma reads that file, sokuma diffwill say a machine matches its declaration while the desktop is visibly running something else.noctalia config export mergedis what shows which settings are in effect. -
GTK4 applications do not follow the palette, which on a kuma machine mostly means flatpaks. libadwaita ignores a user stylesheet that redefines its palette, so they keep their own dark theme while the terminal and every GTK3 application move.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.16.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 78388d11a1543423a0cc223479f0b311571e3d23.
kuma v0.15.0
Swap was always zram, which is memory, so a kuma machine could sleep but never hibernate. It can now put a swapfile on the disk, point the kernel at it, and say plainly when the machine will refuse.
Added
-
Kuma machines can hibernate. Every machine's swap was zram, which is memory, so there was never anywhere to write a hibernate image. Kuma can now make a swapfile on the root disk and set the
resume=andresume_offset=kernel arguments that resume from it. -
kuma installasks, after the encryption question, and creates the file before it pulls the image. Off unless you say yes.--swap 16Ganswers early and--swap nonedeclines without being asked. On a disk you chose not to encrypt, the install plan says that hibernating writes the contents of memory to it in the clear. -
kuma hibernatedoes the same on a machine that is already running, so this needs no reinstall. It defaults to the size of memory, prints what it would do, and changes nothing without--yes.--off --yesremoves the swapfile, its fstab lines and the kernel arguments. The kernel arguments take effect on the next boot.
The file is never resized in place: growing it would move it on the disk, and the kernel would then resume from the wrong place. Change the size by turning it off and on again.
-
kuma doctorgrades it, and grades the part that fails silently. If the swapfile and the kernel arguments disagree, a hibernated machine boots fresh and the session is gone with nothing logged. Doctor compares the two and says so. Runningkuma hibernate --yeson a machine that already has a usable swapfile repairs exactly that, leaving the file where it is. Machines with no swapfile are not graded, because they promise nothing. -
Secure Boot machines are told the truth. A kernel that booted with Secure Boot on runs locked down, and a locked-down kernel refuses to hibernate. Kuma can still make the swapfile and set the kernel arguments correctly, and the machine still will not do it.
kuma installandkuma hibernatesay so before you spend the disk on it, andkuma doctorwarns rather than reporting a machine ready that never was. If you want hibernate on such a machine, turn Secure Boot off in firmware; otherwisekuma hibernate --off --yestakes the space back. -
The swapfile is labelled for SELinux.
systemd-sleepcan only read a file typedswapfile_t, and the policy's own default for a file under/varisvar_t, which it cannot read. A machine with the wrong label has a correct swapfile, correct kernel arguments and active swap, and fails at the moment you ask it to hibernate. There are two labels to get right, not one: the file, and the directorysystemd-sleephas to search to reach it. Kuma images declare that path a swapfile and relabel both at boot, andkuma doctorgrades both.
Known limits
- Hibernate does not work under Secure Boot, and that is the kernel's decision rather than kuma's. See above: everything kuma sets up is correct and the kernel still refuses. Turning Secure Boot off in firmware is the only way to have both.
- Proven in a virtual machine, not on your hardware. A gate installs a machine, hibernates it, boots it again and asks three questions the answer to "did it come up" cannot answer: the kernel's own
boot_id, a marker in tmpfs, and whether uptime continued. All three say the same session came back. What that cannot cover is your machine. Lid-close behaviour, firmware that mishandles S4, and drivers that do not survive a suspend vary by hardware, and none of them are things kuma can test for you. - Hibernating over ssh is refused, and not by kuma.
systemctl hibernateasks logind, which gates it on polkit, whose policy wants an active session; an ssh login is not one and there is no agent to answer the prompt. Hibernate from the desktop, where your session is active.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.15.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 4aeafd2d0bf871ab401df2a7893de5794cbe66f9.
kuma v0.14.0
A declaration describes a system; it never described your files. [backup] copies them somewhere else, and kuma install --restore puts a machine back.
Added
-
[backup]copies what[snapshots]keeps to a restic repository, on a timer, reading from a snapshot so nothing changes mid-copy. Requires[snapshots].enable;kuma checksays so rather than the unit failing at 3am. -
The credential is named, not held.
secret = "backup"points at/var/lib/kuma/secrets/backup.env, mode 0600, which you create. A declaration is committed and baked world-readable, so it is the wrong place for a password; a repository address containing one is refused. Recovering a machine therefore needs two things: this file and that credential. -
network_connectionscarries/etc/NetworkManager/system-connections, and is off by default. Those files hold a passphrase per network and nothing else can recreate them, sokuma doctornames which way it is set. -
kuma backup: bare reports without touching the network,--initseeds the first copy,--listasks the repository,--restorebrings a path back after a dry run. -
kuma install --restore <file>rebuilds a machine from the repository. One file carries the address and its credentials. The restore runs at first boot, after/var/homebecomes a subvolume; if the repository is unreachable that boot, the next one tries again. -
kuma doctorgrades backups on a stamp only a run that copied something writes, so a machine that has quietly stopped is visible. Staleness follows your declared interval. It also grades the credential's mode.
Changed
- Retention applies every copy; pruning runs weekly, because pruning repacks and moves far more data than forgetting a snapshot does.
kuma checkon a valid declaration now names the next command, and its JSON carriesactionseither way.kuma initno longer pinssystem.base, so a first declaration composes its own base like every published image.doctor's dangling-enablement check reports asenablementrather thanunits, which the failed-unit check already used.kuma switchpipes the image into root storage instead of staging 1.5 GB through a temp file, anddoctorruns its podman probes concurrently.
Fixed
kuma install --restoreleft the repository credential world-readable for the length of an install.kuma install --jsonemitted no JSON on failure and printed progress into the document.backup.reporeached generated shell without validation.kuma install --groupswas unvalidated where a declaration's groups are.kuma-brew-setupwrote as root into a directory tree a normal account owns; it refuses a prefix it does not own.- A live session no longer arms kuma's timers or converges Flatpak permissions.
[system.ca_certificates], added in 0.13, was documented nowhere.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.14.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 4020eef29e413a6d305c4d0587971cd0e89e2ffd.
kuma v0.13.0
The declaration could name an app and not say what it was allowed to touch, and that was the largest of a family: state that survives every rebuild, that the file cannot express, and that nothing on the machine will tell you about. This release closes the biggest one, draws the line around the rest, and makes the line a test rather than an intention.
It also stopped kuma sync from reporting success over a declaration it had never read, which is the bug that started the sweep.
Added
[overrides]declares Flatpak permissions, per app and per scope. The declaration could always name an app; it could not say what that app is allowed to touch, so every permission on the machine was undeclared state that survived every rebuild and that nothing could see.
Convergence is per key, not per file. kuma sets the keys you declare, removes the keys it set that you stopped declaring, and copies every other line through untouched, so Flatseal stays the editor people reach for and this file stays the record. The two meet at kuma capture. A key kuma never set is never kuma's to delete, even when it sits in the same group and contradicts what was just declared.
The shape is Flatpak's own override file rather than flatpak override's flag strings, which is a consequence of the above and not a preference: from a flag string kuma cannot tell which file key a --nofilesystem lands in without reimplementing flatpak's parser, and not knowing the key means the only safe convergence is replacing the whole file, which is exactly the authority per-key ownership refuses to take. flatpak override --show round-trips into it.
Both stores are covered. System scope is the default because that is where kuma installs the apps it declares; scope = "user" writes the per-user store, applied by a systemd --user unit rather than by root reaching into a home. One app declares into one store.
kuma diff and kuma capture see them, which is what makes the declaration owning permissions mean anything. diff reports both directions, a declared key the machine lacks and a key kuma set that the declaration stopped naming, and stays silent about keys kuma never set: those are somebody's machine, not drift. capture offers the reverse, turning a permission you toggled in Flatseal into a declaration entry, and offers it only for apps the declaration already installs, because a machine accumulates override files for software that left years ago. kuma check counts them.
Applied at boot and by kuma sync, deliberately never on the daily timer. An install arriving at a random hour is additive and idempotent; a permission reverting at a random hour changes what a running app can reach, and a toggle that silently flips back tomorrow afternoon is indistinguishable from a bug. The rule fits in a sentence: declared permissions are restored when you boot, and the session in between is yours.
kuma doctorreports a unit that is enabled and has no unit file. Found by reading a real machine: an enablement symlink indefault.target.wantspointed at a unit file that had been deleted,systemctl is-enabledanswerednot-found, and every other surface said the machine was in sync. Nothing fails, because a unit that was never found can never fail, which is exactly why nothing reported it.
Narrow on purpose. /etc on a real machine carries dozens of legitimately local files and a check that lists them all is one people learn to scroll past, but a link into a .wants directory is a machine saying it will start something at every boot, and a missing target makes that sentence false.
kuma add --flatpakrefuses an id Flathub does not list. The converger installs the whole declared list in one command, so a name that does not resolve fails the unit and the apps beside it never install, on that boot and every boot after: a typo taking down convergence for everything else.[services]has checked unit names at build time since it existed and this list checked nothing at all.
Checked against flatpak's own cached appstream data, never the network, so declaring a package cannot hang on a captive portal or fail on a plane. No cache to read means the check does not run rather than that the name is wrong, and a refusal names the way out, since a cache can be behind a genuinely new app.
[system.ca_certificates]declares the certificate authorities a machine trusts on top of the ones Fedora ships, keyed by the name each one gets on disk. The certificate goes in the declaration rather than beside it: a file that points at a path somewhere else is not one file any more, and a CA certificate is public by construction, which is what makes that safe here and unsafe for anything in[user].
A private key pasted in by mistake is refused rather than warned about, because it would be baked world-readable into every image built from that declaration and pushed to a registry. The anchors land under /etc by a COPY, so kuma doctor watches them for free, and update-ca-trust runs in the layer that adds them rather than leaving a trust store that only becomes true at boot.
Changed
-
kuma syncsays which declaration it converged to. It starts the same convergers boot does, those read what the image baked, and an edit that has not been built cannot reach them however often sync runs. It reported "Converged" anyway and then offeredkuma diffto "confirm the machine now matches its declaration", which was an instruction to go watch it fail. -
kuma add,kuma removeandkuma captureno longer end with "flatpak and brew changes converge on the machine at boot and daily". That sentence sat under thekuma buildandkuma switchedges and read as the alternative to them, when what converges at boot and daily is the previous list, forever, until a build of the edit boots. They now say that instead.
Fixed
- A declared
system.timezonewas owned by the image and watched by nobody. kuma writes the timezone asln -sfn /usr/share/zoneinfo/<zone> /etc/localtime, and the scan that works out which /etc files an image owns read only COPY destinations and shell redirects, so the single file that key exists to produce fell outside every check kuma has.
It matters most on an installed machine, because Anaconda writes its own /etc/localtime first, and ostree's merge keeps a local file over every future image: a declared timezone would simply never take effect, with nothing anywhere saying so. kuma doctor now grades it like any other file the image owns, and ln -s is read as the third way a build writes into /etc.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.13.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 96bcc308878fc7fb70ee02b7412bde00b965fa77.
kuma v0.12.0
This release is about the difference between a thing being true and a thing being checked. It exists because the day v0.11.0 shipped, a machine stopped converging and only a person reading a journal could tell.
Three of its claims were true and unchecked: that a converger recovers on its own, that the walkthrough a stranger types still describes the tool, and that a declaration written for any released kuma keeps working. All three are now commands that pass or fail.
It also grew a face. kuma menu was meant for a later release and was built on a branch to keep this one honest; it earned its way in by being finished, and by being the first thing here a person sees rather than a thing they can check. The rule that governs it is the same one this release is about: a menu entry is one keystroke with no diff and no pause, so it may change machine state immediately and may never write your declaration.
Added
kuma menuopens a menu in the desktop's own launcher: apps, connect, declaration, system, notifications, power. Bound toMod+Don niri, which is the key that used to open the bare launcher; the menu lists applications itself, so two keys for one job would have left one of them showing strictly less. The stock bind is grepped for before it is replaced, so a niri release that renames it fails the build rather than shipping media whose main key does nothing.
The desktop kuma assembles had no face of its own, so every device-level setting was somebody else's control panel and nothing at all owned the system. This is not a settings application, which is a desktop environment's job and never finishes; it is a tree rendered by fuzzel --dmenu, themed by the fuzzel config kuma already ships, in the launcher the clipboard picker already uses. Nothing new is installed and nothing new is drawn.
Applications are rows in it. Apps was a group holding one entry that opened a second launcher on top of the first; now the menu lists the applications itself, so typing firefox at the top finds Firefox the same way reboot finds reboot. That means implementing the desktop entry spec rather than gesturing at it, and the parts people skip are the parts that bite: on the machine this was written on, 11 of 32 entries are NoDisplay=true and must never appear, and 12 carry field codes (%U, %F) that arrive as literal arguments if nothing strips them. TryExec, Hidden, OnlyShowIn, NotShowIn, Terminal and the spec's quoting are all honoured, entries earlier in the search path shadow later ones, and launch counts are kept so the list is not alphabetical forever.
kuma menu --list prints the rows instead of drawing them, marking which are on screen at rest and which only typing reaches. How to see the menu over ssh, in a VM with no session, or when wondering why a row is missing. scripts/smoke.sh runs it on every installed machine it boots, which is the only automated thing that touches the menu at all.
Descending narrows what is shown, never what can be found. Opening a group shows that group's rows and a way back; every other row is still in the list, so someone who opens Connect and then remembers they wanted to reboot types reboot and gets it, rather than finding that the menu quietly became a smaller menu.
It opens on its groups and searches every row. A launcher can only match against the lines it was handed, so a menu of submenus cannot be searched: typing reboot at the top of one matches nothing and the person who knew what they wanted navigates anyway. Every row is in one list, the groups come first, and the window is sized to exactly their number, so browsing sees six sections while typing reaches all of them. reboot finds the row, power finds the group and its five. A group's row descends into just its own.
The build gives the menu its own icon theme. Adwaita's symbolic icons are the right drawings and the wrong colour: each hardcodes a near-black fill, fuzzel renders the file as it is, and kuma's launcher is #0e1626, so they drew invisibly. Every icon the menu names is now copied and repainted in the launcher's own foreground into /usr/share/icons/kuma, which also buys alignment that text glyphs cannot: fuzzel's icon column is a fixed-width slot, while a proportional face gives every glyph a different width and leaves the labels ragged.
Any hex is rewritten rather than one known value, because the twenty files carry three different darks and a sed for the common one would have left two icons invisible, which reads as a glitch rather than as a bug. fill="none" is left alone, since it means transparent. Then the step checks its own work and fails the build naming the file, because a generator that cannot say whether it worked is how the icons went out invisible the first time. The colour is asserted equal to assets/fuzzel.ini's, so the icons and the text they sit beside cannot part company.
A row that prints keeps its window. A terminal launched as kitty -e <command> closes the moment the command exits, so Health would have shown a sudo prompt and vanished as the password was finished, and every row that runs for a second is exactly the one whose output is never read. The command runs through a shell that waits, which also reports a non-zero exit the closing window would otherwise have swallowed.
Selection comes back as an index rather than as text, and fuzzel is asked for --only-match, so a typo that matches nothing cannot be mistaken for a choice. The window is sized to its longest row rather than to the app launcher's width, and a row inside its own group drops the prefix that the group heading already said.
The menu never writes the declaration. kuma capture is the one deliberate path from what the machine has to what the file says, and its safety is the ceremony: dry run, review, confirm. A menu entry is one keystroke with no diff and no pause, so a second writer would not add convenience, it would remove the only thing that made the first one safe. Declaration entries open the file in your editor or run a verb that asks for itself. Machine state (lock, suspend, notification mode) changes immediately, because that is the half a launcher is better at than a panel.
Entries appear only when their program is present, so the menu offers the terminal tool where there is one, the graphical tool where there is not, and no row at all where there is neither. A group whose every entry is missing is absent rather than empty. Nothing runs as root to draw the menu, so opening it never prompts.
-
Suspend, reboot and power off have a menu. Stock niri binds a lock and a quit and nothing else, so on a laptop the only way to suspend from the desktop was a terminal.
-
NetworkManager-tuion the niri desktop. It is what the menu offers for the network in preference to the graphical editor, because a terminal program inherits the terminal's theme rather than arriving as a window from another system, and it is the only network tool left on a machine whose session will not start. -
kuma doctorreports a Flatpak permission override that points at nothing. A machine that was another distribution first can carry an override symlink into a directory that distribution shipped and kuma does not have, and it survives in/varacross every image switch because nothing has ever looked at it. Graded a warning: flatpak tolerates it and the machine is not broken, but it is a statement about an app's permissions that is not true, and the declaration cannot see it yet. Regular override files are left alone; they are somebody's settings, whoever wrote them. -
kuma doctorreports a machine that has stopped converging. It already graded whether the last convergence attempt failed; it could not see a machine whose last attempt succeeded three weeks ago, because "last run succeeded" and "timer active" are both true of a machine that quietly stopped converging. Asked only of the convergers a timer runs again, which is what makes the age meaningful: they run at boot and on a daily timer that catches up after a machine was asleep, so seven days without one is seven missed firings and every boot in between. The account converger runs at boot only, so its age would measure uptime and it is not asked. -
Every declaration this project has shipped as an example is now tested against the kuma being built. Schema v1 is claimed permanent, and nothing held anyone to it: a renamed field or a changed default would have been found by somebody upgrading a machine whose declaration predated it. The corpus is every distinct example shape from every release, and an example that changes has to be recorded there before it ships. This covers old declarations on new kuma; the other direction, a newer declaration read by an older kuma, is still a hard error and still undecided.
-
Every command the documentation tells you to run is now checked against the command line kuma actually has. Using the machine proves the tool works and never reads the docs, so a renamed flag is noticed the moment somebody types it while the page still naming the old one rots unread. This catches the words, not the behaviour: what the commands do is proven by CI where CI can reach it, and the walkthrough now carries a record of which of its commands that is true of.
-
AppImages run on a kuma machine without a declaration naming anything. An AppImage is a squashfs its runtime mounts over FUSE before any of its own code runs, and Fedora ships only FUSE 3, so a downloaded AppImage failed at
dlopen(): error loading libfuse.so.2on a machine that was otherwise complete. Both halves of FUSE 2 are in every image now: the library the runtime loads, and the setuid helper that library mounts with. They are separate packages and neither requires the other, so naming one looks like the fix and leaves a different error in the same place. Not gated on a desktop, because the packages are under a megabyte together and the failu...
kuma v0.11.0
The media is a download. v0.10.0 built the ISO in CI and booted it on every push to prove it could, and left attaching it to a release switched off until that job had a history rather than a first day; it has one, so a release now carries the thing you write to a USB stick. The walkthrough leads with it, because "describe a machine, build an image, then make your own media" was the order a project with nothing to download had to teach.
One thing the release also fixes is the assertion that guards v0.10.0's signature policy, which could not see the policy going missing.
Added
- Releases carry the live ISO. A tag builds it, boots it, signs it with Sigstore like every other release asset, and attaches it to the release that already exists, so downloading kuma and installing kuma are the same page. Booting it is not a formality: the same script CI runs starts the ISO under UEFI and asks the live session whether it reached a desktop, so the file on the release page is one that came up rather than one that built. This was wired in v0.10.0 and left off, waiting on the job that builds it having a run history rather than on a tag being its first real exercise; ci.yml has built and booted the ISO on every push to main and on a daily cron since, and went green before this was turned on.
Fixed
- The install-and-boot smoke tests could not see a missing signature policy. They asserted that
kuma doctorreports nothing gradedfail, and the three ways this control goes missing are all gradedwarn: no policy file, one that will not parse, or one that does not name kuma's repository. Only a policy naming a key it does not have, or one with nowhere to look for signatures, was everfail. So the scan saw the half-broken states and was blind to the absent one, which is the likeliest of the three and the one an/etcmerge can cause. An installed machine now has to gradesignaturesasok, which is the requirement rather than "not fail" because every image writes the policy, the key and the registries.d entry unconditionally. The cross-version job reports whether upgrading brings the policy to a machine installed before it existed, and fails only if an upgrade takes it away.
Changed
- The getting-started walkthrough leads with installing a machine rather than building an image. It was ordered "build an image, then build media" because media was something you had to make yourself, and it said so; with media on the release page the front door is download, boot, install, and describing your own machine is what you do next rather than what you do first. The builder's path is unchanged and still there, one step later.
Static x86_64 binary, no runtime dependencies.
$ curl -LO https://github.com/Letdown2491/kuma-linux/releases/download/v0.11.0/kuma-x86_64-unknown-linux-musl
$ chmod +x kuma-x86_64-unknown-linux-musl && sudo mv kuma-x86_64-unknown-linux-musl /usr/local/bin/kumaVerify it came from this workflow:
$ cosign verify-blob \
--bundle kuma-x86_64-unknown-linux-musl.bundle \
--certificate-identity-regexp '^https://github.com/Letdown2491/kuma-linux/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
kuma-x86_64-unknown-linux-muslBuilt from 54868ccd411d0ebebe38ef7c689f3f8941e28352.