Files
mesh-catalog/modules/dbus
jochen 7b5e1d3362 dbus: hold node-message-bus, and never restart the bus live (hq ADR 0215)
A live restart of the system bus during an upgrade hung every login on a
workstation until a reboot. The module owns the bus's packages, declares
the bus running with no restart or reload trigger, publishes only curated
events (health, services, denials; never traffic) and serves tools to look
at both buses.
2026-10-05 11:50:52 +02:00
..

dbus

A machine's message bus (novox/hq ADR 0215). This module holds node-message-bus on every machine, servers included, because every machine runs a D-Bus system bus. It owns the bus implementation's packages and declares its system service running. It publishes what matters about the bus on the mesh's bus, and serves tools to look at the system bus and at the operator's session bus.

What it owns

package dbus-broker the bus every machine runs
package dbus-broker-units dbus.service as an alias of dbus-broker.service, for the system and for each user
package dbus the bus's configuration (system.conf, session.conf), dbus.socket, which starts the bus at boot, and libdbus
service dbus-broker.service declared running, with no boot state and no restart or reload trigger (below)

All four machines were found with dbus-broker 37 behind dbus.service and dbus 1.16.2, from the distribution. One server also has an old dbus-units package, an empty package that depends on dbus-broker-units. It is not declared: nothing needs it, and removing it is a choice for its operator.

The bus is never restarted live

On 2026-10-04 a full upgrade on a workstation restarted the system bus while the upgrade was still running. From then on every login hung, sshd answered nothing and the machine's host stopped reporting, until someone rebooted it at its keyboard. Every program that speaks on the bus (the service manager, logind, the network manager, the keyring, the power module's sleep lock) holds a connection to it. A restart takes all of them away at once, and not all of them come back.

So the module never restarts or reloads the bus, for any change. Its service has no restart-on and no reload-on, and the host never restarts a service that has neither. A new bus takes effect at the next boot. dbus_check says when the running bus is older than an installed package, which is the sign that a reboot is due. The distribution's own upgrade can still restart the bus. This rule covers what the mesh does, not what pacman does.

Why state: running and no boot. dbus.service is an alias (systemctl is-enabled says alias), so it cannot be declared enabled: the host would run systemctl enable and read back something other than enabled. The real unit, dbus-broker.service, reads disabled on three machines and enabled on one. It needs no enabling. At boot dbus.socket, which the dbus package links into sockets.target, pulls in dbus.service, and dbus-broker-units ships that name as a link to dbus-broker.service. On the three machines where it reads disabled, enabling it would only write a second alias link into /etc, and a module's apply would change something on a running machine for no gain. The service is declared so that the mesh knows the bus is the module's and says so when it is not running. A host finding it stopped would start it, which can only help a machine whose bus is down. ADR 0215 §1 says "running and enabled". On these machines the package's own socket link is what makes it start at boot.

A policy change a module needs in the future uses the bus's own reload (dbus-broker.service is Type=notify-reload), which keeps every connection. No module needs one today (below).

Events

The watcher runs beside the tools in the same process, on its own connection to the system bus as the operator's account. It reads only the bus driver's answers, the driver's NameOwnerChanged signal and the bus unit's journal.

event when carries
bus.stalled the bus does not answer the driver's GetId within 3 s, or cannot be reached the reason
bus.recovered a stalled bus answers again since when it was stalled, and for how long
bus.restarted after reconnecting, the bus's id or the driver's pid has changed within the same boot (after a boot both change, and power says booted) the old and new id and pid, the unit
service.appeared a well-known name appears and is still there 10 s later the name, the owner's pid, process and unit, whether it is activatable
service.left a well-known name is gone and still gone 10 s later the name and what held it
policy.denied the bus logged a policy denial, at most once per 5 minutes how many since the last one, and up to 5 distinct examples: the refused message's type, sender, destination, path, interface and member

Unique names (:1.42) come and go with every client and are never published. A service restarted within the 10 s, or one that came and went, publishes nothing. Activatable services that exit when idle, such as hostnamed, do appear and leave, and their events say activatable: true.

Why traffic never leaves the machine. The bus carries secrets from the keyring, notification text and the clipboard. The watcher never becomes a monitor, so it never sees another peer's messages. Its events are built from names, pids, units and the header fields the broker logs with a denial. The log line itself is not passed on. The tests hold every event's body to those fields.

Events the mesh's bus does not take wait in order and go out when it answers again, as power's do. At most 1000 events wait. When there are more, the oldest are dropped and counted.

The watcher's ping is GetId, not org.freedesktop.DBus.Peer.Ping. The system policy refuses the peer ping to an account that is not root and logs a denial each time, which the watcher would then publish.

Tools

tool
dbus_names bus: system or session. Every well-known name with its owner's pid, process, user and unit, the activatable names that are not running, and the number of connections
dbus_introspect bus, service, path. The object's interfaces with their methods, properties (type and access, never values) and signals, and its children. Uses --auto-start=no, so looking never starts a service
dbus_monitor bus, seconds (at most 15), optional match rule and names. The headers of what passed (type, sender, destination, path, interface, member, error name), at most 500. Never a body: each line is decoded into the header alone. On the system bus only root may monitor, so it runs through sudo -n
dbus_check the packages are installed. dbus.service is dbus-broker.service and active. The running bus is not older than the installed dbus-broker or dbus (otherwise a reboot is due). Every activatable service file's SystemdService exists. Policy denials in the last hour. The watcher is connected, not stalled, and has nothing stuck
dbus_health the round trip of a ping on the watcher's connection, the connection count (from Debug.Stats through sudo -n, else the unique names), and the bus's unit, pid, start and uptime

Every command is bounded at 20 s and its output read up to 1 MiB. Lists stop at 500 entries. Debug.Stats is asked only as root: asked as the account it is refused and logged as a denial.

The session bus is the runtime's DBUS_SESSION_BUS_ADDRESS, else $XDG_RUNTIME_DIR/bus, else /run/user/<uid>/bus. On a machine where the account has no session, the tools say so.

The check notes, without failing, systemd's own services whose dbus-org.* alias is missing because they are not enabled, such as resolved, networkd and homed on the workstations. Activating them fails by design.

Assignment

On every machine, as the first holder of node-message-bus. The module needs nothing set: no settings, no secrets, no ports.

What comes later

ADR 0215 §4: the seat receives nothing yet. Packages ship their own D-Bus policy and service files, and no module writes one of its own. When one does, that file will be a contribution to this seat, and this module will list the kind and reload the bus's policy for it, without a restart.