Commit Graph
740 Commits
Author SHA1 Message Date
jochen 1059f12ee0 asus-zephyrus-g14: the laptop's keys, scripts and i3 lines are the module's, with a resume backstop
The i3 and laptop READMEs each pointed at the other for 10-asus.conf and 20-g14.conf,
so nobody owned them. The module now writes both (adopted paths, so no duplicate
binding breaks i3's config check), ships the scripts they call, runs the media keys
with notifications again, and brings back the touchpad reset after resume as a unit
the sleep services want. zephyrus_keys answers what each custom key runs; the check
flags as-user, thd's account and the resume unit. xorg-xinput is the xorg module's.
2026-10-04 15:49:46 +02:00
jochen b098b64b22 Phase 3: asus-zephyrus-g14 and memory-pressure, with Go tools and long-running code
The laptop model's hardware module and a memory-pressure module for any
machine (hq research 027/03, 026/05, to-be 42 phase 3). The predecessor's
polling auto-profile and mem-guard user scripts become each module's own
Go code launched by the node runtime (ADR 0198): a profile switcher woken
by the kernel's power-supply uevents, and a guard that warns on RAM, swap
or PSI before systemd-oomd acts, on the desktop over the account's bus and
always as an event. supergfxctl and triggerhappy are kept as found
(research 027 Q1).
2026-10-04 15:42:56 +02:00
mesh-admin f2601e40de Merge pull request 'The licence manager's verb is public-key' (#274) from fix/the-verb-is-public-key into main 2026-10-04 13:33:34 +00:00
jochen 0dc13965e5 The licence manager's verb is public-key: a seat's verb is lower-case letters, digits and hyphens
public_key failed the builder's manifest check and stopped the manager's
rebuild; claude-code asks the new name.
2026-10-04 15:33:28 +02:00
mesh-admin 948e794e8b Merge pull request 'The licence manager's seat declares public_key' (#273) from fix/the-seat-serves-public-key into main 2026-10-04 13:22:27 +00:00
jochen e8562b58ce The licence manager's seat declares public_key, which claude_code_add_api_key asks (novox/hq ADR 0209) 2026-10-04 15:22:15 +02:00
mesh-admin 1db3fdeb2f Merge pull request 'lemurs, i3status-rust: what the first assignment refused' (#272) from fix/desktop-first-assign into main 2026-10-04 13:17:21 +00:00
jochen c790ee24d3 lemurs, i3status-rust: what the first assignment refused
The host refuses boot on a service that leaves its state to the machine, so lemurs.service
is declared running (no trigger, so a push still never restarts it). pacman-contrib is the
pacman module's, and two modules declaring one package make a node unresolvable.
2026-10-04 15:17:07 +02:00
mesh-admin c5c47b92f4 Merge pull request 'A login moves its node; an API key is added from any node, sealed (hq ADR 0209)' (#271) from feat/a-login-moves-its-node into main 2026-10-04 13:12:03 +00:00
jochen 4ef4983d0d A login moves its node; an API key is added from any node, sealed (novox/hq ADR 0209)
The manager binds the node a login was adopted from to that login's licence,
switching it if it was bound to another; serves public_key; adopt takes a
key sealed to it. claude-code gains claude_code_add_api_key: read a file on
this node, seal, hand to adopt, remove the file, optionally switch here.
2026-10-04 15:11:54 +02:00
mesh-admin 1c799101fa Merge pull request 'Phase 2 desktop: thirteen modules, one per piece of the graphical session (hq ADR 0208, to-be 42)' (#270) from feat/phase-2-desktop into main 2026-10-04 13:09:48 +00:00
jochen 19a79bef86 i3, screen-lock: the session's output to the journal, and the colour locker removed before i3lock is checked
lemurs leaves the session a stdout nobody reads, so a program writing to it dies of EPIPE.
i3lock-color provides i3lock, so with it still installed the host saw i3lock as present,
skipped the install and then removed the only locker; removing it first lets the same
apply install i3lock.
2026-10-04 15:09:39 +02:00
jochen 12ee8f41d9 Merge branch 'feat/phase-2-desktop-core' of /tmp/claude-1000/-home-jochen-projects-novox-hq/a2753bc7-871c-4aac-b6b6-21e3919ee6cd/scratchpad/wg/mesh-catalog into HEAD 2026-10-04 14:56:11 +02:00
jochen a44a1fc51e adwaita: the theme as a module, dark by default, for GTK, Qt, the portal and the cursor (hq ADR 0208)
GTK 3/4 settings, qt6ct, portals.conf and the default cursor as owned files.
GTK_THEME, GTK2_RC_FILES, the Qt words and XCURSOR_* as environment
contributions. The GSettings keys the portal serves go in the xinitrc slot,
replacing the predecessor's appearance script, and the cursor in the
xresources slot.

Qt is drawn by Fusion with qt6ct's darker palette instead of the
user-repository adwaita-qt, which is no longer developed. qt5ct is dropped. The
fonts are research 026's Inter and JetBrains Mono, and portals.conf routes the
Secret interface to gnome-keyring, which nothing answered.

The tools are appearance (dark or light per audience, switched for the session),
cursor, icons, and portal-check (which backend answers which interface, and why).
2026-10-04 13:21:47 +02:00
jochen e4abc6eb88 xterm: the terminal holds node-terminal-emulator; its resources through xorg's slot (hq ADR 0208)
It requires x11-display, names itself in TERMINAL, and contributes its X resources
to the xresources slot normal: today's palette and clipboard keys, JetBrainsMono
Nerd Font, 10000 lines of scrollback, all scoped to XTerm* instead of every Xt
program. It owns no file.

The tools are the seat's open, which starts a terminal through the account's
service manager so it outlives the runtime's restarts, and font (in force, what
fontconfig resolves it to, set for new terminals) and colours.
2026-10-04 13:21:47 +02:00
jochen 34829e39fe i3: the window manager holds node-display-session; its config improved and a checked reload watcher (hq ADR 0208)
It requires x11-display and contributes exec i3 to xinitrc's last slot, and
XDG_CURRENT_DESKTOP/XDG_SESSION_DESKTOP to the environment. It owns
~/.config/i3/config, ending with the config.d include where other modules drop
their files, and /etc/lemurs/wms/i3, which runs the session's start.

Over today's identical config it drops the dead lxpolkit, the D-Bus-activated
portal, the xrdb merge xorg now does, and the execs that XDG autostart already
started. It sets JetBrainsMono Nerd Font, runs i3-sensible-terminal, and declares
dex, which the desktop lacked.

The tools speak i3's IPC: the seat's reload, workspaces and windows, and focus,
move, layout save/restore, exec, kill, bindings, config check, marks and the
scratchpad. A watcher in the bundle (ADR 0198) replaces the predecessor's inotify
script and user unit. It reloads only a configuration i3 -C accepts, and at its
start whatever i3 has not loaded.
2026-10-04 13:21:47 +02:00
jochen 98eb3fe33c lemurs: the login manager holds node-login-manager, its config in the current format (hq ADR 0208)
The official package in place of lemurs-git, and /etc/lemurs/config.toml in lemurs
0.4's structure. It offers only the session scripts that modules place in
/etc/lemurs/wms and /etc/lemurs/wayland, never a package's bare desktop entry, which
skips the session's start. The service is enabled and never started, stopped or
restarted by a push.

The tools are the seat's sessions, the default session (lemurs's cache, through
sudo -n) and logins from the journal and lemurs's own log. The package swap from
lemurs-git is a one-off step for the operator, listed in the README.
2026-10-04 13:21:47 +02:00
jochen b2c170acda xorg: the display server holds node-display-server and writes the session's start (hq ADR 0208)
The X server, its start and its tools as one module. It provides x11-display with
the machine's reach, gated by the host's seat capability. It writes a block at the
start of ~/.xinitrc: the account's environment, an explicit import into the user
manager, the mesh's X resources merged without cpp, autorandr, the xinitrc slots,
~/.xinitrc.local, and the session's exec from the last slot.

Its Go tools serve the seat's displays and layout (autorandr profiles keyed by
EDID), and set-mode, primary, dpi, input devices and settings, keyboard, a
screenshot (xwd decoded in Go) and the server's log. internal/desktop is how
every desktop tool finds the operator's session from the runtime, which has none:
from the session's own processes, reading only its words, confirmed with logind.
2026-10-04 13:21:47 +02:00
jochen 6d06648bc1 i3status-rust: no domain names in the icon file's comments (the catalogue check refuses them) 2026-10-04 13:16:02 +02:00
jochen e810afb3eb gnome-keyring: the secret service as a module, claiming node-secret-service (hq ADR 0208, ADR 0102)
PAM lines written into login and passwd as blocks, so login unlocks the keyring
on both workstations; no daemon of its own; gcr's ssh agent named for the session
until the environment can say a runtime-directory path. Go tools unlocked, lock,
collections and ssh-keys, never reading a secret.
2026-10-04 13:15:39 +02:00
jochen 838e1c2616 i3status-rust: the bars as a module, claiming node-bar (hq ADR 0208)
Owns both bars and their icons, the bar blocks as an i3 drop-in; the battery,
GPU and headset blocks leave (hardware and a person's devices), the weather needs
no key; the update count and the bar watchdog become module code, the watchdog
started once per session. Go tools reload, blocks, block-run and themes.
2026-10-04 13:15:39 +02:00
jochen 91c6fa6fce feh: the wallpaper as a module, its image an archive of its own (hq ADR 0208, ADR 0205)
~/.fehbg stops pointing into the predecessor's tree; the session's xinitrc slot
runs it once; Go tools set (for the session) and current.
2026-10-04 13:15:39 +02:00
jochen c42cd285e2 clipmenu: the clipboard manager as a module, claiming node-clipboard and serving history and copy (hq ADR 0208)
Replaces the AUR greenclip (declared absent) with the official clipmenu, started
once from the session's xinitrc slot, its menu the launcher's dmenu command bound
as an i3 drop-in, its history in the runtime directory. Go tools read and change
clipmenu's own store under its lock.
2026-10-04 13:15:39 +02:00
jochen 0fa90e5cf2 screen-lock: the lock screen as a module, claiming node-lock-screen and serving lock (hq ADR 0208)
The distribution's i3lock behind a locker that releases xss-lock's sleep lock
once it is up; timeouts and xss-lock from the session's xinitrc slot, ending
with the session; i3lock-color and xscreensaver declared absent; Go tools lock,
idle, inhibit and locked.
2026-10-04 13:15:39 +02:00
jochen 8bbea4a2ad dunst: the notifier as a module, claiming node-notifier and serving send and history (hq ADR 0208)
Owns one dunstrc (the laptop's, in the interface face, its menu on the seat's
dmenu command) and the dunstrc.d directory for other modules' rules; D-Bus
starts it, so nothing else does. Go tools over the session bus.
2026-10-04 13:15:39 +02:00
jochen f0f0a79623 rofi: the launcher as a module, claiming node-launcher and serving menu (hq ADR 0208)
Places the seat's dmenu-compatible command, the launcher and power menu, its
themes in the decided faces, and its key bindings as an i3 drop-in; Go tools
menu, applications, themes and run.
2026-10-04 13:15:39 +02:00
jochen 04894e6618 picom: the compositor as a module, claiming node-compositor (hq ADR 0208)
Owns its configuration, moved to picom's window rules; started once from the
session's xinitrc slot; Go tools restart, rules, window-opacity and toggle, which
find the operator's X session from the window manager's environment.
2026-10-04 13:15:39 +02:00
mesh-admin b8982b4a7c Merge pull request 'Workstation basics: fonts, docker-compose, snapd, flatpak, cups, bluetooth, xclip, dmenu (hq to-be 42 phase 2), tools in Go' (#269) from feat/phase-2-workstation-basics-rebased into main 2026-10-04 11:02:42 +00:00
jochen 838a6e510b dmenu: the package, which makes the notifier's menu work, and a menu tool (hq to-be 42 phase 2.7)
Research 026/04 counted two plain dmenu calls failing with dmenu installed
nowhere. Measured, they are one line on each workstation: dunst's
`dmenu = /usr/bin/dmenu -p dunst:`. Installing the package fixes both by
existing; the line stays the dunst module's.

No claim: node-launcher is not in the controller's seat table yet, and the
module says so. Two Go tools: menu, shaped like that seat's verb (chosen
line, index, typed, cancelled, timed out within 25 s), and session.
2026-10-04 13:02:23 +02:00
jochen b1b7e58e4b xclip: the package, and the operator's clipboard from the mesh (hq to-be 42 phase 2.7)
A package and nothing else, the tool the desktop's scripts depend on.
Four Go tools: copy, paste (text, base64 for other types, empty when
nothing), targets and session.

The runtime is given no session words, but runs as the account in the
machine's own namespace, so the session is found rather than configured:
the process's DISPLAY, else the account's processes' DISPLAY and XAUTHORITY
from /proc (the window manager's first), else the only X socket with
~/.Xauthority. Measured with both variables unset: :1 found through i3, the
server answered. With no session every tool says so and runs nothing.
2026-10-04 13:02:23 +02:00
jochen 3b43a1ef4d bluetooth: the stack, its daemon, and the devices as tools (hq to-be 42 phase 2.9)
bluez and bluez-utils, and bluetooth.service running and enabled. On both
workstations bluez is installed only as a dependency; declaring it keeps a
clean-up from taking it.

Nine Go tools over bluetoothctl: controller, power, devices with battery
where reported, a bounded scan, connect, disconnect, trust, pair (an agent
that confirms nothing, for headphones) and remove. An act whose output says
it failed is an error whatever the exit status, and one bluez refuses the
account is repeated through sudo -n.
2026-10-04 13:02:23 +02:00
jochen b352f3920e cups: the scheduler and driverless printing, and the printers as tools (hq to-be 42 phase 2.9)
Research 027 asked for cups with the printer's driver. Measured: both
Brother queues already print through IPP Everywhere, so cups and
cups-filters are the whole driver, and the AUR vendor packages beside them
serve no queue. The desktop's Canon is the exception: its 2012 driver is
AUR-only and waits for the mesh's package repository; it stays as found.

Seven Go tools: printers (state, device, driverless or not, supply levels),
queue, cancel (the account first, sudo -n when CUPS refuses it), print,
default, resume, and drivers (which packages bring drivers, which are
foreign, which no queue uses).
2026-10-04 13:02:23 +02:00
jochen 152ef9621d flatpak: the package, Flathub with it, and the installations as tools (hq to-be 42 phase 2.9)
The package ships Flathub in /usr/share/flatpak/remotes.d, so the module
declares no remote of its own and checks it instead. Eleven Go tools: list,
runtimes, remotes (naming the desktop's duplicate user Flathub), updates,
unused, disk usage, and install, remove, update and remove-unused, the
system installation's acts through sudo -n and the account's without.

uninstall --unused has no dry run, so flatpak_unused works it out from
flatpak's own answers: an application's runtime and SDK, the extension
points of what is used, and pins. Acts run as jobs inside the bundle,
because an install outlasts a call.
2026-10-04 13:02:23 +02:00
jochen db297e8bdd snapd: tools for the snaps, the package blocked on the mesh's AUR repository (hq to-be 42 phase 2.9)
snapd is not in the official repositories, and ADR 0205's archive does not
fit a daemon with setuid helpers, so the module declares nothing until
research 027 question 1 (P2) builds it into the mesh's own repository. Not
even its units: on the laptop they do not exist, and the module would fail
there.

Ten Go tools that work wherever snapd is installed and say so where it is
not: status (with AppArmor's absence from the kernel named), list, info,
updates, disk usage with the disabled revisions' share, services, changes,
and install, remove and refresh through sudo -n with --no-wait, answering
snapd's change id.
2026-10-04 13:02:23 +02:00
jochen 45befbbd02 docker-compose: the package, and its projects as tools (hq to-be 42 phase 2.9)
Compose and nothing else, for the two workstations, where it is already
installed by hand; the runtime, buildx and the group stay the docker
module's. Nine Go tools: projects (with their directories from the
containers' labels), ps, logs, a rendered config with secret-looking values
redacted, and up, down, restart and pull by directory or name. Acts run as
jobs inside the bundle, waited on for 18 s and followed with
docker_compose_job, because an up that pulls outlasts a call. down never
removes volumes.
2026-10-04 13:02:23 +02:00
jochen 86cf6d438d fonts: the five decided faces as packages, and what the generic families mean (hq to-be 42 phase 2.1)
JetBrains Mono Nerd Font for monospace, Inter for the interface, the Nerd
Fonts symbols and Noto Color Emoji as fallbacks, Noto for serif. One owned
fontconfig file in the account's conf.d maps monospace, sans-serif,
system-ui, serif and emoji, bound `same`: measured with fontconfig 2.18, a
weakly bound preference loses to Noto Sans Mono. Families today's configs
name but the laptop lacks (Iosevka, the old JetBrains name) stop falling
back to sans-serif.

Six Go tools: families, match, glyph, sources (which hand-copied files can
go and why), config, cache-rebuild. The README lists the hand-copied files
to remove and the modules that must name the new family.
2026-10-04 13:02:23 +02:00
mesh-admin 4d028d40c5 Merge pull request 'Phase 1 system modules: sudo, localization, time-sync, pacman, logrotate, avahi (hq to-be 42), tools in Go' (#268) from feat/phase-1-system-modules-rebased into main 2026-10-04 10:50:38 +00:00
jochen 5c212531da avahi: the discovery daemon declared, and why it hears nothing reported
On all four machines and owned by none. The module declares the package and
the daemon. It leaves nsswitch.conf and nss-mdns as found — the hosts: line is
one list every name source shares, and the host writes blocks, not line
members — and opens nothing: the mesh's filter drops inbound UDP 5353 on every
machine and `listens` has no local-link scope. avahi_status, _browse, _resolve
and _services report both (to-be 42 Phase 1).
2026-10-04 12:50:20 +02:00
jochen 2e082d1680 logrotate: rotation on every machine, its base configuration owned
Rotation ran on one machine of four; the others carried package and fail2ban
rules nothing read, and one log had reached 4.9 GB. The module installs
logrotate, owns /etc/logrotate.conf whole (the distribution's base plus
compress/delaycompress, dropping a hand-set olddir that collides same-named
logs) and enables logrotate.timer. Seven tools from a Go bundle, the journal's
usage and vacuum among them (to-be 42 Phase 1).
2026-10-04 12:50:20 +02:00
jochen d9336d11d0 pacman: the package manager's configuration, mirrors and cache as a module
Mirrors were generated once and never again and caches never cleaned. The
module holds node-package-manager (hq ADR 0207), declares pacman itself, owns
/etc/pacman.conf whole — [options] cannot take an appended block — with the
union of the enabled repositories and improved options, proven by pacman-conf
in its test, and enables reflector.timer (its config owned) and
paccache.timer. Fifteen tools from a Go bundle; transactions run as transient
units so a call's timeout never kills pacman mid-transaction (to-be 42).
2026-10-04 12:50:20 +02:00
jochen 21d8a7f6b4 time-sync: one time daemon, timesyncd, with its servers declared
Three machines ran timesyncd and one ran ntpd. The module declares
timesyncd running with a 50-mesh.conf drop-in (European pool) and ntp absent
(hq ADR 0180). A run-once step of its Go binary stops and disables ntpd first
and takes out only dangling wants-links, so removing the package leaves no
enabled unit pointing at nothing. A provider's drop-in sorting after the
mesh's still wins and is reported, not removed. Tools: time_sync_status,
_servers, _sync_now (to-be 42 Phase 1).
2026-10-04 12:50:20 +02:00
jochen a8d308d440 localization: locale, time zone and console keymap as one module
One machine ran another time zone and a German console keymap with no record
why. The module writes /etc/locale.conf and /etc/vconsole.conf whole and sets
the zone through a run-once step of its own Go binary (timedatectl, read
back): /etc/localtime is a link the mesh may not write (hq ADR 0012) and a
module may not declare an action (ADR 0005). Tools: localization_get,
_time_zone, _locales, _keymaps (to-be 42 Phase 1).
2026-10-04 12:50:20 +02:00
jochen f015aba34a sudo: declare the operator account's passwordless escalation as a module
Three modules' tools act through `sudo -n` and nothing declared that the
account may; each machine said so in a hand-set line in /etc/sudoers. The
module owns the package and /etc/sudoers.d/10-mesh-operator (0440), checked
by visudo in its manifest test, and serves sudo_rules, sudo_check and
sudo_drop_ins from a Go bundle. lab stops declaring the sudo package, which
would collide with this module on the node that runs both (hq ADR 0207,
to-be 42 Phase 1).
2026-10-04 12:50:20 +02:00
mesh-admin 44aafc9b1c Merge pull request 'docker: the container runtime as a module, holding node-container-runtime, tools in Go (hq ADR 0207, to-be 42)' (#267) from feat/docker-module into main 2026-10-04 10:44:38 +00:00
jochen 0d72c3f29a docker: the container runtime as a module, with its tools in Go
Claims node-container-runtime (ADR 0207). Owns the packages, the socket and a weekly
prune of dangling images and unused build cache. Serves 18 tools over every container,
marking the mesh's. daemon.json, docker.service and the docker group are left to a
proposed change: dnsmasq and zsh declare them today, and the controller refuses a
second declaration (README).
2026-10-04 12:43:51 +02:00
mesh-admin 3ac7c0289e Merge pull request 'ssh-client: the mesh's region first in ~/.ssh/config, its hosts in config.d, tools in Go (hq research 027/03, to-be 42)' (#266) from feat/ssh-client-owns-ssh into main 2026-10-04 10:38:56 +00:00
jochen dde9c264f5 ssh-client: the mesh's region first in ~/.ssh/config, its hosts in config.d, tools in Go
The region at the end let earlier Host lines win over the mesh's (research 027/03). A
roster fact cannot be placed at the start, so the region holds one Include of config.d,
and the hosts are config.d/00-mesh, read first. Eight tools; authorized_keys and
known_hosts stay found until the controller holds those facts.
2026-10-04 12:38:40 +02:00
mesh-admin 24f11f2138 Merge pull request 'The licence manager binds a node reporting an account it already holds' (#265) from fix/a-reporting-node-is-bound-to-its-account into main 2026-10-04 10:34:52 +00:00
jochen af63f12129 The licence manager binds a node reporting an account it already holds
Found going live: the other nodes report the adopted account with older
logins, which are never candidates, and the first binding was only made at
adoption — so a node reporting afterwards was never bound (ADR 0206 §7).
2026-10-04 12:34:39 +02:00
mesh-admin a72df57214 Merge pull request 'photos authenticates against the database its user lives in (hq issue 232)' (#264) from fix/photos-authenticates-against-its-own-database into main 2026-10-04 10:34:16 +00:00