From de032e704c2cda46eed7b805c0b0d5922e58180f Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 11:17:34 +0200 Subject: [PATCH 1/9] Research 026 (the graphical session) and 027 (the system layer) Evidence from both workstations and all four machines, read-only, and the questions each must answer: seats and gating for the display stack, who starts the session with which environment, contributions beyond shells, sway as a sibling session; the container runtime with docker-compose on workstations only, software outside the official repositories, secrets in the account's environment, and three security findings. --- .../00-overview.md | 62 ++++++++ .../01-what-the-workstations-run.md | 141 ++++++++++++++++++ .../02-the-questions-and-the-options.md | 123 +++++++++++++++ .../00-overview.md | 53 +++++++ .../01-what-the-machines-run.md | 103 +++++++++++++ .../02-candidates-and-questions.md | 87 +++++++++++ 6 files changed, 569 insertions(+) create mode 100644 01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md create mode 100644 01-RESEARCH/026-the-graphical-session-as-modules/01-what-the-workstations-run.md create mode 100644 01-RESEARCH/026-the-graphical-session-as-modules/02-the-questions-and-the-options.md create mode 100644 01-RESEARCH/027-the-system-layer-as-modules/00-overview.md create mode 100644 01-RESEARCH/027-the-system-layer-as-modules/01-what-the-machines-run.md create mode 100644 01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md new file mode 100644 index 0000000..1790df5 --- /dev/null +++ b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md @@ -0,0 +1,62 @@ +--- +status: active +initiated: 2026-10-04 +touches: + - 02-DECISIONS/0176-the-login-shell-is-a-node-seat-and-execute-is-its-contract.md + - 02-DECISIONS/0177-a-unit-may-be-user-scoped-and-the-service-manager-is-a-node-seat.md + - 02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md + - 02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md + - 02-DECISIONS/0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md + - 02-DECISIONS/0205-software-the-distribution-does-not-package-ships-as-a-pinned-archive-of-the-module.md + - 03-DESIGN/01-to-be/37-the-operators-machine.md + - 03-DESIGN/01-to-be/38-building-the-operators-machine.md + - 04-ISSUES/168-a-setting-reaches-every-file-and-contribution/00-report.md +became: [] +--- + +# 026 — The graphical session as modules + +## What is investigated + +The workstations' graphical session as modules of the mesh, at the same level as the shell +([to-be 41](../../03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md)): a package, files +under the account's home, a seat, and nothing that names a machine. The pieces are: + +- the login manager; +- how a session starts and what environment it gets; +- the display server (X today, Wayland as a sibling); +- the window manager (i3, and sway as its Wayland sibling); +- the terminal emulator (xterm); +- the session's companions: bar, compositor, launcher, notifier, lock and idle, clipboard, + wallpaper, theming, fonts. + +[To-be 38](../../03-DESIGN/01-to-be/38-building-the-operators-machine.md) names this WP7, and says +each seat begins with a record naming its holders and verbs. [To-be 37](../../03-DESIGN/01-to-be/37-the-operators-machine.md) +§4 leaves one question for the resolver: whether a held seat can gate another's assignment. + +## Why + +The operator asked for the graphical modules next, at the shell's level, and for one consistent +experience across machines. Since the predecessor retired, nothing manages the workstations' +desktops. Measured in [01](01-what-the-workstations-run.md): + +- Two workstations carry one 983-line predecessor module's output, still byte-identical in its core. +- One workstation also carries another machine's hardware fragments. +- One runs a session that predates two fixes, with two notification daemons and two portals. +- The session's environment is a hand-kept second copy of the account's, beside the one the mesh + now writes. + +## What it touches + +- **The seat table:** up to ten node seats. +- **The resolver:** a seat held on a node gating another module's assignment. +- **The contribution mechanism of ADR 0204:** whether it generalises beyond shells, or whether + tools' own drop-in directories serve. +- **The host's user-scoped units** (mesh-host #72, still open). +- **Settings** for per-machine values (issue 168). +- **ADR 0205's archive** for the two pieces the distribution does not package. + +## Documents + +- [01 — What the workstations run](01-what-the-workstations-run.md): evidence. +- [02 — The questions and the options](02-the-questions-and-the-options.md) diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/01-what-the-workstations-run.md b/01-RESEARCH/026-the-graphical-session-as-modules/01-what-the-workstations-run.md new file mode 100644 index 0000000..c9317c8 --- /dev/null +++ b/01-RESEARCH/026-the-graphical-session-as-modules/01-what-the-workstations-run.md @@ -0,0 +1,141 @@ +# 01 — What the workstations run + +Measured 2026-10-04 on the two workstations of one installation, read-only: a laptop with a hybrid +GPU and an internal panel, and a desktop with one GPU and two external monitors. Both run the same +predecessor-generated desktop. File equality was checked by checksum across the two machines. + +## How a session starts + +The chain is the same on both: + +1. The login manager (`lemurs`, built from the distribution's user repository, its package now in + the official one) runs its X setup script on a virtual terminal. +2. That script sources the login shell's profile files, then `~/.xprofile`, then the system's + `xinitrc.d` drop-ins, then merges `~/.Xresources`. +3. `~/.xprofile` reuses the systemd user manager's bus, then sources `~/.xinitrc`. +4. `~/.xinitrc` sets up the session and ends with `exec i3`. + +The login manager's own window-manager entry (`exec startx`) is never reached. Its configuration +file uses a format two releases old, and an unmerged newer one sits beside it. + +**What `~/.xinitrc` does**, in order: + +1. Sources the system drop-ins, which import `DISPLAY` and `XAUTHORITY` into the user manager. +2. Starts the keyring and exports its ssh socket. +3. Exports the session's environment: + - `PATH`, with nine entries, one of them a directory that no longer exists; + - toolchain variables; + - `XDG_CONFIG_HOME` and `XDG_DATA_DIRS` (with flatpak); + - five GTK/Qt theme variables; + - the desktop's identity (`XDG_CURRENT_DESKTOP`, `XDG_SESSION_DESKTOP`); + - three of the operator's own variables. +4. Imports an explicit allowlist of ten of those into the user manager and D-Bus activation. It is + never `--all`, because: +5. a predecessor file of **secrets as environment variables** (package-registry and API tokens) is + sourced next. +6. Sets the screensaver and display power timeouts, restores the wallpaper, and starts the lock + watcher in a respawn loop. It is deliberately not a unit, because it needs the login session. +7. `exec i3`. + +**The account's environment, as of today, has three sources that disagree:** + +- this file, for the session; +- the mesh's `environment.sh`, for shells + ([ADR 0203](../../02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md)); +- `~/.config/environment.d/`, for the user manager. It holds the mesh's `50-mesh.conf`, and a + predecessor file that **sets `PATH` outright** and sorts after it. + +## The roles, and what fills them + +| role | software | where configured | +|---|---|---| +| login manager | lemurs | `/etc/lemurs/*` (identical on both, and to the predecessor's source) | +| session start and environment | the login manager's X setup, `~/.xprofile`, `~/.xinitrc`, `xinitrc.d`, the D-Bus import, `environment.d` | `~/.xprofile`, `~/.xinitrc`, `~/.config/environment.d/*` | +| display server | Xorg (`xorg-server`, `xinit`, the X apps; vendor drivers per GPU) | **no** `xorg.conf.d`; monitors by `xrandr` scripts | +| monitor layout | `xrandr` scripts (arandr), a hotplug rule on the laptop | `~/.screenlayout/`, a scripts folder, a window-manager fragment | +| window manager | i3 4.25 | `~/.config/i3/config` and `config.d/*`, a reload watcher (user unit) | +| bar | i3bar with i3status-rust | `~/.config/i3status-rust/*`, 14 themes, a bar watchdog (user unit) | +| terminal | xterm (the only terminal installed) | `~/.Xresources.d/xterm`, the window manager's binding, the compositor's opacity rule | +| compositor | picom | `~/.config/picom/picom.conf` | +| launcher and menus | rofi | `~/.config/rofi/*`, launcher, power-menu and theme-picker scripts | +| notifier | dunst (D-Bus activated) | `~/.config/dunst/dunstrc`, `dunstrc.d/*` | +| lock, idle, display power | xss-lock and i3lock-color, `xset` | `~/.xinitrc`, a lock script | +| clipboard | greenclip, xclip | `greenclip.toml` | +| wallpaper | feh | `~/.fehbg` (points into the predecessor's tree) | +| theming | Adwaita dark, qt5ct/qt6ct, the desktop portal (GTK backend pinned) | GTK `settings.ini`, `qt*ct.conf`, `portals.conf`, an appearance script, `.Xresources` cursor | +| fonts | Hack and Meslo Nerd fonts in `~/.local/share/fonts` (not packaged), noto | `~/.Xresources.d/xft` (DPI fixed at 96) | +| keyboard | nothing set; the default layout; vendor keys via triggerhappy on the laptop | window-manager bindings, `/etc/triggerhappy` | + +**Packages:** every piece except two is in the distribution's official repositories, and the login +manager now is too. The two exceptions are the lock screen's colour build (`i3lock-color`) and the +clipboard manager (`rofi-greenclip`). The Nerd fonts exist as official packages, but both machines +carry hand-copied files instead. + +## Identical, different, and why + +**Byte-identical on both machines:** + +- the session files: `.xinitrc`, `.xprofile`, `.Xresources` and its drop-ins; +- the i3 main configuration and two of its fragments; +- the bar's top configuration and themes; +- picom, rofi, the GTK and Qt settings, the portal configuration, the login manager. + +**Different, by cause:** + +| cause | what | +|---|---| +| hardware | the monitor layout script; the bar's battery block; the laptop's power and vendor-key units and udev rules | +| misassignment | the desktop carries the **laptop's** hardware fragments: the vendor-key daemon and its triggers, the backlight rule, the brightness drop-in, a touchpad reset, and the laptop's monitor layouts, in an older version | +| drift | the notifier's position and corner radius; a "temporary" window-manager fragment from a test; the bar watchdog disabled; a second Qt configuration tool; different font builds | +| a stale session | the desktop's session began before two fixes, so it runs two notification daemons and two portals, and its user manager lacks the desktop's identity | + +**Dead references:** the window manager starts a polkit agent that is installed on neither machine, +so there is no polkit agent at all. `PATH` names a directory that does not exist. + +**Per-machine values inside shared files:** + +- the DPI; +- absolute home paths, in the clipboard configuration and the flatpak data directories; +- the laptop's panel name, inside a fragment both machines carry. + +## User units the desktop needs + +| unit | does | laptop | desktop | +|---|---|---|---| +| reload watcher | reloads the window manager and bar when their files change | on | on | +| bar watchdog | restarts a dead bar | on | off | +| clipboard daemon | from its package | via the window manager | unit **and** window manager | +| vendor power profile, memory guard | laptop power | on | — | + +None is managed. Applying them as the account needs the host's user scope +([ADR 0177](../../02-DECISIONS/0177-a-unit-may-be-user-scoped-and-the-service-manager-is-a-node-seat.md)), +which is still an open change. + +## The predecessor's module + +One manifest of 983 lines covers the window manager, bar, launcher, notifier, compositor, lock +screen, session bootstrap, theming and scripts. It: + +- has four *flavors*: i3, laptop (i3 plus the monitor wizard and hotplug), desktop (i3 plus + nothing) and a laptop model (laptop plus vendor keys); +- has about **105 theme variables** substituted into templates: border, gaps, fonts, workspace + names, every colour of bar, launcher, notifier and lock screen, compositor opacity, cursor, idle + times, Qt and GTK theme names; +- enables the two user units from an install hook. + +Separate modules held the login manager and the display server (one flavor, `xorg`, with a comment +calling `wayland` "the intended sibling"). The shell module held no graphical part. + +## Wayland and sway + +**Nothing exists.** There is no compositor, no sway configuration, no Wayland session entry, and the +login manager's Wayland directory is empty. What is installed is libraries: + +- Wayland itself and the Qt Wayland plugins, which other packages pull in; +- `xwayland`, explicitly installed and required by nothing; +- on the desktop, an orphaned compositor library from another desktop environment, and that + environment's portal backend, pulled in by a game launcher. The portal configuration pins + against it. + +Every piece a sway session needs is in the official repositories: the compositor, its lock screen, +a terminal (`foot`), a bar (`waybar`), a notifier (`mako`) and `xwayland`. diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/02-the-questions-and-the-options.md b/01-RESEARCH/026-the-graphical-session-as-modules/02-the-questions-and-the-options.md new file mode 100644 index 0000000..7600ac6 --- /dev/null +++ b/01-RESEARCH/026-the-graphical-session-as-modules/02-the-questions-and-the-options.md @@ -0,0 +1,123 @@ +# 02 — The questions and the options + +Seven questions. Each has its options and a starting position, which is what this effort tests, not +what it has decided. + +## 1. How finely the desktop splits into modules + +| | option | for | against | +|---|---|---|---| +| G1 | One desktop module, as the predecessor had | one assignment | flavors again, per machine; ADR 0174 refuses them, and the evidence shows a flavor landing on the wrong machine | +| G2 | **One module per piece of software:** `lemurs`, `xorg`, `i3`, `i3status-rust`, `xterm`, `picom`, `rofi`, `dunst`, `xss-lock` with the lock screen, `greenclip`, `feh`, a theme module, a fonts module | each is what it declares; a machine gets exactly what is assigned; the same split already works for the shell and its plugins | about thirteen assignments per workstation | +| G3 | G2, plus a named **set** the controller assigns as one (for example *the X desktop*) | G2's precision with G1's convenience | a set is a new controller concept | + +**Starting position: G2.** Whether a set is worth a record is left until the thirteen assignments +have been done by hand once. + +## 2. The seats + +Research 018 listed the candidates. ADR 0204 has since put the login shell in the mesh's own set, +because a role with a protocol should not depend on one module's registration. The same reasoning +applies here: + +| seat | holders | protocol, first verbs | +|---|---|---| +| `node-login-manager` | lemurs, greetd | which sessions it offers, the default session | +| `node-display-server` | xorg, sway | `displays`, `layout` | +| `node-display-session` | i3, sway | `reload`, `workspaces`, `windows` | +| `node-terminal-emulator` | xterm, foot, alacritty | which terminal `$TERMINAL` names; `open` | +| `node-bar`, `node-compositor`, `node-launcher`, `node-notifier`, `node-lock-screen`, `node-clipboard` | the pieces above, and their Wayland counterparts | one verb or none each, until a use asks for one | + +**A compositor that is its own server holds two seats.** Sway is both the display server and the +display session. A module may claim several seats, so this needs nothing new. + +**Starting position:** the first four seats are in the mesh's own set. The companion seats are +added only as each holder is written; for those, a module without a seat is acceptable at first. + +## 3. One module requiring another seat to be held + +i3 needs an X server held on its node, and sway needs nothing below it. A terminal needs a session. +To-be 37 left open how that is said. + +| | option | for | against | +|---|---|---|---| +| R1 | A seat **delivers a provision** (`x11-display`, `wayland-display`) and a module requires it at node scope. The seat table has a `delivers` field already, and requirements already resolve | existing machinery; the refusal names the seat and its possible holders, which design 27 already lists | a node-scoped requirement that never crosses machines has to be stated as such | +| R2 | A new field, *needs the seat X held* | reads plainly | a second way to say what R1 says | +| R3 | Nothing; assign carefully | — | the mistake the evidence shows (a laptop's fragments on a desktop) is exactly an unchecked assignment | + +**Starting position: R1.** `xorg` and `sway` each deliver what they serve. `i3`, `picom` and `xss-lock` +require `x11-display`. `foot` requires a Wayland display, and xterm requires an X one, which a Wayland +session gives through `xwayland`. + +## 4. Who starts the session, and with what environment + +Today `~/.xinitrc` is a hand-kept second environment and the session's whole start script. + +| | option | for | against | +|---|---|---|---| +| S1 | The display server's module writes `~/.xinitrc` **into**: a mesh block at the start that sources the account's environment (`environment.sh`), merges the X resources, and runs the session's contributed start lines. The session holder's module contributes its `exec` line. The operator's lines stay after the block | one environment for shells, the session and the user manager; nothing to keep in step | the order inside `.xinitrc` becomes the slot order of a contribution (question 5) | +| S2 | The login manager's module owns the session script under `/etc` | system scope; no home file | the environment is the account's, and the script is the same for every account | +| S3 | Leave `.xinitrc` the operator's | nothing to build | the third environment stays | + +**Starting position: S1.** + +- The desktop's identity (`XDG_CURRENT_DESKTOP`) and the theme variables become **environment + contributions** (ADR 0203) from `i3` and from the theme module. They then also reach the user + manager through `environment.d`, which replaces most of today's allowlist import. +- The secrets file stays out of the environment until research 027 settles how a secret reaches an + account. + +## 5. How other modules contribute to a holder's file + +The terminal's settings are X resources. A bar, a launcher binding and a hardware module's key +bindings are window-manager configuration. Autostarts are the session's. ADR 0204 built slot +contributions for shells only. + +| | option | for | against | +|---|---|---|---| +| C1 | **The tool's own drop-in directory**, where it has one: i3's `include`, dunst's `dunstrc.d`, X resources' `#include`, XDG autostart entries, `environment.d`. Each contributor owns its own file there | no mesh change; the tools already read these directories; unassigning removes the file | each contributor names a path in another tool's directory (ADR 0204 rejected this for shells, where no drop-in convention exists); ordering is by file name | +| C2 | **ADR 0204's mechanism generalised:** `contributes` text *for a format* (`zsh`, `xresources`, `i3`, `xinitrc`) in a slot, placed by the holder's placeholder | one mechanism, checked by the controller, order declared | every format must be named in the controller; a bigger change to ADR 0204 | +| C3 | C1 where the tool has a drop-in convention, C2 where it does not (`.xinitrc`, `.Xresources` order) | uses each tool's own grain | two mechanisms to learn | + +**Starting position: C3**, with the boundary drawn by the tools. A tool that reads a directory gets +drop-ins. A file without one gets slots. This means amending ADR 0204's "shell" to "a format", which +is a progressive extension rather than a reversal. + +## 6. What varies per machine + +| what | today | option | +|---|---|---| +| monitor layout | per-machine `xrandr` scripts, monitor names baked in | a **setting** of `xorg` (issue 168), and a `layout` verb of the display server seat | +| DPI, fonts' size | fixed in an X resource | a setting | +| battery block, vendor keys, brightness, touchpad | a laptop model's flavor | **a hardware module** per machine model, contributing its window-manager fragment, bar block and udev rules. The desktop simply is not assigned it | +| theme (the 105 variables) | template substitution | settings of each tool's module, after issue 168 closes (ADR 0174). Until then each module carries today's values as its default | + +**Starting position:** + +- Hardware modules for what follows the machine. +- Defaults now, settings after issue 168, for what the operator varies. +- The monitor layout waits for settings. Until then it is an operator-owned script the display + server's block calls if present. + +## 7. Wayland and sway + +Nothing of a Wayland session exists, and every piece is officially packaged. "Wayland" is a protocol, +not a piece of software, so it has no module of its own. Its parts are `sway` (server and session), +`swaylock`, `foot`, `waybar`, `mako`, and `xwayland` for X clients. + +**Starting position:** + +- The seats and the requirements (questions 2 and 3) are designed so that sway fits from the first + day. +- The X stack is built first, because it is what runs. +- `sway` and its companions are written after that, and proven on one workstation as a second + session the login manager offers beside i3. That lets the operator try it without losing the + working desktop. + +## Prerequisites this effort cannot remove + +- **User-scoped units** (mesh-host #72) for the reload watcher and the bar watchdog. +- **Settings** (issue 168) for monitors and theme values. +- **The two packages not in the official repositories:** the lock screen's colour build and the + clipboard manager. Each is ADR 0205's case, a pinned archive, or a choice of an official + alternative (`i3lock` without colours; `clipmenu`/`cliphist`). diff --git a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md new file mode 100644 index 0000000..60742e2 --- /dev/null +++ b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md @@ -0,0 +1,53 @@ +--- +status: active +initiated: 2026-10-04 +touches: + - 02-DECISIONS/0165-container-runtime-is-what-a-machine-can-run-and-a-running-runtime-is-its-holders-health.md + - 02-DECISIONS/0166-the-container-runtime-is-a-node-seat-and-the-host-creates-containers-through-its-holder.md + - 02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md + - 02-DECISIONS/0205-software-the-distribution-does-not-package-ships-as-a-pinned-archive-of-the-module.md + - 03-DESIGN/01-to-be/37-the-operators-machine.md + - 03-DESIGN/01-to-be/38-building-the-operators-machine.md +became: [] +--- + +# 027 — The system layer as modules + +## What is investigated + +What runs on the machines below the operator's home and outside the mesh's own services, and which +of it should be modules. That covers: + +- the container runtime and its tools; +- privilege (sudo); +- the package manager and the software it cannot install; +- time, locale, the kernel and boot; +- log rotation; +- the machine-specific daemons the workstations and servers carry: printing, bluetooth, VPN + clients, virtualisation, storage, sharing. + +## Why + +The operator asked for the system level beside the graphical session. In particular: + +- a `docker` module (decided in principle by the proposed ADRs 0165 and 0166, never built); +- a `docker-compose` module for development work, assigned **only to the two workstations**. + +Measured in [01](01-what-the-machines-run.md): on four machines, almost nothing at this level is +owned by a module. The pieces differ by machine for no recorded reason. Three findings are security +matters on their own. + +## What it touches + +- **The container runtime seat** (ADRs 0165 and 0166, both proposed). +- **The host's `package` shape**, which installs from the distribution's official repositories only, + while the workstations carry 67 and 114 packages from elsewhere. +- **How a secret reaches the account's environment.** ADR 0203 forbids it in the contributed + environment, but a predecessor file supplies such secrets today. +- **The facts the mesh assumes and never declares,** above all that the operator account escalates + without a prompt. + +## Documents + +- [01 — What the machines run](01-what-the-machines-run.md): evidence. +- [02 — Candidates and questions](02-candidates-and-questions.md) diff --git a/01-RESEARCH/027-the-system-layer-as-modules/01-what-the-machines-run.md b/01-RESEARCH/027-the-system-layer-as-modules/01-what-the-machines-run.md new file mode 100644 index 0000000..0810cec --- /dev/null +++ b/01-RESEARCH/027-the-system-layer-as-modules/01-what-the-machines-run.md @@ -0,0 +1,103 @@ +# 01 — What the machines run + +Measured 2026-10-04 on four machines, read-only, including the host's own record of what it applied: +two servers (the anchor and a home server) and two workstations (a laptop and a desktop). "Owned" +means a module the mesh assigns declares it. + +## The container runtime + +| | anchor | home server | laptop | desktop | +|---|---|---|---|---| +| docker | 29.8.2 | 29.8.2 | 29.7.2 | 29.7.2 | +| compose | 5.5.1 | 5.6.0 | 5.5.0 | 5.5.0 | +| buildx | 0.37.2 | — | — | — | +| podman | — | 6.1.3 | 6.1.0 | 6.1.0 | +| `docker.socket` | disabled | enabled | enabled | enabled | +| `containerd.service` | disabled | disabled | disabled | **enabled** | +| `daemon.json` beyond the shared keys | direct routing, two more insecure registries | log rotation (100 MB × 10) | — | — | +| docker group | operator, **a CI user** | operator | operator | operator | + +**Ownership:** + +- The `docker` package is owned on one machine only, by the installer's bootstrap, not by a module. +- `docker.service` is declared indirectly, by the name resolver and the private-network modules, + which each merge their own keys into `daemon.json`. +- Nothing owns the socket, containerd, compose, buildx or the group. + +**Compose in use:** + +- On the servers, no running container belongs to a compose project. Their compose files are + pre-mesh trees under the operator's and root's homes, plus a dangling enabled unit for one of them. +- On the workstations, compose runs development stacks, and pre-mesh service trees sit under a + top-level directory. + +The mesh marks its own containers with a host label. On the workstations, a handful of unlabelled +development and test containers run beside its build agent. + +## Privilege + +- The operator account escalates **without a prompt on all four machines**. The mesh relies on this, + but it is set by hand in `/etc/sudoers` (a `wheel` rule on two machines, the account named on + two), and nothing declares it. +- On the anchor, a **CI user from the predecessor** keeps passwordless sudo and docker membership, + and a predecessor drop-in in `sudoers.d` survives. +- On the desktop, the operator account is also in the **`root` group**. + +## The package manager + +- `pacman.conf` is stock except on one server (parallel downloads). +- The mirror list was generated once by a tool that is no longer installed. On the anchor, it is the + hosting provider's single mirror. +- An AUR helper is installed everywhere. +- **Packages from outside the official repositories:** 2 on the anchor, 21 on the home server, + 67 on the laptop, 114 on the desktop. They include: + - the agent CLI, which a catalogue module declares as a package and the host cannot install; + - a VPN client; + - a remote-access client; + - printer drivers; + - GPU tools; + - a kernel module built from source (DKMS) for a storage filesystem; + - a snap daemon. + +## Time, locale, kernel, boot + +| | anchor | home server | laptop | desktop | +|---|---|---|---|---| +| time zone, keymap | **another zone**, a non-US console keymap | local zone, unset | local zone, unset | local zone, unset | +| time sync | timesyncd plus a provider drop-in | timesyncd | timesyncd | **ntpd**, timesyncd disabled | +| bootloader | grub (BIOS) | systemd-boot **and** grub | systemd-boot | systemd-boot **and** grub | +| kernels | one | two, plus a DKMS filesystem module | one | one, plus a DKMS controller driver | +| microcode | **none** | yes | yes | **none** | +| swap | RAID partition | partition | zram, a file and a partition | partition | +| log rotation timer | not found | enabled | not found | not found | + +## Daemons and services no module owns + +- **All four:** avahi. +- **Workstations:** + - a VPN client daemon (both); + - virtualisation (incus) with a hand-made unit that inserts container-runtime firewall rules (both); + - printing and bluetooth; + - GPU and power tuning per model; + - a remote-access daemon (laptop); + - snap and flatpak (desktop); + - the local model server, run from a hand-written unit although a catalogue module for it exists + (desktop); + - Samba sharing and a network filesystem mount from the home server (desktop). A second mount is + failing, and its **credential is written in clear in `/etc/fstab`**. +- **Servers:** + - a storage pool (about 167 TB) with its import, mount and scrub units, an NFS server and Samba + sharing (home server); + - traffic and sensor monitoring (home server); + - a DHCP client daemon the catalogue has a module for but does not assign there (home server); + - cron, an entropy daemon, and the **legacy `iptables` services**, which run beside the mesh's own + filter (anchor). +- **Not found anywhere:** a backup agent, a monitoring agent, a second VPN mesh. + +## What is plain debris + +- Dangling enabled-unit links on three machines. +- Predecessor blocks in `/etc/hosts` on both servers. +- The CI user, and the predecessor sudoers drop-in, on the anchor. +- Pre-mesh compose trees on the anchor, the home server and the desktop. +- Unlabelled test containers on the workstations. diff --git a/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md b/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md new file mode 100644 index 0000000..3ea28d6 --- /dev/null +++ b/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md @@ -0,0 +1,87 @@ +# 02 — Candidates and questions + +## Decided by the operator on 2026-10-04 + +- **`docker`** holds the container runtime seat on every machine, as ADRs 0165 and 0166 propose. Those + records are promoted from proposed when it is built. +- **`docker-compose` is a module of its own,** the distribution's package and nothing else. It is + assigned **only to the two workstations**, for development work. The servers run nothing through + compose. + +## Candidate modules + +**On every machine:** + +| module | owns | first reason | +|---|---|---| +| `docker` | the packages (runtime, containerd), the service and socket, `daemon.json`'s base keys (live restore, log rotation), the docker group's members | four machines, four configurations, one owner on one | +| `sudo` | the operator account's escalation as a drop-in, declared | the mesh's tools rely on it (to-be 38 WP4) and nothing states it | +| `pacman` | `pacman.conf`'s few keys, the mirror list and its refresher, cache cleaning | mirrors generated once and never again | +| `time-sync` | timesyncd and its drop-ins | two daemons across four machines | +| `locale` | locale, time zone, console keymap | one machine differs, with no record why | +| `kernel` | the kernel packages, microcode, initramfs presets | two machines without microcode | +| `logrotate` | the timer and the base configuration | rotation runs on one machine of four | +| `avahi` | the daemon and name-service switch entry | on all four, owned by none | + +**On the workstations only:** + +- `docker-compose`; +- `lemurs`, the login manager (research 026); +- a VPN client module; +- `incus` with its forward unit (the lab module declares the package on one workstation only); +- `cups` with the printer's driver; +- `bluetooth`; +- per-model **hardware** modules: GPU, power, vendor keys, brightness. These are the same modules + research 026 needs for the desktop's fragments. + +**On the servers only:** + +- `zfs` with its scrub timer, and the long-term kernel it builds against; +- `nfs-server`; +- `samba`; +- `vnstat`, `lm_sensors`. + +`cron` on the anchor serves one stock file and can go. So can the entropy daemon on a modern +kernel. + +**Retire, not model:** the legacy `iptables` services on the anchor. They duplicate the mesh's +filter, which is ADR 0100's ground. + +## Questions this effort has to answer + +1. **Software outside the official repositories.** The host's `package` shape installs from the + official repositories only. A catalogue module already declares an AUR package (the agent CLI), + which no machine could install, and the workstations carry 181 such packages between them. + + | | option | for | against | + |---|---|---|---| + | P1 | ADR 0205's pinned vendored archive, per piece | exists | wrong for packages that build native code or kernel modules | + | P2 | **The build machine builds AUR packages into a package repository the mesh serves** from its artifact store. The host then installs them as packages, signed | one shape for every package; pinned, reviewed and built once | a repository to serve and a signing key to keep | + | P3 | An AUR helper on each machine, driven by the host | nothing to serve | builds on every machine, unpinned: the predecessor's `git clone` in another form | + + Starting position: P2, for anything with native code. ADR 0205 stays for plain files such as a + theme. + +2. **Secrets in the account's environment.** A predecessor file feeds package-registry and API tokens + to the session. ADR 0203 refuses secrets in contributed values, because they travel in the clear. + The candidate is ADR 0182's third class: a module's own process writes a mode-0600 file of + exports, from secrets the vault hands it over the bus, and the shell and the session source it. + This needs its own record. + +3. **Per-machine sizing and drivers.** The swap layout, the GPU, the storage pool and the boot + loader are facts of one machine's hardware. They belong in hardware modules, or in settings + (issue 168), not in the shared ones. + +4. **What a module may leave behind.** Compose is installed on both servers, unowned. The mesh + removes nothing it did not make. The choice is between an operator's one-off removal and a + server-side `absent` declaration. + +## Security findings, independent of any module + +1. A filesystem credential in clear text in a workstation's `/etc/fstab`, for a mount that is failing + anyway. +2. A predecessor CI user with passwordless sudo and docker membership on the anchor, and a + predecessor sudoers drop-in. +3. The operator account in the `root` group on one workstation. + +Each is one small change. None waits for a module. From 61e70b93951e8f3e80057f354afa3eef89a4026c Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 11:21:35 +0200 Subject: [PATCH 2/9] Research 027: the operator's choices, the hosts file, mounts, and two DHCP clients on one interface --- .../02-candidates-and-questions.md | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md b/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md index 3ea28d6..b6be40b 100644 --- a/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md +++ b/01-RESEARCH/027-the-system-layer-as-modules/02-candidates-and-questions.md @@ -8,6 +8,19 @@ assigned **only to the two workstations**, for development work. The servers run nothing through compose. +Later the same day, on the candidates below: + +- **Yes, all of them:** `docker`, `docker-compose`, `sudo`, `pacman`, an AUR helper (question 1), + `time-sync`, `kernel` (with boot and microcode), `logrotate`, `avahi`, `cups` with the printer's + driver, and every server-only candidate. +- **Locale, time zone and keymap are one module, `localization`.** +- **`snapd` and `flatpak`** are modules, on the two workstations only. +- **`incus` is the lab's,** whose module depends on it. It is not a module of its own beside the lab. +- **The agent's and the local model server's modules are still being developed,** and are not + assigned until they are. +- **The predecessor's CI user is retired.** It was removed from the anchor the same day, with its + sudoers line, its docker membership and a dangling unit link; the backup is on the machine. + ## Candidate modules **On every machine:** @@ -18,7 +31,7 @@ | `sudo` | the operator account's escalation as a drop-in, declared | the mesh's tools rely on it (to-be 38 WP4) and nothing states it | | `pacman` | `pacman.conf`'s few keys, the mirror list and its refresher, cache cleaning | mirrors generated once and never again | | `time-sync` | timesyncd and its drop-ins | two daemons across four machines | -| `locale` | locale, time zone, console keymap | one machine differs, with no record why | +| `localization` | locale, time zone, console keymap (one module, the operator's choice) | one machine differs, with no record why | | `kernel` | the kernel packages, microcode, initramfs presets | two machines without microcode | | `logrotate` | the timer and the base configuration | rotation runs on one machine of four | | `avahi` | the daemon and name-service switch entry | on all four, owned by none | @@ -76,12 +89,40 @@ filter, which is ADR 0100's ground. removes nothing it did not make. The choice is between an operator's one-off removal and a server-side `absent` declaration. +5. **The hosts file.** ADR 0199 (decided on an open change, not yet merged) + gives `/etc/hosts` to one module through a seat, `node-hosts-file`, with an operator region and + three verbs. It is not built. Today the private network's foundation writes only its own block, and + the rest of each file is a predecessor's stale blocks (both servers) or the operator's development + names (both workstations). The candidate module is that seat's first holder. It takes the + private-network block as a contribution, and its operator region replaces the hand-kept lines. + +6. **Mounts.** The host has no shape for a filesystem mount; ADR 0091 is about what a container + mounts. One workstation mounts a share of the home server over NFS, and a second share over SMB. + That second one fails, and its credential sits in clear in `/etc/fstab`. + + | | option | for | against | + |---|---|---|---| + | M1 | **A module owns `/etc/fstab`** and other modules contribute lines | one file, as people know it | the file also carries the root and boot filesystems the installer wrote, which no module should rewrite; a slot contribution into a file that can stop a machine booting | + | M2 | **Each client module writes its own systemd mount (and automount) unit**, which is the service manager's drop-in for exactly this. The `nfs-client` or `smb-client` module declares the unit file and the service shape enables it. `/etc/fstab` stays the machine's | no new host shape, and no shared file; the unit names its own dependencies (network online, the private network) and an automount does not hang a boot when the server is away; unassigning removes the mount | a mount reads as a unit, not a line | + | M3 | A new `mount` shape in the host | the host knows what a mount is | a second way to say what M2 says | + + Starting position: **M2.** The credential an SMB mount needs is a secret, written by the module's + own process from the vault, mode 0600, which is question 2's mechanism. The pair is a server + module exporting (`nfs-server`, `samba`) and a client module mounting. The client requires the + share the server provides, so the mount is resolved, not hand-typed. + +7. **Two DHCP clients on one interface.** The home server runs `dhcpcd`, a DHCP *client* (no machine + runs a DHCP server), next to the network manager, which is its assigned networking module. Both + lease an address on the same interface, which therefore carries two LAN addresses. The catalogue's + `dhcpcd` module is assigned nowhere, and this unit is a leftover. The network manager is the + machine's one DHCP client, and `dhcpcd` should be disabled there. + ## Security findings, independent of any module 1. A filesystem credential in clear text in a workstation's `/etc/fstab`, for a mount that is failing anyway. 2. A predecessor CI user with passwordless sudo and docker membership on the anchor, and a - predecessor sudoers drop-in. + predecessor sudoers drop-in. *The user was removed on 2026-10-04; the drop-in remains.* 3. The operator account in the `root` group on one workstation. Each is one small change. None waits for a module. From 8712d666bf14fddf3fc7c4a64bb8b5c79fbed403 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 11:49:19 +0200 Subject: [PATCH 3/9] Research 026/03: what the predecessor taught; issue 231: a misspelled placeholder is written out as text --- .../00-overview.md | 1 + .../03-what-the-predecessor-taught.md | 51 +++++++++++++++++++ .../00-overview.md | 1 + .../00-report.md | 41 +++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 01-RESEARCH/026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md create mode 100644 04-ISSUES/231-a-misspelled-placeholder-is-written-out-as-text/00-report.md diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md index 1790df5..410fc0f 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md @@ -60,3 +60,4 @@ desktops. Measured in [01](01-what-the-workstations-run.md): - [01 — What the workstations run](01-what-the-workstations-run.md): evidence. - [02 — The questions and the options](02-the-questions-and-the-options.md) +- [03 — What the predecessor taught](03-what-the-predecessor-taught.md): its 128 modules and 3,395 commits, as patterns to keep and failures not to repeat; shared with research 027. diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md b/01-RESEARCH/026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md new file mode 100644 index 0000000..d4e41be --- /dev/null +++ b/01-RESEARCH/026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md @@ -0,0 +1,51 @@ +# 03 — What the predecessor taught + +A study on 2026-10-04 of the retired predecessor: + +- its 128 module manifests, their hooks, its installer and its sync engine; +- 3,395 commits of history; +- what it left on four machines. + +This document holds what bears on the graphical session and on the system layer +([research 027](../027-the-system-layer-as-modules/00-overview.md)). The evidence is in the +predecessor's history. A commit is cited here by what it fixed, not by its hash, because the +repository is private. + +## Keep: what worked + +| pattern | where it shows | in the mesh | +|---|---|---| +| ownership marked inside the file: inside the markers is reconciled, outside is kept verbatim | a block marker in a shared file, after an engine that rewrote whole files | kept regions (ADR 0174, ADR 0204) | +| two writers get two files and an `include`, the include first | the ssh client's configuration, after two writers fought over one file | ADR 0203's two files; research 026 C1 | +| one writer per file, one authority per action | only the reload watcher restarts the window manager, after three mechanisms each did | ADR 0182 | +| refuse to write when the source of truth is unreadable; never empty a block because a query found nothing | a block of names was emptied by a failed query | — keep | +| an unresolved template variable fails the install | a literal unfilled path was installed green | [issue 231](../../04-ISSUES/231-a-misspelled-placeholder-is-written-out-as-text/00-report.md): the mesh still has this gap | +| prune only what you can prove you placed | stale files from earlier deliveries | ADR 0189 | +| ensuring never rotates a credential | a silent rotation caused a retry storm, a ban of the shared address and a lost registry | ADR 0114 | +| vendor only the files you use; never clone and link | three files instead of 77 MB | ADR 0205 | +| copy, never symlink | a recursive delete followed a link, and every reinstall failed | ADR 0012 | +| verification says what it did not check | a verifier said *clean* while the secret was still on disk | — keep | +| alert once per condition | 411 alerts hid a 28-hour outage | ADR 0090 | + +## Do not repeat + +| failure | what it did | the mesh instead | where the mesh is still exposed | +|---|---|---|---| +| **Flavors** | variant files and packages per machine type: a gate dropped, the first-seen variant won, packages never installed, the verifier ignored the gate. On the day of the study a desktop carried a laptop model's fragments | one module per piece, assignment per machine (ADR 0174, research 026 §1) | a setting that switches which whole file is rendered is a flavor under another name | +| **The freeze** | existing values outranked new defaults; templated files were rendered once (*copy if absent*) | files are generated (ADR 0011) | a created-once file (ADR 0087) is a deliberate freeze, and a push must say *kept* | +| **Adopting drift** | a *merge* strategy made a local edit the record forever; switching strategies clobbered a person's model choice | nothing is read back (ADR 0174) | an edit outside a kept region is overwritten **silently**. The predecessor's *why is this back* loop: the push should name what it overwrote | +| **Environment templating** | `${VAR}` matched any name; unresolved names stayed literal; comments and destination paths were interpolated | namespaced placeholders; `$` forbidden in contributed values (ADR 0203) | issue 231 | +| **Hooks with privilege** | install hooks ran `sudo`, `chsh`, `systemctl`, `git clone` and `curl`, and swallowed failures into a warning | the `user` shape, the service shape, archives (ADR 0176, 0177, 0205) | the agent module writes under `/etc` from its own tool through `sudo` (no keep-original, no give-back); the prompt's helper downloads itself unpinned; that the operator escalates without a prompt is assumed by three modules and declared by none (research 027) | +| **Secrets in environment files** | `.env` files left world-readable; the decryption key beside what it decrypts; a deleted secret stayed in the file, so rotation was a no-op | the vault (ADR 0113, 0114) | a predecessor file of secrets is still sourced into the graphical session on two machines (research 027 Q2) | +| **Symlinks into a home** | a system file linked into a person's home | ADR 0012 | on the control machine, a fail2ban action file is still a predecessor link into its home tree. Deleting that tree would silently break the repeat-offender jail. The mesh's fail2ban module must own it as a file first | +| **Green while broken** | a recorded version frozen for four months; a verifier passing what it skipped | ADR 0134, 0145, 0184 | issue 230: a plan waiting for ever reads as healthy | + +## What it means here + +- **Research 026:** the desktop's 88 flavor-gated files and 92 theme variables are the flavor and + templating failures in one module. Question 1 (one module per piece) and question 6 (hardware + modules, settings later) are the answer, and nothing in the new modules may switch whole files on a + setting. +- **Research 027:** the hooks that installed the AUR helper, enabled the login manager and changed + shells are what the `package`, `service` and `user` shapes replace. Every remaining `sudo` in a module's + own code is a debt to be named, starting with the agent module. diff --git a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md index 60742e2..aa64f27 100644 --- a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md +++ b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md @@ -51,3 +51,4 @@ matters on their own. - [01 — What the machines run](01-what-the-machines-run.md): evidence. - [02 — Candidates and questions](02-candidates-and-questions.md) +- The predecessor's lessons, shared with research 026: [026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md) diff --git a/04-ISSUES/231-a-misspelled-placeholder-is-written-out-as-text/00-report.md b/04-ISSUES/231-a-misspelled-placeholder-is-written-out-as-text/00-report.md new file mode 100644 index 0000000..45181bd --- /dev/null +++ b/04-ISSUES/231-a-misspelled-placeholder-is-written-out-as-text/00-report.md @@ -0,0 +1,41 @@ +--- +status: open +opened: 2026-10-04 +located-in: + - mesh-controller +fixed-by: +amended-design: +--- + +# 231 — A misspelled placeholder is written out as text + +## What was observed + +2026-10-04, while studying the predecessor's failures. A manifest was checked whose one file holds four +placeholders: `${shel:zsh:first}` (a misspelled namespace), `${setting:Undeclared}` (a setting the module +does not declare), `${machnie:address}` (a misspelled namespace) and `${XDG_CACHE_HOME:-x}` (shell +syntax, which must pass through). The catalogue check, which runs the same functions registration does, +answered `ok`. + +The controller fills each namespace it knows with its own pattern (`machine`, `setting`, `bound`, `dir`, +`port`, `secret`, `environment`, `shell`, …). A word in that shape that no pass consumes is left in the +file as it was written. A misspelling therefore reaches a machine as literal text, in a configuration +file that then reads it as a value. + +## Why it matters beyond this instance + +This is exactly the predecessor's failure: an unresolved template variable in a destination path +installed green, and a literal `${...}` path stood under `/etc/ssl` until somebody looked. The mesh's +namespaced placeholders were meant to end it ([ADR 0164](../../02-DECISIONS/0164-a-setting-is-declared-with-its-default-its-meaning-and-what-changing-it-costs.md)), +and they do for every name spelled right. + +## What a fix has to settle + +After every pass, a final sweep refuses any remaining `${:` whose word is a lower-case +namespace-shaped token, naming the module, the field and the token, at the catalogue check and at +composition. Shell syntax (`${NAME:-…}`, `${(%):-…}`, `${1:-.}`) is not namespace-shaped and passes. So +does contributed shell code, which no pass reads (ADR 0204). A setting a module uses but does not +declare is refused the same way. + +How it is checked: the controller's test with the four placeholders above, three refused by name and +one passed through. From 27b2d3044175695ea8f881abb0149b31069f5a5a Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 11:59:23 +0200 Subject: [PATCH 4/9] Research 027/03: ~/.ssh as one module's, scripts on every machine, the keyring, mail as events --- .../00-overview.md | 1 + .../03-the-accounts-own-tools.md | 128 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md diff --git a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md index aa64f27..024df41 100644 --- a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md +++ b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md @@ -51,4 +51,5 @@ matters on their own. - [01 — What the machines run](01-what-the-machines-run.md): evidence. - [02 — Candidates and questions](02-candidates-and-questions.md) +- [03 — The account's own tools](03-the-accounts-own-tools.md): `~/.ssh` as one module's, scripts on every machine, the keyring, mail as events - The predecessor's lessons, shared with research 026: [026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md) diff --git a/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md b/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md new file mode 100644 index 0000000..69f0f4d --- /dev/null +++ b/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md @@ -0,0 +1,128 @@ +# 03 — The account's own tools: ssh, scripts, mail + +Three further directions from the operator on 2026-10-04. Each is account-level, like the shell +([to-be 41](../../03-DESIGN/01-to-be/41-the-shell-and-the-accounts-environment.md)). + +## `~/.ssh` is one module's + +*"A module owns `~/.ssh`, so it is its responsibility that every folder is set up consistently and +correctly."* + +**Measured:** + +- The catalogue's `ssh-client` module owns the directory (mode 0700) and one region of + `~/.ssh/config`: a `Host` block per machine of the mesh. It owns nothing else. +- On one workstation, a predecessor's header, `Include` and hand-written host block sat **above** the + mesh's region. ssh takes the first match, so the predecessor's entries were the ones in force, for + the same machines. Removed on 2026-10-04. +- On the control machine, two keys of a retired CI system were still in the operator's + `authorized_keys`, able to log in as the operator. Removed the same day. +- Permissions differ by file and by machine. Backups of the configuration lie beside it. + +**Starting position:** `ssh-client` becomes the holder of everything under `~/.ssh`, classified as +ADR 0182 asks: + +| path | class | how | +|---|---|---| +| `~/.ssh/`, its mode, every file's mode | owned | the directory resource, plus a check verb that reports a file with the wrong mode | +| `~/.ssh/config` | written into, the mesh's block **at the start** | the mesh's hosts win; the operator's lines after it are kept; an `Include config.d/*` line in the block | +| `~/.ssh/config.d/` | owned by the contributing module | ssh's own drop-in: a work module adds its forge's host there (research 026 C1) | +| `~/.ssh/authorized_keys` | written into, the mesh's block | the operator's keys as the mesh records them, and nothing a retired system left. The operator's own lines are kept below the block | +| `~/.ssh/known_hosts` | written into, the mesh's block | every mesh machine's host key, so the first connection never asks | +| private keys | found | never read and never written by the mesh; a key the mesh should hand out comes from the vault, through the module's own process (ADR 0182, third class) | + +The sshd module is the other half: the machine's side. It is already in the catalogue. + +## Scripts on every machine, shared and machine-specific + +*"All nodes should get some custom scripts, both node-specific and mesh-specific (shared)."* + +**Measured:** the operator's script folder holds 64 entries plus 33 in its `bin/`. It is under no +version control, and exists only where it was copied. It mixes three kinds: + +1. scripts belonging to a module (the desktop's watchers, lock, menus; a laptop model's brightness); +2. the operator's own tools; +3. installers that modules have replaced. + +**Starting position:** + +- **The operator's scripts live in a repository of their own,** registered as any application is + ([ADR 0015](../../02-DECISIONS/0015-applications-live-in-their-own-repository.md)), built as archives, + unpacked into a directory the module owns under the home. `bin/` goes on `PATH` through an + environment contribution ([ADR 0203](../../02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md)), + and small functions go into the shell through a `shell` contribution + ([ADR 0204](../../02-DECISIONS/0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md)). +- **"Machine-specific" is said by assignment, never by naming a machine** + ([ADR 0112](../../02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md)). One repository + holds several modules: + - `scripts` (shared, on every machine); + - `scripts-workstation`; + - `scripts-media`; + - and so on, each assigned where it applies. + + A script that belongs to a piece of software or hardware moves into that module instead. A flavor + inside one module is what [research 026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md) + says not to repeat. +- **A script can also be a tool.** A script with a one-line description is served by the node's + runtime, so it can be called through the mesh on any machine that has it. +- A script that needs a secret gets it through question 2's mechanism, never from a file of + environment secrets. + +## The keyring + +*"A keyring is also a good thing to create a module for."* + +**Measured on the two workstations, which both run GNOME Keyring:** + +- **On one, the keyring unlocks at login.** The login manager's PAM service includes `login`, which + carries `pam_gnome_keyring`. +- **On the other, it does not.** The PAM line is only in the screensaver's service, so at session + start the window manager runs a script that asks for the password a second time and unlocks the + keyring with it. +- **On both, the session's start script starts the daemon again** with the ssh and gpg components, + and exports the ssh agent's socket. The keyring's current release serves the ssh agent through a + separate per-user socket unit instead. + +**Starting position:** a `gnome-keyring` module that holds a node seat, `node-secret-service` (the +holder of the desktop's secret service; a password manager could hold it instead). It declares: + +- the package; +- its lines in the login manager's PAM file, written into, never over (ADR 0102), so login unlocks it + on every machine; +- the ssh agent's user socket, once user-scoped units ship; +- the agent's socket path as an environment contribution, which needs a machine fact for the + account's runtime directory. ADR 0203 forbids `$` in values, so `$XDG_RUNTIME_DIR` cannot be + written in one. + +The second unlock prompt and the second daemon start go away. + +## Mail as events + +*"Ideally a mail consumer with all my mail accounts configured, so my mail is recorded in the bus."* + +**Measured:** + +- The predecessor polled one work mailbox every minute. It **read an access token out of the mail + client's process memory**, called a mail API with it, and raised a desktop notification per unread + message. It worked only while the mail client ran, and stopped silently when the predecessor's units + were retired. +- Two further predecessor modules served mail tools, for one provider and for IMAP. +- The mesh runs a mail server of its own for its domains. + +**Not decided here; it needs an effort of its own.** The questions it would have to answer: + +- **Accounts and how each authenticates:** + - IMAP with an app password; + - a provider's OAuth with a registered application; + - the mesh's own mail server, which can publish delivery itself. + + An employer's tenant may forbid registering an application at all. +- **What the bus records:** + - headers and a summary as events; + - bodies and attachments in an object store the event points at; + - retention, since mail is the most personal data the mesh would hold. +- **What consumes it:** a notifier bridge to the desktop (the predecessor's notifications), search, + an agent's context. +- **Where it runs:** one long-running module, not per machine (ADR 0198). + +The obvious first step is the mail server the mesh already runs. From b7aebedc2dcd78a0d12e88fd192ff380f7c6e077 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:00:01 +0200 Subject: [PATCH 5/9] Research 026/04: the screensaver, monitor layouts and menus --- .../00-overview.md | 1 + .../04-screensaver-displays-and-menus.md | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md index 410fc0f..1383048 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md @@ -60,4 +60,5 @@ desktops. Measured in [01](01-what-the-workstations-run.md): - [01 — What the workstations run](01-what-the-workstations-run.md): evidence. - [02 — The questions and the options](02-the-questions-and-the-options.md) +- [04 — Screensaver, displays and menus](04-screensaver-displays-and-menus.md): the lock and idle module, monitor layouts by the monitors' identity, rofi and dmenu behind one launcher seat - [03 — What the predecessor taught](03-what-the-predecessor-taught.md): its 128 modules and 3,395 commits, as patterns to keep and failures not to repeat; shared with research 027. diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md new file mode 100644 index 0000000..55b25ed --- /dev/null +++ b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md @@ -0,0 +1,70 @@ +# 04 — Screensaver, displays and menus + +Three areas the operator named on 2026-10-04, as their own modules. Each sharpens a row of +[01](01-what-the-workstations-run.md) and a question of [02](02-the-questions-and-the-options.md). + +## The screensaver: idle, lock and display power + +**Measured on both workstations:** + +- **Idle and lock** are three things wired by hand in the session's start script: + - the X screensaver timeout (`xset s 1800`); + - the display power timeouts (`xset dpms`); + - `xss-lock` running the colour build of `i3lock` through a wrapper, in a respawn loop. +- **A second screensaver,** xscreensaver, is installed and deliberately not started. Earlier it + overrode the display power settings with its own, and locked nothing. Its configuration file is + still in the home. +- **The lock screen's 20-odd colours and formats** were predecessor theme variables. +- **The colour build is not in the official repositories** (research 026/01). + +**Starting position:** + +- **One module for the lock screen,** holding `node-lock-screen`: the locker and its wrapper as the + module's own files, the screensaver and display power timeouts, and `xss-lock`. +- The timeouts and colours are its defaults, and settings later (issue 168). +- The colour build ships as ADR 0205's pinned archive, or the module uses the official `i3lock`. + That is the operator's choice, and the colours are the only difference. +- xscreensaver is not a module; its package and file are removed. +- `xss-lock` needs the logind session, so it stays a session-start line contributed into + `.xinitrc`'s block (question 4), not a unit. + +## Monitor layout (xrandr) + +**Measured:** + +- Each workstation has a layout script generated by `arandr`, with the monitor names baked in. One + workstation also has several layouts for named places, a hotplug rule and a wizard. +- **The desktop carried the laptop's layout scripts.** +- No `xorg.conf.d`, and no layout tool beyond the scripts. + +**Starting position: `autorandr`** (official repositories) inside the display server's module. + +- `autorandr` saves a layout as a profile **keyed by the connected monitors' identities** (their EDID) + and applies the matching one at login and on hotplug. +- Profiles therefore need no machine's name. A profile can be shared mesh-wide and simply never + matches on a machine without those monitors. That is exactly the "say it by what is there, never by + a name" rule (ADR 0112). +- The profiles are the operator's data, saved by the tool itself, so they are *found* (ADR 0182). A + `layout` verb on `node-display-server` lists, saves and applies them. +- The arandr scripts and the hotplug rule retire once a profile exists for each. + +## Menus: rofi and dmenu + +**Measured:** + +- rofi is the launcher, the power menu, the theme picker and the clipboard menu. +- The operator's scripts call `rofi -dmenu` in four places and **plain `dmenu` in two. dmenu is + installed on neither workstation, so those two fail.** + +**Starting position:** + +- **`rofi` holds `node-launcher`**, and the seat's protocol includes a **dmenu-compatible command**: + read choices on standard input, print the chosen one. Scripts call that command, not a program by + name. +- **`dmenu` is a module of its own** (official repositories), able to hold the same seat on a machine + that wants it, for instance a Wayland session where `wofi` or `fuzzel` would hold it instead. +- The rofi module carries its theme files, and the menus that belong to other modules arrive as those + modules' scripts: + - power menu → the session; + - clipboard menu → the clipboard module; + - theme picker → settings, once issue 168 closes. From 550453c5db16183889787d38936b297ee473ad0c Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:00:20 +0200 Subject: [PATCH 6/9] Research 026/04: the clipboard --- .../00-overview.md | 2 +- .../04-screensaver-displays-and-menus.md | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md index 1383048..4931cf5 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md @@ -60,5 +60,5 @@ desktops. Measured in [01](01-what-the-workstations-run.md): - [01 — What the workstations run](01-what-the-workstations-run.md): evidence. - [02 — The questions and the options](02-the-questions-and-the-options.md) -- [04 — Screensaver, displays and menus](04-screensaver-displays-and-menus.md): the lock and idle module, monitor layouts by the monitors' identity, rofi and dmenu behind one launcher seat +- [04 — Screensaver, displays and menus](04-screensaver-displays-and-menus.md): the lock and idle module, monitor layouts by the monitors' identity, rofi and dmenu behind one launcher seat, the clipboard - [03 — What the predecessor taught](03-what-the-predecessor-taught.md): its 128 modules and 3,395 commits, as patterns to keep and failures not to repeat; shared with research 027. diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md index 55b25ed..1c19fe4 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md @@ -68,3 +68,27 @@ Three areas the operator named on 2026-10-04, as their own modules. Each sharpen - power menu → the session; - clipboard menu → the clipboard module; - theme picker → settings, once issue 168 closes. + +## The clipboard: xclip and greenclip + +**Measured:** + +- **greenclip** keeps the clipboard's history, and rofi shows it on a key binding. +- **greenclip is not in the official repositories.** +- It is started two ways: the window manager's configuration starts it on both workstations, and on + one a user unit is enabled as well. +- Its configuration names an absolute home path. +- **xclip** (official) is the command-line clipboard the operator's scripts use. + +**Starting position:** + +- **`xclip` is a module of its own,** a package and nothing else. It is the tool scripts depend on, + and a module that needs it requires it. +- **The clipboard manager holds `node-clipboard`:** its daemon, started once by the session (a session + contribution, or a user unit once user-scoped units ship, never both), its configuration with no + absolute path, and its menu binding contributed to the window manager. +- **Which manager holds it is the operator's choice:** + - greenclip, as today, shipped under ADR 0205; + - or `clipmenu` (official), which feeds the same dmenu-compatible command as the launcher seat above, + and needs no archive. +- **On Wayland** the same seat is held by `cliphist` with `wl-clipboard`, both official. From 6e5dfd2ab823c67272ca3e41916ef392c2d8edbb Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:03:29 +0200 Subject: [PATCH 7/9] Research 027/03: the laptop's power management; 026/04: fonts --- .../00-overview.md | 2 +- .../04-screensaver-displays-and-menus.md | 23 +++++++++++ .../00-overview.md | 2 +- .../03-the-accounts-own-tools.md | 41 +++++++++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md index 4931cf5..68d3f43 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md @@ -60,5 +60,5 @@ desktops. Measured in [01](01-what-the-workstations-run.md): - [01 — What the workstations run](01-what-the-workstations-run.md): evidence. - [02 — The questions and the options](02-the-questions-and-the-options.md) -- [04 — Screensaver, displays and menus](04-screensaver-displays-and-menus.md): the lock and idle module, monitor layouts by the monitors' identity, rofi and dmenu behind one launcher seat, the clipboard +- [04 — Screensaver, displays and menus](04-screensaver-displays-and-menus.md): the lock and idle module, monitor layouts by the monitors' identity, rofi and dmenu behind one launcher seat, the clipboard, fonts - [03 — What the predecessor taught](03-what-the-predecessor-taught.md): its 128 modules and 3,395 commits, as patterns to keep and failures not to repeat; shared with research 027. diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md index 1c19fe4..e52dce3 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md @@ -92,3 +92,26 @@ Three areas the operator named on 2026-10-04, as their own modules. Each sharpen - or `clipmenu` (official), which feeds the same dmenu-compatible command as the launcher seat above, and needs no archive. - **On Wayland** the same seat is held by `cliphist` with `wl-clipboard`, both official. + +## Fonts + +**Measured:** + +- The fonts the desktop uses are **hand-copied files** in the account's font directory, not packages: + - a Nerd font for the window manager, the bar and the terminal; + - a second one for the prompt; + - on one workstation, the same four files twice, once under URL-encoded names; + - on the other, a different build of the same font and three more copied from a theme's repository. +- The system's default monospace is a different font (`Noto Sans Mono`), so anything that asks for + `monospace` gets another face than the terminal. +- The DPI is fixed in an X resource. +- **Every Nerd font in use is in the official repositories** (Hack, Meslo, Iosevka, JetBrains Mono). + +**Starting position:** + +- **A `fonts` module:** the official packages, and a fontconfig file it owns that maps `monospace` (and + the emoji and symbol fallbacks) to the chosen faces, so every program agrees. +- The terminal, bar, launcher and prompt modules name the family, not a file. +- The DPI becomes the display server's setting (issue 168). +- The copied files are removed by the operator once the packages are in (ADR 0182). +- Fonts are not a seat: several coexist. The module owns the one place where *the* default is said. diff --git a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md index 024df41..e3a4a94 100644 --- a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md +++ b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md @@ -51,5 +51,5 @@ matters on their own. - [01 — What the machines run](01-what-the-machines-run.md): evidence. - [02 — Candidates and questions](02-candidates-and-questions.md) -- [03 — The account's own tools](03-the-accounts-own-tools.md): `~/.ssh` as one module's, scripts on every machine, the keyring, mail as events +- [03 — The account's own tools](03-the-accounts-own-tools.md): `~/.ssh` as one module's, scripts on every machine, the keyring, the laptop's power management, mail as events - The predecessor's lessons, shared with research 026: [026/03](../026-the-graphical-session-as-modules/03-what-the-predecessor-taught.md) diff --git a/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md b/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md index 69f0f4d..00c596b 100644 --- a/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md +++ b/01-RESEARCH/027-the-system-layer-as-modules/03-the-accounts-own-tools.md @@ -126,3 +126,44 @@ The second unlock prompt and the second daemon start go away. - **Where it runs:** one long-running module, not per machine (ADR 0198). The obvious first step is the mail server the mesh already runs. + +## Power management on the laptop + +*"Power management for the laptop."* + +**Measured on the laptop** (a gaming model with a hybrid GPU): + +- **The platform profile is driven by a vendor daemon** (`asusd`) and its CLI. The vendor CLI is + now in the official repositories; the copy installed came from elsewhere. A predecessor script + runs as a user unit and switches the profile every five seconds: quiet on battery, balanced on + mains, performance above 50 % CPU. +- **The hybrid GPU's mode** (now hybrid) is held by a second vendor daemon (`supergfxd`), which is + **not** in the official repositories. Kernel-module options for the discrete GPU's power state + and its suspend, hibernate and resume units are set by hand. Its own power daemon is masked. +- **The battery charge limit is 80 %,** set by the vendor daemon. +- **The lid and power key suspend.** The brightness key is ignored by logind and handled by the + vendor-key path. Both are logind drop-ins. +- **Memory pressure:** compressed swap in RAM (`zram`) beside a swap file and a partition; + `systemd-oomd` with drop-ins; a predecessor *memory guard* user unit that notifies before the OOM + killer acts. +- `upower` runs. There is no `power-profiles-daemon`, `tlp`, `auto-cpufreq` or `thermald`, so nothing + competes with the vendor daemon, by design. + +All of it came from two predecessor modules, one of which was a laptop-model *flavor*. A desktop +received part of it (research 026/01). + +**Starting position:** + +- **A hardware module per machine model** (here, the laptop's model). It holds the vendor daemon and + its profile configuration, the GPU mode daemon (ADR 0205's case, or the build machine's package + repository of research 027 question 1), the discrete GPU's module options and suspend units, the + logind drop-ins, the battery charge limit, and the vendor keys and brightness. It is assigned to + the one machine of that model, and to any second one later. +- **The profile switching** moves from a polling script to the module's own long-running code + (ADR 0198). It reacts to the power-supply change event instead of polling, and its thresholds + become settings (issue 168). +- **Memory pressure is not the laptop's alone.** `zram` and `systemd-oomd` with the notifier are a + `memory-pressure` module, assigned wherever wanted. The swap layout stays the machine's (`kernel` + module, question 3). +- A **`node-power-profile`** seat (vendor daemon, or `power-profiles-daemon` on other hardware) + gives the mesh one verb, `profile`, the same on every machine that has one. From 9016d88d54c84d21acdfb6572099b901c03f87e7 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:06:08 +0200 Subject: [PATCH 8/9] Research 026/027: improve while adopting, the fonts chosen, and a catalogue of the tools each module serves --- .../00-overview.md | 10 +++ .../04-screensaver-displays-and-menus.md | 16 +++- .../05-the-tools-each-module-serves.md | 77 +++++++++++++++++++ .../00-overview.md | 9 +++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 01-RESEARCH/026-the-graphical-session-as-modules/05-the-tools-each-module-serves.md diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md index 68d3f43..5e44247 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md @@ -46,6 +46,15 @@ desktops. Measured in [01](01-what-the-workstations-run.md): - The session's environment is a hand-kept second copy of the account's, beside the one the mesh now writes. +## How it is approached + +**Adopting is also improving** (the operator, 2026-10-04). A module is not a copy of what a machine +does today. Making it is the moment to fix what is broken, drop what is dead, choose the better tool +and remove the leftovers. Every module's design lists its improvements over today. **Every module +also serves tools,** many of them, for reading, acting and diagnosing; a module that only places a +package and a file is unfinished. The tools are catalogued in +[026/05](../026-the-graphical-session-as-modules/05-the-tools-each-module-serves.md). + ## What it touches - **The seat table:** up to ten node seats. @@ -61,4 +70,5 @@ desktops. Measured in [01](01-what-the-workstations-run.md): - [01 — What the workstations run](01-what-the-workstations-run.md): evidence. - [02 — The questions and the options](02-the-questions-and-the-options.md) - [04 — Screensaver, displays and menus](04-screensaver-displays-and-menus.md): the lock and idle module, monitor layouts by the monitors' identity, rofi and dmenu behind one launcher seat, the clipboard, fonts +- [05 — The tools each module serves](05-the-tools-each-module-serves.md): a first catalogue for the modules of 026 and 027 - [03 — What the predecessor taught](03-what-the-predecessor-taught.md): its 128 modules and 3,395 commits, as patterns to keep and failures not to repeat; shared with research 027. diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md index e52dce3..44530c9 100644 --- a/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md +++ b/01-RESEARCH/026-the-graphical-session-as-modules/04-screensaver-displays-and-menus.md @@ -107,10 +107,22 @@ Three areas the operator named on 2026-10-04, as their own modules. Each sharpen - The DPI is fixed in an X resource. - **Every Nerd font in use is in the official repositories** (Hack, Meslo, Iosevka, JetBrains Mono). +**Decided** (the operator left the choice open, except that it must not be today's Hack): + +| role | face | why | +|---|---|---| +| monospace: terminal, window manager, bar, launcher, prompt | **JetBrains Mono Nerd Font** | built for long reading in a terminal, unambiguous `0O1lI`, optional ligatures; a version-3 Nerd font, so every icon the prompt and bar use is present | +| interface: GTK, Qt, notifications | **Inter** | designed for screens, clear at small sizes | +| icons missing from any face | Nerd Fonts Symbols | a fallback, so a font without icons still shows them | +| emoji | Noto Color Emoji | | +| serif and every other script | Noto | | + +All five are official packages. + **Starting position:** -- **A `fonts` module:** the official packages, and a fontconfig file it owns that maps `monospace` (and - the emoji and symbol fallbacks) to the chosen faces, so every program agrees. +- **A `fonts` module:** those packages, and a fontconfig file it owns that maps `monospace`, `sans-serif`, + `serif` and the emoji and symbol fallbacks to the chosen faces, so every program agrees. - The terminal, bar, launcher and prompt modules name the family, not a file. - The DPI becomes the display server's setting (issue 168). - The copied files are removed by the operator once the packages are in (ADR 0182). diff --git a/01-RESEARCH/026-the-graphical-session-as-modules/05-the-tools-each-module-serves.md b/01-RESEARCH/026-the-graphical-session-as-modules/05-the-tools-each-module-serves.md new file mode 100644 index 0000000..efa669e --- /dev/null +++ b/01-RESEARCH/026-the-graphical-session-as-modules/05-the-tools-each-module-serves.md @@ -0,0 +1,77 @@ +# 05 — The tools each module serves + +A first catalogue for the modules of research 026 and 027, as the operator asked: "all kinds of useful +tools for all these modules". Each tool is served by the node's runtime (ADR 0175), on the machine the +module runs on. Through discovery (ADR 0195) it is reachable from any machine as +`/.`, or as `/.` where a seat defines it. + +**Conventions:** + +- **(r)** reads. +- **(a)** acts on the machine, escalating where it must, as the packet filter does (to-be 38 WP4). +- **(d)** is a desktop act that needs the operator's session. +- A tool that changes something a module declares says so in its answer: the next push restores the + declaration. +- Every tool answers structured data, not prose (issue 229). +- **Seat verbs** (marked *seat*) are the protocol every holder of that seat serves. The rest are the + module's own. + +## The graphical session (026) + +| module | tools | +|---|---| +| `xorg` (*node-display-server*) | *seat* `displays` (r: outputs, modes, rates, connected monitors with their identity) · *seat* `layout` (r/a: list, save, apply an autorandr profile) · `set-mode` (a: one output's resolution, rate, rotation, scale) · `primary` (a) · `dpi` (r/a) · `input-devices` (r) · `input-set` (a: touchpad tap, natural scroll, pointer speed) · `keyboard` (r/a: layout and options) · `screenshot` (d: one screen or all, as a file) · `x-log` (r: the server's errors since start) | +| `i3` (*node-display-session*) | *seat* `reload` (a) · *seat* `workspaces` (r) · *seat* `windows` (r: tree with classes, titles, workspaces) · `focus` (d: window or workspace) · `move` (d: window to workspace or output) · `layout-save` / `layout-restore` (d: a workspace's arrangement) · `exec` (d: start a program in the session) · `kill` (d) · `bindings` (r: every key binding and what it runs) · `config-check` (r: validate the composed configuration before a reload) · `marks` (r) · `scratchpad` (d) | +| `sway` (*node-display-server*, *node-display-session*) | the same seat verbs over Wayland, plus `outputs` (r) and `idle-inhibitors` (r) | +| `lemurs` (*node-login-manager*) | *seat* `sessions` (r: what the login screen offers) · *seat* `default-session` (r/a) · `logins` (r: who logged in when, from the journal) | +| `xterm` (*node-terminal-emulator*) | *seat* `open` (d: a terminal, optionally running a command, in a directory) · `font` (r/a: face and size) · `colours` (r) | +| `i3status-rust` (*node-bar*) | *seat* `reload` (a) · `blocks` (r: what the bar shows and each block's current value) · `block-run` (r: run one block once and answer its output) · `themes` (r) | +| `picom` (*node-compositor*) | *seat* `restart` (a) · `rules` (r: opacity, shadow and blur rules in force) · `window-opacity` (d) · `toggle` (d: compositing off and on, for a game or a test) | +| `rofi` (*node-launcher*) | *seat* `menu` (d: show a list, answer the chosen line: the dmenu-compatible command as a tool) · `applications` (r: the desktop entries it would offer) · `themes` (r) · `run` (d) | +| `dmenu` (*node-launcher*) | *seat* `menu` (d) | +| `dunst` (*node-notifier*) | *seat* `send` (d: title, body, urgency, actions) · *seat* `history` (r) · `pause` / `resume` (d: do not disturb) · `close-all` (d) · `rules` (r) · `count` (r: shown, waiting, history) | +| lock module (*node-lock-screen*) | *seat* `lock` (d) · `idle` (r/a: screensaver and display power timeouts) · `inhibit` (d: keep the screen on for a while) · `locked` (r: is the session locked now, and since when) | +| clipboard manager (*node-clipboard*) | *seat* `history` (r: entries, newest first, length-limited) · *seat* `copy` (d: put text on the clipboard) · `paste` (r: what the clipboard holds now) · `clear` (d) · `delete` (d: one entry) | +| `xclip` | `copy` (d) · `paste` (r): the plain clipboard without a manager | +| `feh` (wallpaper) | `set` (d: an image, per output) · `current` (r) | +| `fonts` | `families` (r: installed faces) · `match` (r: what `monospace`, `sans-serif` and `emoji` resolve to) · `glyph` (r: which installed font has a given character) · `cache-rebuild` (a) | +| theme module | `appearance` (r/a: dark or light, for GTK, Qt and the portal at once) · `cursor` (r/a) · `icons` (r) · `portal-check` (r: which portal backend answers which interface) | +| `gnome-keyring` (*node-secret-service*) | *seat* `unlocked` (r) · `lock` (d) · `collections` (r: names and item counts, never secrets) · `ssh-keys` (r: what the agent holds, by fingerprint) | +| desktop hardware module (laptop) | `brightness` (r/a: panel and keyboard) · `battery` (r: charge, health, cycles, limit) · `charge-limit` (r/a) · `gpu-mode` (r/a: integrated, hybrid, discrete) · *seat* `profile` (r/a: quiet, balanced, performance) · `thermals` (r: temperatures and fan speeds) · `power-draw` (r) | + +## The system and the account (027) + +| module | tools | +|---|---| +| `docker` (*node-container-runtime*, ADR 0166) | *seat* `list`, `inspect`, `logs`, `stats`, `start`, `stop`, `restart` (r/a) · `images` (r: with size and which container uses each) · `prune` (a: dangling images, stopped containers not held by the mesh, build cache, with a dry run first) · `disk-usage` (r) · `networks` (r) · `volumes` (r: with what mounts each and whether the mesh holds it) · `events` (r: the last hour) · `daemon-config` (r) | +| `docker-compose` | `projects` (r: compose projects running and where their files are) · `up` / `down` / `restart` (a: one project, by directory) · `logs` (r) · `ps` (r) | +| `sudo` | `rules` (r: what the account may run, without a prompt and with one) · `check` (r: does the escalation the mesh relies on work here) | +| `pacman` | `search` (r) · `installed` (r: with version and explicitly or as a dependency) · `info` (r) · `owns` (r: which package owns a path) · `files` (r) · `updates` (r: what an upgrade would change) · `upgrade` (a: with the news first) · `orphans` (r) · `remove-orphans` (a) · `cache` (r/a: size, clean to the last N versions) · `history` (r: installs and upgrades from the log) · `mirrors` (r/a: rank and refresh) · `news` (r: distribution news since the last upgrade) | +| AUR (package repository, 027 question 1) | `search` (r) · `build` (a: on the build machine, into the mesh's repository) · `outdated` (r) · `published` (r) | +| `snapd`, `flatpak` | `list` (r) · `install` / `remove` (a) · `update` (a) · `runtimes` (r) · `disk-usage` (r) | +| `time-sync` | `status` (r: synchronised, offset, server) · `servers` (r) · `sync-now` (a) | +| `localization` | `get` (r: locale, time zone, keymap) · `time-zone` (r/a) · `locales` (r) | +| `kernel` | `running` (r: version, command line, uptime) · `installed` (r) · `modules` (r: loaded, with what uses them) · `reboot-needed` (r: a newer kernel or library than the one running) · `microcode` (r) · `boot-entries` (r) · `initramfs-rebuild` (a) · `dmesg` (r: errors since boot) | +| `logrotate` | `status` (r: last rotation per log) · `force` (a: one configuration) · `big-logs` (r: the largest logs on the machine) | +| `avahi` | `browse` (r: services on the local network) · `resolve` (r) | +| `cups` | `printers` (r) · `queue` (r) · `cancel` (a) · `print` (a: a file to a printer) · `default` (r/a) | +| `bluetooth` | `devices` (r: paired, connected, battery where reported) · `connect` / `disconnect` (a) · `scan` (r) · `power` (r/a) | +| `ssh-client` (owns `~/.ssh`) | `hosts` (r: every `Host` and where it came from: the mesh, a module, the operator) · `check` (r: modes, keys without a passphrase, keys unused for a year, stale `known_hosts` entries) · `authorized` (r: who may log in, by fingerprint and comment) · `revoke` (a: one authorized key, into the operator's region) · `known-host` (r/a: verify, refresh one host's key) · `test` (r: can this machine reach a host and authenticate, batch mode) | +| `sshd` | `sessions` (r: who is logged in, from where) · `config-effective` (r: `sshd -T`) · `failed-logins` (r: since a time, with fail2ban's verdicts) | +| scripts modules | `list` (r: each script with its one-line description) · `run` (a: one script by name with arguments, as the account, bounded like `execute`) · `which` (r: which module ships a command) | +| `node-env` (*node-environment*) | `show` (r: every variable and `PATH` entry with the module that contributed it) · `diff` (r: what a shell actually has versus what the mesh composed) | +| `zsh` (*node-login-shell*) | *seat* `execute` · `zsh_config` (r) · `history-search` (r: the account's history, by pattern) · `functions` (r: aliases and functions in force, with where each came from) · `startup-time` (r: how long an interactive shell takes to start, per slot) | +| `memory-pressure` | `status` (r: memory, swap, compressed swap ratio, pressure stall) · `top` (r: the largest processes) · `oom-history` (r: what was killed, when) | +| `zfs` | `pools` (r: health, capacity, fragmentation) · `datasets` (r) · `snapshots` (r/a: list, create, destroy by name) · `scrub` (r/a: status, start) · `errors` (r) · `arc` (r: cache statistics) | +| `nfs-server`, `samba` | `exports` / `shares` (r) · `clients` (r: who has it mounted now) · `reload` (a) | +| `nfs-client`, `smb-client` | `mounts` (r: each share, mounted or not, and since when) · `mount` / `unmount` (a) · `test` (r: is the server reachable, is the export offered) | +| hosts-file holder (*node-hosts-file*, ADR 0199) | *seat* `entries`, `add`, `remove` | +| `vnstat`, `lm_sensors` | `traffic` (r: per interface, day, month) · `sensors` (r) | +| mail consumer (future effort) | `accounts` (r) · `search` (r) · `unread` (r) · `read` (r: one message) · `mark` (a) · `send` (a) | + +## What this catalogue is for + +It is a starting list, not a contract. A tool becomes a contract only when it is a seat's verb, and +each seat's verbs are decided in that seat's record (ADR 0132). A module's own tools can grow freely. +Every row above is a tool the operator would otherwise run by hand over ssh. That is the measure of +whether one is worth writing. diff --git a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md index e3a4a94..a95b1d4 100644 --- a/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md +++ b/01-RESEARCH/027-the-system-layer-as-modules/00-overview.md @@ -37,6 +37,15 @@ Measured in [01](01-what-the-machines-run.md): on four machines, almost nothing owned by a module. The pieces differ by machine for no recorded reason. Three findings are security matters on their own. +## How it is approached + +**Adopting is also improving** (the operator, 2026-10-04). A module is not a copy of what a machine +does today. Making it is the moment to fix what is broken, drop what is dead, choose the better tool +and remove the leftovers. Every module's design lists its improvements over today. **Every module +also serves tools,** many of them, for reading, acting and diagnosing; a module that only places a +package and a file is unfinished. The tools are catalogued in +[026/05](../026-the-graphical-session-as-modules/05-the-tools-each-module-serves.md). + ## What it touches - **The container runtime seat** (ADRs 0165 and 0166, both proposed). From ca13f59c88258ea251e8a3b403a90161b8ea5ad5 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 4 Oct 2026 12:08:26 +0200 Subject: [PATCH 9/9] =?UTF-8?q?To-be=2042:=20the=20machines'=20modules,=20?= =?UTF-8?q?in=20order=20=E2=80=94=20every=20machine's,=20then=20the=20work?= =?UTF-8?q?stations',=20then=20one=20model's?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../42-the-machines-modules-in-order.md | 98 +++++++++++++++++++ 03-DESIGN/01-to-be/README.md | 1 + 2 files changed, 99 insertions(+) create mode 100644 03-DESIGN/01-to-be/42-the-machines-modules-in-order.md diff --git a/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md b/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md new file mode 100644 index 0000000..7708391 --- /dev/null +++ b/03-DESIGN/01-to-be/42-the-machines-modules-in-order.md @@ -0,0 +1,98 @@ +--- +layer: to-be +status: in-progress +code: [mesh-catalog, mesh-controller, mesh-host] +updated: 2026-10-04 +decisions: + - 02-DECISIONS/0173-the-operators-machine-is-the-meshs-and-a-module-is-what-it-declares.md + - 02-DECISIONS/0040-what-a-module-is.md + - 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md + - 02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md + - 02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md + - 02-DECISIONS/0198-a-modules-long-running-code-is-launched-by-the-node-runtime-and-reaches-the-bus-through-it.md + - 02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md + - 02-DECISIONS/0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md + - 02-DECISIONS/0205-software-the-distribution-does-not-package-ships-as-a-pinned-archive-of-the-module.md +--- + +# 42. The machines' modules, in order + +The order in which the modules of [research 026](../../01-RESEARCH/026-the-graphical-session-as-modules/00-overview.md) +and [research 027](../../01-RESEARCH/027-the-system-layer-as-modules/00-overview.md) are built and rolled +out, as the operator set it on 2026-10-04. + +Three phases, in order, each built from the bottom up (the most core module first): + +1. the modules every machine shares; +2. those both workstations share; +3. those of one machine model. + +The shell came first ([to-be 41](41-the-shell-and-the-accounts-environment.md)). Each module's +definition, improvements and tools follow the research. A position that needs a new mechanism (a new +seat, a gated assignment, generalised contributions) gets its record when its first module needs it, +not before. Modules that need none go ahead now. + +## How every module moves + +1. Written in the catalogue, with its tools and their tests, and checked by the controller's catalogue + check. +2. Merged, which builds it. +3. Assigned to **the first workstation, the proving machine**, and pushed. Its tools and files are + proven there. +4. Then assigned to every other machine it applies to, and pushed. + +The operator delegated the go-ahead for each step on 2026-10-04 ("non-important decisions, easily +reversed"). Each step is reported. + +**Adopting is also improving** (research 026, 027 overviews): every module lists what it fixes over +today, and leaves no predecessor copy of what it now owns. + +## Phase 1 — every machine + +In order: + +| | module | owns | improves | +|---|---|---|---| +| 1 | `sudo` | the operator account's escalation, as a drop-in it owns | declares what three modules' tools assume and nothing stated | +| 2 | `localization` | locale, time zone, console keymap | one machine on another zone and keymap | +| 3 | `time-sync` | timesyncd and its servers | two different daemons across four machines | +| 4 | `pacman` | the package manager's configuration, mirrors and their refresh, cache cleaning | mirrors generated once and never again; caches never cleaned | +| 5 | `logrotate` | the timer and base configuration | rotation running on one machine of four | +| 6 | `avahi` | the daemon | on all four, owned by none | +| 7 | `systemd` | the service manager's tools (to-be 41 WP4) | built, assigned nowhere | +| 8 | `docker` | the runtime's packages, base configuration, group | four configurations, one owner on one machine | +| 9 | `ssh-client` | everything under `~/.ssh` (research 027/03) | a predecessor's entries winning over the mesh's; stale keys | +| 10 | scripts | the operator's own scripts, shared and per role (research 027/03) | under no version control, copied by hand | +| 11 | `kernel` | kernel, microcode, boot entries | two machines without microcode | + +`kernel` is last because a mistake in it costs a boot. `docker` stays a module without the runtime seat +until ADRs 0165 and 0166 are accepted. + +## Phase 2 — both workstations + +In order: + +1. `fonts`; +2. `xorg` with autorandr; +3. `lemurs`; +4. `i3`; +5. `xterm`; +6. the theme module; +7. `picom`, `rofi`, `dmenu`, `dunst`, the lock module, `xclip`, the clipboard manager, `feh` and + `i3status-rust`; +8. `gnome-keyring`; +9. `docker-compose`, `snapd`, `flatpak`, `cups`, `bluetooth`. + +The seats, gating and session-start questions of research 026 §2–§5 are recorded when `xorg` and `i3` +need them. + +## Phase 3 — one machine model + +The laptop's hardware module (vendor daemon, GPU mode, charge limit, logind, brightness and vendor keys) +and `memory-pressure` (research 027/03). + +## How it is checked + +Each module's own tests and the catalogue check, at merge. On the proving machine, each tool answered +through the mesh and each owned file checked in place, before any other machine is assigned. This +document's tables are updated as each module lands. diff --git a/03-DESIGN/01-to-be/README.md b/03-DESIGN/01-to-be/README.md index 125651c..ab52764 100644 --- a/03-DESIGN/01-to-be/README.md +++ b/03-DESIGN/01-to-be/README.md @@ -44,6 +44,7 @@ document is written and this one's status becomes `implemented`. | [`37-the-operators-machine.md`](37-the-operators-machine.md) | **In progress.** Every configurable thing on a node is a module, the home included; one default per module varied by settings or kept regions; roles a machine has once as seats with tool contracts; one tool runtime per node on the host side | [ADR 0173](../../02-DECISIONS/0173-the-operators-machine-is-the-meshs-and-a-module-is-what-it-declares.md), [0174](../../02-DECISIONS/0174-a-node-varies-a-module-through-settings-and-kept-regions-never-an-edit.md), [0175](../../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md), [0176](../../02-DECISIONS/0176-the-login-shell-is-a-node-seat-and-execute-is-its-contract.md), [0177](../../02-DECISIONS/0177-a-unit-may-be-user-scoped-and-the-service-manager-is-a-node-seat.md) | | [`38-building-the-operators-machine.md`](38-building-the-operators-machine.md) | **In progress.** The work of design 37 as packages: the runtime serves many modules, the controller composes one per node, the console becomes its serving mode, the packet filter moves first, then the shell and the service manager — tested on the live mesh by the operator's decision | [ADR 0175](../../02-DECISIONS/0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md), [0160](../../02-DECISIONS/0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md), [0149](../../02-DECISIONS/0149-the-live-mesh-is-the-test-bed.md) | | [`41-the-shell-and-the-accounts-environment.md`](41-the-shell-and-the-accounts-environment.md) | **In progress.** The shell and the account's environment as modules: an environment module every module contributes variables and `PATH` entries to, shell code contributed to the login shell in named slots, the prompt and plugins as modules, the host giving a login back, and the service manager's module finished | [ADR 0203](../../02-DECISIONS/0203-the-accounts-environment-is-one-modules-and-every-module-contributes-to-it.md), [ADR 0204](../../02-DECISIONS/0204-a-module-contributes-shell-code-to-the-login-shell-in-named-slots.md), [ADR 0205](../../02-DECISIONS/0205-software-the-distribution-does-not-package-ships-as-a-pinned-archive-of-the-module.md) | +| [`42-the-machines-modules-in-order.md`](42-the-machines-modules-in-order.md) | **In progress.** The order the machines' modules of research 026 and 027 are built and rolled out: every machine's first (sudo, localization, time sync, pacman, logrotate, avahi, systemd, docker, `~/.ssh`, scripts, kernel), then both workstations', then one machine model's; each proven on one workstation before the rest | [ADR 0173](../../02-DECISIONS/0173-the-operators-machine-is-the-meshs-and-a-module-is-what-it-declares.md), [ADR 0182](../../02-DECISIONS/0182-inside-a-home-the-mesh-owns-what-it-places-and-holds-the-rest-as-found.md), [ADR 0205](../../02-DECISIONS/0205-software-the-distribution-does-not-package-ships-as-a-pinned-archive-of-the-module.md) | ## Not yet written