# 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//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.