Let a machine say what it is for, and give Firefox its face back

Phase 4: the optional application categories, and the Firefox chrome.

Everything Panama installed until now was what every machine gets, which meant a
work laptop acquired emulators and a desktop that wanted Steam had to be told
about it by hand. The interview now offers the categories in
setup/packages/extras/ as a checklist -- gaming, creative, communication,
virtualization -- and nothing is preselected, because a default here installs
applications nobody chose on a machine whose owner answered a question they
thought was about something else.

A category is one file, and a category mixes both package managers because the
applications do: Steam is in RPM Fusion, Slack publishes only a flatpak. So a
bare line is a dnf package and a flatpak: line is a Flathub ID, and one file
holds the whole answer rather than splitting each category across two. The menu
is read from the directory rather than written down, so adding a category is
adding a file. Every name in all four was resolved against the actual
repositories before being written down, and the contract re-resolves them --
the point of admitting applications one at a time is that they stay installable,
and a typo here fails on somebody else's machine, not this one.

Firefox is declared, and its chrome is Edge-Frfox, vendored into config/firefox.
sunhat carried that theme with no license and no attribution; it is MIT, and now
it says so and says whose it is.

It is the only piece of Panama's configuration that does not go to a path this
repository chooses. Firefox owns the profile directory, names it with a random
salt, and does not create one until the browser has been run -- so link-dotfiles
finds or creates a profile and links both halves into it. Both, or neither works:
chrome/ is the CSS and user.js sets the preference that makes Firefox read chrome/
at all, without which the theme is a directory of dead files.

Two assumptions there were wrong, and the contract exists for both. Firefox has
moved to the XDG directories -- the profile root is ~/.config/mozilla/firefox on
this build, not ~/.mozilla/firefox, and writing to the wrong one themes nothing
and says nothing about it. And -CreateProfile turns out to be non-interactive, so
a fresh machine gets the theme on the first install rather than the second. The
contract runs link-dotfiles for real against a throwaway home with no profile in
it and looks at what came out; it was checked by pointing the search at the
legacy path only and watching it fail.

Also: the enrolment/enrollment spellings from the last commit are corrected. This
repository is US-spelled everywhere else -- color 1131 times against colour never
-- and consistency in prose is worth as much as it is in code.

Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
This commit is contained in:
Gabriel Brown
2026-08-20 21:24:15 -04:00
parent b319d1a5e1
commit 88497826ec
168 changed files with 6293 additions and 32 deletions
@@ -83,6 +83,15 @@ interview, so a work laptop does not acquire emulators and a desktop does not sk
Steam. But the menu starts nearly empty and grows by explicit decision, one
application per decision — it is not seeded by porting sunhat's list.
The categories shipped are `gaming`, `creative`, `communication` and
`virtualization`, one file each under `setup/packages/extras/`. A category mixes
both package managers because the applications do — Steam is in RPM Fusion, Slack
publishes only a flatpak — so a bare line is a dnf package and a `flatpak:` line
is a Flathub ID, and one file holds the whole answer for a category rather than
splitting it across two. The interview reads the menu from the directory, so
adding a category is adding a file, and nothing is preselected: a default here
would install applications nobody chose.
The rule is that every application Panama installs must come from dnf or from
Flathub, with flatpak preferred where both exist. Anything requiring a pinned RPM
URL, a `curl | bash`, or a source build is admitted only case by case, with a stated
@@ -173,7 +182,7 @@ for icons and Vicinae themes, so it needs no stage of its own — and because
was invented to satisfy holds anyway.
`install-hardware` runs *last*, not fourth. The constraint was always "late", and
fourth of eight is not late: MOK enrolment arms a prompt consumed at the next
fourth of eight is not late: MOK enrollment arms a prompt consumed at the next
boot and a firmware update may ask for a reboot, so a machine that reboots out of
this stage should already be completely configured — dotfiles linked, settings
applied, identity set. Placing it after `setup-identity` costs nothing, since
@@ -187,8 +196,8 @@ Ordering constraints that are not obvious and must not be broken:
installed by `install-packages`, which already runs first.
- `setup-identity` follows `install-packages`, because `gh` is installed there.
- `install-hardware` is last, for the reason above. It also has to follow its own
driver install: akmods generates the certificate that MOK enrolment enrols, so
there is nothing to enrol until `akmod-nvidia` is on the machine.
driver install: akmods generates the certificate that MOK enrollment enrolls, so
there is nothing to enroll until `akmod-nvidia` is on the machine.
Every stage stays independently re-runnable and independently failable, which is
the existing contract and the reason Panama exists.
@@ -313,14 +322,14 @@ hostname and it was one of the things that made sunhat unmaintainable.
Everything below blocks sunhat's deletion.
- **NVIDIA and secure boot** — `akmod-nvidia`, `xorg-x11-drv-nvidia-cuda` and MOK
enrolment via `mokutil --import`. sunhat's version opened an editor to hand-edit
enrollment via `mokutil --import`. sunhat's version opened an editor to hand-edit
grub; the replacement does not, because a step that requires a human mid-run is
the failure this repository exists to avoid. It turned out not to need one:
sunhat's editor step existed to delete duplicated kernel arguments, and `grubby
--update-kernel=ALL --args=` replaces an argument that already exists rather than
appending a second copy, so the duplicates cannot accumulate.
Enrolment is likewise non-interactive, and did not need a compromise either.
Enrollment is likewise non-interactive, and did not need a compromise either.
`mokutil --generate-hash` and `--hash-file` are exactly the pair for this: the
interview asks for the password, hashes it immediately, and records only the
hash — so the plaintext never reaches the answers file, the environment, or a
@@ -344,11 +353,27 @@ Everything below blocks sunhat's deletion.
to port; most of them install by methods that no longer work. Reviewing them is
its own task, done one application at a time against the dnf/flatpak rule above,
and it does not block anything else in this spec.
- **Firefox** — the package itself, which Panama does not currently install, plus
- **Firefox** — the package itself, which Panama did not previously install, plus
sunhat's `themes/firefox` userChrome. Helium is the daily driver; Firefox stays
because web development means checking a second engine. If Helium supports
userChrome, it gets the equivalent treatment.
The theme is [Edge-Frfox](https://github.com/bmFtZQ/edge-frfox), MIT, vendored
into `config/firefox` with its provenance recorded — sunhat carried it with
neither. It is the only piece of Panama's configuration that does not go to a
path this repository chooses: Firefox owns the profile directory, names it with
a random salt, and does not create one until the browser has been run. So
`link-dotfiles` finds or creates a profile and links both halves into it —
`chrome/` for the CSS and `user.js` for the preference that makes Firefox read
`chrome/` at all, which without the other is a directory of dead files.
Two things here were assumptions that turned out wrong, and the contract exists
for both. Firefox has moved to the XDG directories, so the profile root is
`~/.config/mozilla/firefox` on a current build and `~/.mozilla/firefox` on an
older one; writing to the wrong one themes nothing and says nothing. And
`-CreateProfile` turns out to be non-interactive, so a fresh machine gets the
theme on the first install rather than the second.
Deliberately not ported: sunhat's `configs/docker/compose_files`. Those are server
workloads — caddy, gitea, homeassistant, nextcloud_aio, pihole, vaultwarden,
wireguard, searxng — and belong to whatever runs them, not to a workstation
@@ -405,7 +430,7 @@ three places instead:
the broken tree.
- **The hardware contract**, for the one stage that cannot be verified by running
it. `install-hardware` installs a proprietary driver and queues a Secure Boot
enrolment, and the machine Panama is developed on is an AMD desktop; so every
enrollment, and the machine Panama is developed on is an AMD desktop; so every
privileged command is stood in on `PATH` and the contract asserts which answer
led to which call. That covers the decisions, which is the part that can be
wrong. It does not cover whether `akmod-nvidia` builds, and does not claim to —
@@ -415,6 +440,17 @@ three places instead:
- **`panama test`**, which makes the existing 119 contracts runnable in one command
for the first time.
One gap this list did not cover was found while doing phase 4 and is worth
recording, because it is the exact failure mode the spec opens with. Every list
in `setup/packages/` is annotated, and `install-packages` passed the whole file
to dnf, comments included; dnf does not ignore an argument it cannot match, so
`set -e` ended the stage on the first annotated list. It could not be seen from
here — on a machine that already has everything, only the comments fail — and
every contract that reads those lists strips comments before comparing, so the
tests were reading a file the installer was not. A checker with a blind spot is
worse than none, and this one had the same blind spot the dependency contract's
own header warns about.
The open risk this leaves is anything that breaks only on a genuinely clean system
and that none of the three notices — a repository that fails to enable, a package
renamed between Fedora releases. That risk is accepted; a VM run can settle it later