Skip to content

fix: resolve the session bus instead of refusing on a stripped environment - #8

Open
timrichardson wants to merge 3 commits into
tristanmuzzu:mainfrom
timrichardson:fix/session-bus-fallback
Open

timrichardson wants to merge 3 commits into
tristanmuzzu:mainfrom
timrichardson:fix/session-bus-fallback

Conversation

@timrichardson

Copy link
Copy Markdown

Closes #7

What this fixes

MCP hosts that spawn stdio servers with a sanitized environment (Hermes passes only PATH/HOME/USER/LANG/XDG_*; other hosts filter similarly to avoid leaking credentials) strip DBUS_SESSION_BUS_ADDRESS, so the _gdbus guard refused every extension/window call with extension_unavailable — on the user's own, working desktop.

The change

  • execution.session_bus_address(): resolve the reachable bus — explicit env var, else the /run/user/1000/bus socket GLib itself falls back to, else None.
  • execution.ensure_session_bus_env(): resolve once and export, so the gdbus/gnome-extensions subprocesses inherit the bus GLib already picked in-process.
  • shell._gdbus: guard now resolves instead of refusing-before-trying. The diagnostic survives for the genuine case (no env var and no runtime-dir socket: bare ssh login, system service).
  • execution.session_key(): normalized through the resolver — hashing the raw env var handed two servers on ONE desktop different lease keys (fallback-connected vs explicit-connected).
  • desktop_health: reports the resolved address instead of set/MISSING, so it no longer cries MISSING while everything works.

Evidence

Scripted reproducer in #7 (no MCP client, no deps): on main, gdbus reaches the bus in the sanitized env while _gdbus('Ping') refuses it; on this branch the call goes through.

  • New contract tests: 6 (all fail on main — sabotage-checked by reverting deskwright/ and re-running)
  • Full fast suite: 396 passed; ruff check . clean
  • Live E2E, sanitized env, no bus var: desktop_healthREADY (extension ACTIVE, pointer absolute via Mutter.RemoteDesktop, 13 AT-SPI apps), list_windows returns 6 windows

Invariants kept

  • Explicit DBUS_SESSION_BUS_ADDRESS always wins — headless sessions (pin_env sets the private bus address) are untouched.
  • The guard still fires, with the same remedy text, when no bus exists at all.

… had nothing to talk to

A private dbus-daemon cannot activate at-spi2-registryd: the a11y broker
routes the start through org.freedesktop.systemd1, and on a bus with no
systemd that name is the stub that answers /bin/false. So the headless
a11y bus existed but no registry ever owned org.a11y.atspi.Registry, and
ui_tree/ui_find failed with 'no application named gnome-shell' while the
user's real session, whose bus has real systemd behind it, worked.

headless.py now starts registryd itself, BEFORE the shell: order matters,
because the shell's atk-bridge stops retrying registration when no
registry answers (measured: registry-after-shell = 16/18 self-test,
registry-before-shell = 18/18).

Unit tests for the spawn/liveness/stop paths run anywhere; the live proof
is tests/test_headless_atspi.py, which fails 2/3 checks on main and
passes 3/3 here, and a cold headless self-test at 18/18.
…ix for every finding

setup --check proves the machine before anything exists; the doctor proves
the RUNNING stack after install and registration, and every finding prints
its action. Born from tristanmuzzu#3, where every static check was green and even the
self-test said 16/18 while the a11y registry quietly did not exist: a
doctor has to ask the live system (does anything own
org.a11y.atspi.Registry on this session's a11y bus? does the registered
command answer MCP on stdio?), not the package manager.

Checks: session, deps (setup's probes, same install lines), the a11y flag,
the registry, the extension (files/enabled/live), the server command
(version + provenance + a real initialize/tools/list handshake), and each
running headless session's registry. --self-test adds the headless
self-test (private virtual monitor, safe unattended). Never changes
anything, never sudos; exit 0 healthy / 1 action needed / 2 not a target.

11 in-process tests (seams only, subprocess booby-trapped); verified live
on Ubuntu 26.04: correctly demands the one logout this machine still owes,
spots a pre-fix headless session and prints the restart command.
…nment

MCP hosts that spawn stdio servers with a sanitized environment (Hermes
passes only PATH/HOME/USER/LANG/XDG_*; others do the same to avoid leaking
credentials) strip DBUS_SESSION_BUS_ADDRESS, so the guard in shell._gdbus
refused every extension and window call with extension_unavailable -- on
the user's own, perfectly working desktop.

The env var is an address hint, not the mechanism: since the systemd user
bus, GLib, gdbus and AT-SPI all connect to $XDG_RUNTIME_DIR/bus when it is
absent, and XDG_RUNTIME_DIR survives every sanitized baseline. Resolve the
reachable bus once (explicit address, else the runtime-dir socket), export
it so subprocesses agree, and refuse only when neither exists -- the bare
ssh login / system service case the guard exists for.

Also normalize execution.session_key() through the resolver: hashing the
raw env var handed two servers on ONE desktop different lease keys
(fallback-connected vs explicit-connected). desktop_health now reports the
resolved address instead of crying MISSING while everything works.
@timrichardson
timrichardson force-pushed the fix/session-bus-fallback branch from fc49d6d to 99b912c Compare September 15, 2026 08:21
@timrichardson

Copy link
Copy Markdown
Author

Stacked onto feat/doctor (commit 99b912c now sits on top of #5's branch, same pattern as #5 on #4). Base stays main per this repo's convention; until #4 and #5 merge, the diff here includes their commits — merge order #4#5 → this. Suite on the stacked tree: 415 passed, ruff clean.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sanitized MCP-host environment strips DBUS_SESSION_BUS_ADDRESS; guard refuses a working desktop

1 participant