Claude Desktop and ChatGPT Desktop ship for macOS and Windows. The Linux path for both is a community wrapper that converts the official build into an RPM -- so what lands is still a package dnf owns and can remove, which is the part of the dnf/flatpak rule that actually matters. What they need an exception for is the build itself, and there is no packaged form to prefer over it. `panama app` builds one by name, and is deliberately not part of ./install. A source build is slow, wants the network throughout, and depends on an upstream that moves -- twenty minutes in, an error, with nobody at the keyboard, which is the exact failure the interview exists to prevent. Asking for one is something you do on purpose, and it is also the rebuild path when a new version ships. Nothing is pinned. Each build takes the current default branch and the current upstream release, and reports a failure rather than working around it, leaving the tree where the error can be read. sunhat pinned versions and every pin was a 404 within a release cycle. Adding one is adding a file to setup/apps/, and the file has to say why the exception exists -- the contract fails a definition that does not, because the guard against this list growing by habit is having to write the reason down. sunhat had seventy-odd installers and a reason recorded for none of them. The contract had a bug worth recording: `while read` on the right of a pipe runs in a subshell, so two of its three per-definition checks recorded findings into an array that went out of scope at the end of the loop. It reported PASS on a definition with no description and no build function. Found by standing one in deliberately and noticing only the third check spoke up. Also: nautilus-open-any-terminal is now declared, and Panama's copy of the extension is gone. Fedora packages that extension AND its gsettings schema, and Panama shipped its own fork of the .py over the same path while declaring neither -- so a fresh machine got an extension whose schema did not exist. It worked here only because the RPM has been installed since sunhat. The fork was also 63 lines behind the packaged version, missing its newer Nautilus and Caja handling. Auditing the rest of config/copy for the same shape found nothing else: dnf.conf is a config file its package expects to be replaced, and the GPU udev rules are Panama's own. 125 contracts pass. Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
7.0 KiB
Panama
Formerly Sunhat. A personal config for Fedora, with the intention of helping a user set up their Fedora system with one command.
git clone https://git.gbrown.org/gib/Panama.git ~/.local/share/Panama
~/.local/share/Panama/install
install asks its questions first and then runs the stages in setup/scripts/
in order, without stopping again:
| Script | Does |
|---|---|
interview |
Every prompt, before anything is installed. Answers last one run and are never written to a durable path |
install-packages |
Repos (RPM Fusion, Terra, Hyprland COPR), the package lists in setup/packages/, then whichever optional categories were chosen |
link-dotfiles |
Symlinks config/dot/<name> → ~/.config/<name>, and seeds the wallpaper, cursor theme and Firefox chrome |
change-settings |
Copies config/copy/ over /, applies gsettings, enables user services |
link-vicinae-scripts |
Publishes the Vicinae script commands |
setup-identity |
git config, gh auth login, an SSH key — whichever were asked for |
install-hardware |
NVIDIA, Secure Boot enrollment, Fedora's extras, firmware — each only if it was asked for. Last, because enrollment and firmware are consumed at the next boot |
The run ends with a health summary from panama-doctor, which reports what is
actually running rather than what was attempted. It never fails the install: on a
fresh machine it legitimately reports things as not yet configured.
Optional applications
Every machine gets the lists in setup/packages/. The interview also offers the
categories in setup/packages/extras/ as a checklist, so a work laptop need not
acquire emulators and a desktop need not skip Steam. Nothing is preselected.
A category is one file. A bare line is a dnf package and a flatpak: line is a
Flathub ID, because the applications in a category do not all come from one
place. Adding a category is adding a file — the menu is read from the directory,
not written down anywhere.
Existing configs are moved to config/old/ rather than overwritten.
The desktop
Hyprland, with a shell written from scratch. It began as a replacement for a GNOME session — Forge for tiling, Dash-to-Dock, Openbar, Vitals — and was built to reproduce it closely enough that muscle memory transferred: same keybinds, same panel contents, same dock, same Tokyo Night Moon palette.
That is history now rather than a second option. Panama installs and configures
one desktop, and the GNOME session it grew out of is neither installed nor
configured here. What each piece replaced is recorded in
config/dot/hypr/DESKTOP-PARITY.md and in
the comments of the components themselves, because knowing what a thing was
modelled on explains why it behaves the way it does.
GNOME is not gone from the machine: gnome-control-center is a declared
dependency, and Panama's own Settings hands off to it for the panels it
deliberately does not own — Online Accounts, Color, Sound, Network, Keyboard,
Privacy, Wellbeing, Accessibility, and System for users, date and time, region
and remote desktop. The allow-list in
services/SystemSettings.qml
is what decides; anything not on it is a panel Panama owns itself.
| Piece | What it is |
|---|---|
config/dot/hypr/ |
Compositor config. Lua, not hyprlang — see its README |
config/dot/quickshell/ |
The shell: bar, dock, Continuum overview, Settings, Screen Intelligence, focus sessions, quick settings, notifications, screenshot UI |
config/dot/vicinae/ |
Raycast-style launcher, themed |
config/dot/uwsm/ |
Session environment (see the uwsm caveat in the hypr README) |
config/dot/wofi/ |
Fallback launcher, in case the shell fails to start |
config/dot/xdg-desktop-portal/ |
Portal backend routing |
Start here: config/dot/hypr/README.md — it
covers the Lua migration, the uwsm environment gotcha, the HDR decision, the
full keymap, and troubleshooting.
Log in as "Hyprland (uwsm-managed)", not plain "Hyprland".
Layout
bin/ Small user-facing commands on PATH; `panama` is the entry point
config/
bash/ .bashrc, aliases, env (env is gitignored)
copy/ Files copied verbatim over / (needs sudo)
dot/ Symlinked into ~/.config
firefox/ Vendored Firefox chrome, linked into the browser profile
local/ Icons and the cursor theme, linked into ~/.local/share
old/ Backups of whatever was replaced (gitignored)
wallpapers/ Copied into ~/Pictures/Wallpapers when absent
setup/
apps/ Applications built from source, one file each
packages/ One package per line; extras/ holds the optional categories
scripts/ Run in order by ./install
tests/ Contracts. See below
docs/ Settings reference, and the design specs behind the work
Tests
121 of them, under tests/. Run the lot, or a subset by pattern:
panama test # everything
panama test dock # just the ones matching "dock"
tests/setup/interview-contract # or one directly; they are plain executables
They are called contracts rather than unit tests because that is what they are:
each one pins a decision that was expensive to get right and is cheap to undo by
accident. Most read or measure the real thing — launching a shell to measure a
surface's geometry, standing stub commands on PATH to see what a stage would
have installed, running a script against a throwaway HOME — rather than
asserting things about source text, because the bugs worth catching here have all
been ones that source text looked fine for.
tests/setup/ The installer: the interview, package lists, hardware, extras
tests/quickshell/ The shell and its settings pages
tests/hypr/ The compositor config
The panama command
panama update # review, commit and sync this repo
panama edit # open it in Neovim
panama doctor # what is actually running, not what was installed
panama test # every contract, or a subset by pattern
panama upgrade # re-run ./install from anywhere
panama app # applications no repository carries; build one by name
panama app is deliberately not part of ./install. Everything else Panama
installs comes from dnf or Flathub; these are built from source because no
packaged form exists, and a source build is slow, wants the network throughout,
and depends on an upstream that moves. That is the failure the interview exists
to prevent, so asking for one is something you do on purpose — and it is also
how you rebuild when a new version ships. Nothing is pinned: each build takes
the current upstream and reports a failure rather than working around it.
Adding one is adding a file to setup/apps/, and the file has to say why the
exception exists.
None of the scripts in this repository carry a .sh extension. A shebang and
the executable bit already select the interpreter, and the extension only
becomes something to keep in sync — which it did not stay.