Files
mesh-catalog/modules/pacman/README.md
T
jschoubben 2b495c4818 pacman keeps one mirror list for the mesh, ranked in the write that opens each round and written by every node (ADR 0585, issue 1876)
Each node's reflector ranked its own list on its own timer, and on 2026-10-31 one node's
lagging first mirror left it a Node.js version behind the rest. The list is now the
module's state mirrors, key list: the node that opens a week's round ranks it with the
module's reflector options, keeps only mirrors whose last sync is no older than the
newest found, and the round's key names the list's revision. Every keeper writes
/etc/pacman.d/mirrorlist whole from the key through the root helper's new verb
mirrorlist, when the key changes and when the file differs, and never writes an empty
or unreadable list. reflector.timer is declared stopped and disabled, and reflector.conf
saves nothing. pacman_mirrors answers the shared list, its ranker and age and whether
this node's file matches; with refresh it asks this node's keeper to rank again,
refused while a round runs, naming it. The round's integration test proves the opening
write and both nodes' files.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 16:18:22 +01:00

9.9 KiB

pacman

The package manager as a module (novox/hq to-be 42 Phase 1, ADR 0207, research 027). It holds the node-package-manager seat, which has no verbs yet. Every module that declares a package depends on that seat (ADR 0207).

What it owns

  • The pacman package. A component's own package belongs to the module that holds its seat (ADR 0207).
  • /etc/pacman.conf, whole. A block cannot be added to [options] by appending: anything added at the end of the file lands in the last repository's section. So the module owns the file:
    • The repositories are the union of what the mesh's machines had enabled on 2026-10-04: core, extra and multilib. All had all three.
    • The options are the distribution's defaults, plus Color, ParallelDownloads = 5, VerbosePkgLists, and DownloadUser = alpm (pacman 7; the alpm user exists on every machine).
    • The manifest test runs pacman-conf on the rendered file and checks the repository list and the options as pacman reads them. It skips that check where pacman-conf is absent.
    • The host keeps the machine's previous file once, the first time it writes over it (ADR 0102).
  • One mirror list for the mesh (ADR 0585). The reflector package as the ranking tool, /etc/xdg/reflector/reflector.conf written whole with its options (https, Belgium, the Netherlands, Luxembourg, Germany, France, the 20 most recently synced, sorted by rate; nothing saved), and reflector.timer declared stopped and disabled. The list is the module's state mirrors, one key list: the node that opens a week's round ranks it with those options inside the write that opens the round, keeps only the mirrors whose last sync is no older than the newest found, and the round's key names the list's revision. Every node's keeper writes /etc/pacman.d/mirrorlist whole from it through the root helper when the key changes and when the file differs, so a hand edit comes back; an empty or unreadable list is never written.
  • Cache cleaning. The pacman-contrib package and paccache.timer running and enabled. Each week it keeps the last three versions of each package.
  • The package view on the bus (ADR 0507). A process of its own, the keeper (pacman-tools keeper, account pacman-keeper, its own bus account), keeps the node's packages in the state installed and what an upgrade would change in updates, written only on change: at its start, whenever pacman's database lock is released (an inotify watch), when reflector rewrites the mirror list, and once a day. Each change of the packages is package.changed. A list over the bus's cap on a value is not written: the key keeps its last value and <node>.not-current says the size and since when, which the health says too. pacman_packages, pacman_updates, pacman_installed and pacman_foreign answer every node from these states, and what no module declares from the node-engine's packages (read as mesh-host.packages, the engine's manifest's name), else the controller's declared-packages until its lease is written, without asking a machine.
  • Upgrades as runs, one node at a time (ADR 0508). Every full upgrade and every rollback is a run in the state runs at <node>.<run>, written at each step and emitted as it: the package record taken and read back (records, kept record-days), the DKMS gate before the transaction (a kernel an installed DKMS module's own Linux-Maximum or BUILD_EXCLUSIVE_KERNEL excludes is held with its headers, upgrade.kernel-held), the transaction as a unit of its own, its end, and the after-upgrade check (.pacnew/.pacsave files, the news since the previous upgrade, units newly failed, a restart needed, processes still mapping a replaced file by unit and executable, DKMS modules not built for an installed kernel). pacman's log is read once into runs as history.
    • The round. The nodes upgrade in the order of the mesh-level key upgrade-order (a trusted list, predefined.json; the module's config value of the same name falls back to it). On Saturday at 04:00 local time the first node of the order opens the week's round in rounds; each node's keeper watches rounds and runs and starts its own run when the node before it passed its after-upgrade check. A failed run or check stops the round there; only the operator passes a node (pacman_round with pass and a reason asks on the operator's channel, and the round goes on only on the operator's Approve). With no order set no round opens.
    • Acts are asks. pacman_upgrade and pacman_rollback with apply write an ask in asks at <node>.<id>, which that node's keeper alone takes.
    • The root helper (ADR 0510). The keeper never runs as root. The acts that need root go through a second process of the same build, pacman-root (pacman-tools root), running as root with no bus account, on a unix socket in ${dir:state}/root/ that only the keeper's account may open, the peer's account checked on every connection. It serves four verbs and refuses everything else: start a run's full upgrade as the unit mesh-pacman-upgrade-<run> (leaving out held packages), start a rollback's unit with the plan in the request (packages from the cache or the Arch Linux Archive, the dated repository's file found by name), read which processes map a replaced file, and read a transaction unit's journal. No sudo rule is placed.
    • The health is read from the bus. The keeper's health check (pacman_upgrade_health) is unhealthy while no upgrade-order is set; while the node's latest run was refused, failed or did not pass its check (until a later run passes); while its transaction goes on past two hours (hung, said and not killed); while a kernel is held; and while the round stands still at this node or at the node it waits for, naming it and since when. A node that slept through a run is judged by what the bus holds, not by a unit's age (issue 1579). The weekly timer of ADR 0477 §5 and its service are dropped from the manifest, so the node-engine removes them.

What it improves

  • Mirrors were generated once and never again: in 2022, 2023 and 2024, and on one machine by its hosting provider's installer. Then each node's reflector ranked its own list weekly, and on 2026-10-31 one node's lagging first mirror left it a version behind the rest. Now every node of a round upgrades from one list ranked when the round opened; pacman_mirrors with refresh: true ranks it again for the mesh, refused while a round runs.
  • Package caches were never cleaned. One workstation held 48 GB, of which paccache would free 33 GB.
  • Every machine has the same options. Only one had parallel downloads.

What it leaves found

  • The stale mirrorlist.pacnew, .bak, .original and similar copies beside the mirror list. Until the first round opens (or a refresh), each node keeps the mirror list it has.
  • /etc/pacman.d/hooks, the keyring, and the AUR helper. Packages from outside the repositories are research 027 question 1.

Tools

tool answers
pacman_search r pacman -Ss: repository, name, version, groups, installed and at which version, description
pacman_info r -Qi, or -Si when not installed, with lists as lists
pacman_installed r every package with version, explicit or dependency, foreign; filters and totals, from installed on the bus
pacman_packages r every node's packages from the bus: one package everywhere, differing versions, packages on some nodes only, each node's explicit packages no module declares, foreign apart, nodes not current
pacman_owns r which package owns a path, or owned: false
pacman_files r what a package placed, bounded
pacman_updates r what a full upgrade would change, for one node or all, from updates on the bus
pacman_upgrade a asks a node's keeper for a full upgrade as a run (an ask in asks); answers the ask and the news since the last upgrade; given a transaction unit, how it goes
pacman_upgrade_health r the keeper's health check, read from the bus (see the health above)
pacman_upgrades r every run of every node from runs: narrowed to a node and days, or one run whole
pacman_round r/a where every node stands in the newest rounds; pass with a reason asks the operator to let a round go on past a node
pacman_rollback r/a what returning a node to a run's record would change, each package with its source; apply asks the node's keeper to run it
pacman_orphans r pacman -Qdt
pacman_remove_orphans a pacman -Rs on named orphans, or all of them, as a unit of its own; a name that is not an orphan is refused
pacman_cache r/a size, interrupted downloads, what paccache would free keeping N; clean: true removes them
pacman_history r /var/log/pacman.log: installs, upgrades, downgrades, reinstalls and removals since a day, and the last full upgrade
pacman_mirrors r/a the mesh's list, who ranked it and its age, and whether this node's file matches; refresh: true ranks it again for the mesh, refused during a round
pacman_foreign r the foreign packages, from installed on the bus
pacman_news r the distribution's news since the last full upgrade (or a day), over https; no network is reachable: false
pacman_config r pacman-conf: options, repositories, and whether /etc/pacman.conf is the module's

A transaction never runs as the tool's own child. An upgrade takes longer than the 20 s a call may take, and a pacman killed mid-transaction leaves a half-upgraded machine and a lock. So a transaction runs as a transient unit (systemd-run, named mesh-pacman-…), and its log is read from the journal. A run's transaction is started by the root helper; removing orphans and cleaning the cache still go through sudo -n from the tools bundle.