gui: one shared dbus session bus for the gui session (#1906)

This commit is contained in:
damocles 2026-06-24 00:06:07 +02:00
commit baa5ce3e1a
2 changed files with 62 additions and 5 deletions

View file

@ -270,6 +270,19 @@ connects to the compositor at `127.0.0.1:<vnc_port>`.
wayland client sharing the `/run/gui/wayland-0` socket). Wayland
clients in the agent's config (e.g. bitburner electron) must run as the
same user with `XDG_RUNTIME_DIR=/run/gui`.
- **One shared D-Bus session bus (`gui-dbus.service`)**: a single
persistent `dbus-daemon --session` bound at `/run/gui/bus`, run as the
agent user, ordered `before weston.service` (it shares the same
`RuntimeDirectory=gui`, creating the dir first). Chromium/electron via
ozone refuse to map an `xdg_toplevel` without a reachable session bus
("Failed to connect to the bus" → binds `xdg_wm_base` then destroys it
= invisible window even though CDP works). The fix is **not** to wrap
each client in its own `dbus-run-session` (a private throwaway bus per
process — that's a *separate* session, defeating the one-session
model); it's this one shared bus, whose address is exported as
`DBUS_SESSION_BUS_ADDRESS=unix:path=/run/gui/bus` via
`systemd.globalEnvironment` so weston, the harness and every GUI client
inherit it.
- **Fixed Wayland socket name (`--socket=wayland-0`)**: weston is
launched with `--socket=wayland-0` so the socket path is
deterministic. `harness-base.nix` exports `WAYLAND_DISPLAY=wayland-0`

View file

@ -55,9 +55,11 @@ in
`Type = "simple"` so a misconfigured weston degrades to a restart
loop instead of blocking `nixos-container update`.
Weston and the wayland client run as the agent's own non-root
user (`hyperhive.user.name`), sharing one user session with a
fixed `XDG_RUNTIME_DIR=/run/gui`.
Weston, the wayland client and the agent harness run as the
agent's own non-root user (`hyperhive.user.name`), sharing one
session: a fixed `XDG_RUNTIME_DIR=/run/gui`, one wayland display,
and one D-Bus session bus at `/run/gui/bus` (gui-dbus.service),
so GUI clients need no private `dbus-run-session`.
'';
};
@ -100,9 +102,48 @@ in
session sufficient pam_permit.so
'';
# One shared D-Bus *session* bus for the whole GUI session, bound at
# /run/gui/bus and owned by the agent user. Wayland GUI clients
# (chromium / electron via ozone) refuse to map a toplevel without a
# reachable session bus ("Failed to connect to the bus" -> binds
# xdg_wm_base then destroys it = invisible window, though CDP still
# works). Running ONE persistent bus here -- instead of each client
# wrapping itself in `dbus-run-session` (a private throwaway bus per
# process) -- keeps weston, the agent harness and the GUI client in a
# single session: one user, one XDG_RUNTIME_DIR, one wayland display,
# one bus. The address is exported via globalEnvironment below so
# every unit in the container inherits it.
systemd.services.gui-dbus = {
description = "Shared D-Bus session bus for the GUI session";
wantedBy = [ "multi-user.target" ];
before = [ "weston.service" ];
serviceConfig = {
Type = "simple";
User = userName;
Group = userName;
# Share /run/gui with weston; ordered `before` weston so this
# creates + chowns the dir first. Preserve across restarts so
# weston + clients don't lose the dir holding their sockets.
RuntimeDirectory = "gui";
RuntimeDirectoryMode = "0700";
RuntimeDirectoryPreserve = "yes";
Environment = "XDG_RUNTIME_DIR=/run/gui";
# dbus-daemon unlinks a stale path socket before binding, so a
# restart re-binds /run/gui/bus cleanly.
ExecStart = "${pkgs.dbus}/bin/dbus-daemon --session --nofork --nopidfile --address=unix:path=/run/gui/bus";
SyslogIdentifier = "gui-dbus";
Restart = "on-failure";
RestartSec = "2s";
};
};
systemd.services.weston = {
description = "Weston Wayland compositor (VNC backend)";
after = [ "network.target" ];
after = [
"network.target"
"gui-dbus.service"
];
wants = [ "gui-dbus.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "simple";
@ -155,10 +196,13 @@ in
# to systemd service units. Without these vars a service starting
# a Wayland client would either fail to connect (libwayland falls
# back to a headless display) or open a second compositor — VNC
# shows a blank desktop.
# shows a blank desktop. DBUS_SESSION_BUS_ADDRESS points every unit
# at the one shared session bus (gui-dbus.service above) so GUI
# clients use it instead of spawning a private `dbus-run-session`.
systemd.globalEnvironment = {
WAYLAND_DISPLAY = "wayland-0";
XDG_RUNTIME_DIR = "/run/gui";
DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/gui/bus";
};
# weston on the agent's interactive PATH so claude can run Wayland