Skip to content

daemon: honour XDG layout when run as an installed service#106

Merged
zzet merged 1 commit into
mainfrom
fix/service-xdg-env-propagation
Jun 16, 2026
Merged

daemon: honour XDG layout when run as an installed service#106
zzet merged 1 commit into
mainfrom
fix/service-xdg-env-propagation

Conversation

@zzet

@zzet zzet commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Problem

Fixes #105.

gortex daemon install-service writes a launchd plist (macOS) / systemd --user unit (Linux) that propagated only PATH into the supervised process. launchd and systemd --user start the daemon with a near-empty environment — they do not inherit the XDG_* variables a user exports from their shell or session manager (e.g. nix/home-manager).

Path resolution (internal/platform/xdg.go) reads those variables at runtime, so with them absent the daemon falls back to ~/.gortex even when the user opted into an XDG layout. Net effect from the issue's repro: gortex install (run in the XDG-configured shell) correctly writes under $XDG_CONFIG_HOME/gortex, but starting the service then creates ~/.gortex — silently splitting state across two trees.

This affected both macOS launchd and Linux systemd --user (the systemd unit had no Environment= lines either).

Fix

At install time — which runs in the user's shell, where the variables are present — capture the absolute XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME values and bake them into the service unit's environment:

  • launchd: added to the plist EnvironmentVariables dict (alongside PATH).
  • systemd: emitted as Environment= lines in [Service].

Details:

  • Absolute-only, matching platform.unifiedDir (a relative XDG path is ignored per the XDG Base Directory spec).
  • XDG_RUNTIME_DIR is intentionally excluded — the init system sets it per session; pinning an install-time value would point the Linux socket at a stale runtime dir.
  • Rendering moved into pure renderLaunchdPlist / renderSystemdUnit helpers with format-correct escaping: XML-escape plist string values; for systemd, escape %%% (specifier introducer) and double-quote whitespace values.
  • Re-running install-service re-captures changed values (documented in docs/onboarding.md and in code).

No behaviour change for users without an XDG override: with nothing captured the plist/unit render exactly as before (PATH-only / no Environment=).

Note on the issue's suggested behaviour

The report suggested the daemon "check if a config exists under the XDG path." That isn't possible without the variable set — Gortex deliberately uses a unified ~/.gortex unless an absolute XDG_*_HOME is explicitly present, so the env var is the only signal of intent. This PR makes that signal survive into the service, which is the capture approach the issue's own footnote anticipated.

Tests

  • cmd/gortex/daemon_service_test.go: render helpers exercised for the no-XDG baseline (no stray env / blank lines) and the populated case (regression test for the bug), XML-escaping, systemd whitespace-quoting and %-escaping, and the xdgServiceEnv absolute-only filter. Plist output asserted to be well-formed XML.
  • go build ./cmd/gortex/ (CGO), go vet, golangci-lint, and go test -race of the touched tests all green.

`gortex daemon install-service` wrote a launchd plist / systemd --user
unit that propagated only PATH. launchd and systemd --user start the
daemon with a near-empty environment — they do not inherit the XDG_*
variables a user exports from their shell or session manager. Path
resolution reads those variables at runtime, so the supervised daemon
fell back to ~/.gortex even when the user had opted into an XDG layout,
silently splitting their state across two trees.

Capture the absolute XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME
values in effect at install time and bake them into the service unit's
environment, applying the same absolute-only rule platform.unifiedDir
uses. XDG_RUNTIME_DIR is intentionally excluded — the init system sets
it per session. Re-run install-service to re-capture changed values.

Rendering moves into pure renderLaunchdPlist / renderSystemdUnit helpers
with format-correct escaping: XML-escape plist string values, and escape
systemd Environment= values (%% specifier escape, plus double-quoting
for whitespace).
@zzet zzet merged commit 67f83de into main Jun 16, 2026
10 checks passed
@zzet zzet deleted the fix/service-xdg-env-propagation branch June 16, 2026 22:32
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.

XDG setup ignored by daemon when run as service

1 participant