The machine runs essentially every popular self-hosted service, and on looking at them the batch was revised once and then dropped. Almost all of it is infrastructure for other services rather than anything a desktop consumes -- whisper there feeds Bazarr subtitles, not dictation here -- the desktop has the better GPU anyway, and the one genuinely desktop-shaped idea is already solved by an installed application. Recorded rather than deleted so it is not proposed again, with the lesson that matters: a service existing is not a reason to integrate it. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
314 lines
13 KiB
Markdown
314 lines
13 KiB
Markdown
# Settings beyond GNOME
|
|
|
|
Written 2026-08-19, after the last GNOME panel worth owning was taken.
|
|
|
|
The goal has changed. Up to now the target was parity: make GNOME Settings
|
|
unnecessary. That is done except for Color, which is blocked by the platform
|
|
rather than by effort. What follows is the other direction — the things a
|
|
settings application should own on *this* machine, which neither macOS nor
|
|
Windows can offer because neither assumes you run your own infrastructure.
|
|
|
|
Everything below was checked against this machine before it was written. Where
|
|
something is not possible, it says so and why.
|
|
|
|
---
|
|
|
|
## What is already true
|
|
|
|
| | |
|
|
|---|---|
|
|
| `dnf5`, `dnf`, `flatpak` | present; **2 flatpak updates pending** at time of writing |
|
|
| `firewalld`, `nmcli` | present |
|
|
| `podman` | 22 images, 5.31 GB reclaimable |
|
|
| `gamemoded`, `mangohud` | present; Steam library is 1.2 TB |
|
|
| Nextcloud client | installed and running |
|
|
| `rustdesk` | installed |
|
|
| Self-hosted services | cloud, git, home, agentchat all answering over HTTPS |
|
|
| `wg`, `tailscale` | **absent** — the WireGuard tunnel lives on the router, not here |
|
|
|
|
That last row matters: a VPN page on this machine would have nothing to drive.
|
|
Skipped for that reason, not forgotten.
|
|
|
|
---
|
|
|
|
## Batch 1 — Keeping the machine current
|
|
|
|
The clearest "this OS is unfinished" gap. Both macOS and Windows put this front
|
|
and centre; here you update in a terminal.
|
|
|
|
### 1.1 Software Update
|
|
|
|
- Pending updates by source (dnf and flatpak, counted separately because they
|
|
fail separately), download size, and which are security fixes.
|
|
- Whether a kernel update means a reboot, stated plainly rather than implied.
|
|
- When it last checked, and a switch for automatic updates (`dnf-automatic`).
|
|
- Update history, and what `dnf history undo` can and cannot reverse.
|
|
|
|
**Mechanism.** `dnf5` has machine-readable output; `flatpak remote-ls --updates`
|
|
is already scriptable. Applying updates needs root, so pkexec, prompting through
|
|
the agent that is already running.
|
|
|
|
**Deliberately excluded.** Automatic *unattended* installation of anything but
|
|
security updates. A machine that reboots itself into a new kernel while you are
|
|
mid-sentence is not a feature.
|
|
|
|
### 1.2 Snapshot before updating
|
|
|
|
The reason to do updates first. Snapper is wired up as of yesterday, so an
|
|
update can take a restore point before it touches anything — with the snapshot
|
|
named after the transaction, so the Snapshots page shows *"before 47 package
|
|
updates"* rather than a timestamp.
|
|
|
|
Neither macOS nor Windows does this cleanly. It is the single most distinctive
|
|
thing on this list and it is nearly free now.
|
|
|
|
**Done when** the machine can be brought fully current from Settings, and going
|
|
back is one click on a page that already exists.
|
|
|
|
**Landed 2026-08-19.** All three sources, counted and applied separately.
|
|
Firmware was added to the plan after the fact and belonged there: `fwupd` is
|
|
installed, reports cleanly as JSON, and answers instantly.
|
|
|
|
Two things the shape of the data forced. Checking costs about nine seconds, so
|
|
the page opens on a cached result and says when it was taken -- a first visit
|
|
with nothing cached goes and finds out rather than showing a confident "up to
|
|
date" it has no basis for. And `dnf5` exits 100 when upgrades exist, which is
|
|
success, not failure.
|
|
|
|
Automatic updates apply to applications only, through a Panama-owned systemd
|
|
user timer running daily with a randomized delay. Packages still ask.
|
|
dnf-automatic is reported as absent rather than offered, because installing
|
|
software is not a settings action.
|
|
|
|
Health gained a check for it, which is where the interesting bug was: it first
|
|
returned status "degraded", and the doctor's vocabulary is ok, warning, error,
|
|
unconfigured. The check was counted as nothing at all while the summary still
|
|
reported healthy -- the same silent no-op this codebase keeps relearning, this
|
|
time caught by writing the contract. A contract now asserts every status a check
|
|
can return is one the doctor counts.
|
|
|
|
---
|
|
|
|
## Batch 2 — Your infrastructure — DROPPED 2026-08-19
|
|
|
|
Recorded rather than deleted, so it does not get proposed again.
|
|
|
|
The machine turned out to run essentially every popular self-hosted service:
|
|
Immich, Beszel, Uptime Kuma, Sentry, Gitea with runners, Jellyfin, Plex, RomM,
|
|
SearXNG, Open WebUI, ollama, ComfyUI, whisper, piper, the *arr stack, Vaultwarden
|
|
and more, across a home server and a VPS. On seeing it, the plan below was
|
|
revised once (read Beszel and Uptime Kuma rather than reimplement their polling)
|
|
and then dropped entirely, on Gabriel's judgement, which was right:
|
|
|
|
* Almost all of it is infrastructure FOR other services rather than anything a
|
|
desktop would consume. whisper on the server exists to feed Bazarr's
|
|
subtitles, not to transcribe speech here.
|
|
* The desktop has a better GPU than either server, so anything worth running
|
|
for the desktop's sake should run on the desktop.
|
|
* The one genuinely desktop-shaped idea -- dictation -- is already solved on
|
|
this machine by Wispr Flow.
|
|
|
|
The lesson is worth keeping: the services existing is not a reason to integrate
|
|
them. A page earns its place by answering a question someone actually asks, and
|
|
"is my media server up" is a question Uptime Kuma already answers better,
|
|
somewhere the desktop is not.
|
|
|
|
**Possible survivors, both local rather than remote.** The Nextcloud desktop
|
|
client runs here and its sync state is a real desktop concern in the way iCloud
|
|
Drive is; RustDesk is installed here and pairs with the RDP controls already
|
|
built. Neither is "reach into my server", which is what was dropped.
|
|
|
|
---
|
|
|
|
## Batch 2, as originally written
|
|
|
|
The part no shipped operating system has, because no shipped operating system
|
|
assumes the user runs the servers.
|
|
|
|
### 2.1 Services
|
|
|
|
A page listing your self-hosted services with reachability, TLS certificate
|
|
expiry, and version where the service exposes one.
|
|
|
|
**Mechanism.** Endpoints stored as a preference (so the list is yours, not
|
|
hardcoded); an HTTP HEAD per endpoint and an `openssl s_client` for expiry.
|
|
|
|
**Deliberately a status view, not management.** Settings will not administer a
|
|
VPS. "Is my stuff up, and does anything expire soon" is the question worth
|
|
answering here; anything more belongs in the service's own admin surface.
|
|
|
|
**Watch for.** Certificate expiry is the row that earns this page. It is also
|
|
the one that must never report a false green — a check that fails should say
|
|
"could not check", never "fine".
|
|
|
|
### 2.2 Nextcloud
|
|
|
|
The iCloud Drive equivalent, and the reason this list exists rather than a
|
|
OneDrive page. Sync state, which folders, quota, conflicts, pause and resume.
|
|
|
|
**Mechanism.** The desktop client is running; its configuration is readable and
|
|
`nextcloudcmd` exists. **Risk to check first:** the client's status surface may
|
|
be too thin to drive a page honestly. If it is, this becomes a smaller card —
|
|
"syncing / paused / N conflicts" and a button to open the client — rather than
|
|
a pretend management panel.
|
|
|
|
### 2.3 RustDesk
|
|
|
|
The peer to the RDP controls already built. ID, whether unattended access is on,
|
|
and — the part that matters to you — whether it is pointed at your own relay or
|
|
the public one.
|
|
|
|
**Mechanism.** `rustdesk --get-id`, plus its config. Credentials follow the same
|
|
rule as everywhere else: never through Panama, never on a command line.
|
|
|
|
---
|
|
|
|
## Batch 3 — Machine control
|
|
|
|
### 3.1 Firewall
|
|
|
|
You expose services, so this is worth owning. Zones, what is open, and which
|
|
zone a connection is in.
|
|
|
|
**Mechanism.** `firewall-cmd`, which is fully scriptable, plus polkit for
|
|
changes.
|
|
|
|
**Deliberately excluded.** Rich rules and direct rules. They are a syntax, not a
|
|
setting, and a settings page that half-supports a syntax is a trap.
|
|
|
|
**This one is security-sensitive**: every change must state what it exposes,
|
|
and closing a port someone is currently connected over should say so first.
|
|
|
|
### 3.2 Containers
|
|
|
|
podman is here and Storage already found 5.31 GB of reclaimable images. Running
|
|
containers, images, disk, logs, start and stop.
|
|
|
|
**Deliberately excluded.** Building images and editing compose files. That is a
|
|
development tool, not a setting.
|
|
|
|
### 3.3 Gaming
|
|
|
|
You have `gamemoded`, `mangohud`, and a terabyte of Steam. Windows has Game
|
|
Mode; macOS added one.
|
|
|
|
Gamemode status and which process triggered it; the MangoHud overlay and preset;
|
|
how it pairs with the power profile Settings already controls; Proton versions;
|
|
where the library lives.
|
|
|
|
**Watch for.** This is the page most likely to become a dumping ground. It
|
|
should own the handful of things that are actually settings and link out for the
|
|
rest.
|
|
|
|
---
|
|
|
|
## Batch 4 — The developer surface
|
|
|
|
### 4.1 SSH and keys
|
|
|
|
Keys, agent state, known hosts, per-host configuration. Neither macOS nor
|
|
Windows does this well, and this machine has a pile of `ksshaskpass` entries in
|
|
the keyring already.
|
|
|
|
**Rules carried from the keyring work.** A private key is never read, never
|
|
displayed, and never leaves its file. The page manages *which* keys exist and
|
|
what they are for.
|
|
|
|
### 4.2 Settings that follow you
|
|
|
|
`SettingsBackup` already exists. Nextcloud as transport would make Panama
|
|
settings arrive on another machine.
|
|
|
|
**Watch for.** Machine-specific values — monitor layout, device names — must not
|
|
follow. A sync that carries a display arrangement to a laptop with one screen is
|
|
worse than no sync.
|
|
|
|
---
|
|
|
|
## Cross-cutting, and worth more than any single page
|
|
|
|
**Make snapshots ambient.** Now that they exist, anything risky can offer to
|
|
take one first: updates, restoring defaults, firewall changes. This is the
|
|
thread that makes a settings application feel *safe* rather than merely capable,
|
|
and it is cheap everywhere it applies.
|
|
|
|
**Make findings actionable.** Storage reports 86 GB in the trash and 5.31 GB of
|
|
reclaimable images, then leaves you to deal with it. The offer belongs next to
|
|
the finding — carefully, since these are the destructive ones.
|
|
|
|
---
|
|
|
|
## Principles this plan inherits
|
|
|
|
Every one of these was learned the hard way in the work before it.
|
|
|
|
- **No switch that lies.** If the mechanism cannot act — Color's missing
|
|
devices, snapper's overridden rollback, a driver we cannot choose — the page
|
|
says so instead of offering a control that changes nothing.
|
|
- **A write is not done until it is read back.** The failure that made "some
|
|
things in the settings app don't work" true was a refresh that silently no-oped
|
|
after a successful write.
|
|
- **Guards read the object, not a binding over it.** A derived property is stale
|
|
inside the handler that changes it.
|
|
- **A test must be seen to fail.** "Something returned an error" is not evidence
|
|
that your own code refused it; assert the reason.
|
|
- **Check what exists before writing a file.** Two components were overwritten in
|
|
one session by not looking first.
|
|
|
|
---
|
|
|
|
## Order, and why
|
|
|
|
Batch 1 first: it is the biggest gap, it is self-contained, and the snapshot
|
|
pairing makes it distinctive rather than a copy of someone else's panel.
|
|
|
|
Batch 2 second: Services is read-only, which makes it low-risk and high-value,
|
|
and it is the page that makes this feel like *your* operating system.
|
|
|
|
Batch 3 and 4 in either order. Firewall is the most valuable of the remainder
|
|
and also the one that most deserves care.
|
|
|
|
|
|
---
|
|
|
|
## The authentication prompt
|
|
|
|
Not on the original list, and worth recording because of how it was built.
|
|
|
|
hyprpolkitagent's dialog is compiled into its binary -- no config, no
|
|
stylesheet, nothing to theme -- and it was the one window on the desktop that
|
|
looked like it belonged to something else. Panama now provides the agent.
|
|
|
|
**The split is the security design.** A Python process owns the D-Bus side: it
|
|
registers with polkitd, receives the request, and hands the shell the action,
|
|
the message, who may answer, and a one-time cookie. It never sees a password.
|
|
The shell draws the prompt, and on submit spawns the setuid
|
|
`polkit-agent-helper-1` itself and writes the password to that helper's stdin;
|
|
the helper performs the PAM conversation and reports to polkitd directly. So the
|
|
password exists in the shell process and the helper's stdin, and nowhere else --
|
|
never on a command line, never over D-Bus, never through IPC arguments.
|
|
|
|
The request travels as a file created 0600 with O_EXCL in a 0700 runtime
|
|
directory. A cookie is not a password, but it is a capability, and capabilities
|
|
do not belong in a process listing either.
|
|
|
|
**Three things cost real time and are worth writing down.**
|
|
|
|
polkitd calls `BeginAuthentication` back on the same connection that called
|
|
`RegisterAuthenticationAgent`. Exporting the object on the session bus while
|
|
registering from the system bus produced no error at all -- every request just
|
|
failed as "Not authorized" without ever prompting.
|
|
|
|
`XDG_SESSION_ID` is not in the systemd user environment, because a user unit
|
|
runs under `[email protected]`, which belongs to no login session. `GetSessionByPID`
|
|
fails for the same reason. The user object's `Display` property is the answer.
|
|
|
|
PyGObject does not accept the `@` placeholder in variant format strings; a tuple
|
|
has to be assembled from already-built variants.
|
|
|
|
**The fallback is deliberate.** hyprpolkitagent stays installed, and only one
|
|
agent may register per session, so the autostart starts exactly one and the
|
|
comment beside it says how to get the stock prompt back. A session with no
|
|
working agent can still authenticate from a terminal, which is the escape hatch
|
|
that made this safe to attempt at all.
|