Skip to content

Add systemd --user backend for Atlas on Linux - #1743

Open
schmetti-dev wants to merge 1 commit into
danielmiessler:mainfrom
schmetti-dev:atlas-systemd-backend
Open

Add systemd --user backend for Atlas on Linux#1743
schmetti-dev wants to merge 1 commit into
danielmiessler:mainfrom
schmetti-dev:atlas-systemd-backend

Conversation

@schmetti-dev

Copy link
Copy Markdown

Summary

Atlas's launchd collector and its com.lifeos.atlas background tick service had no Linux equivalent — the asset graph couldn't discover background services or schedule its own reconciliation on Linux at all. Same shape as #1692 (WorkSweep's systemd port): the gap is in the platform-specific scheduling/discovery layer, not the core logic.

  • collectors/Systemd.ts (new) — reads ~/.config/systemd/user/com.{lifeos,pai}.*.{service,timer} unit files directly (plain-text parse, no subprocess — OnUnitActiveSec/OnBootSec/Persistent are just INI keys in the .timer file). Registered alongside launchd in Atlas.ts. Produces the identical asset/edge shape (service + RUNS_ON) so blast/owns/exposed queries work the same regardless of platform.
  • InstallAtlas.ts + com.lifeos.atlas.{plist,service,timer}.template (new) — Atlas had no installer for its own tick service on either platform. Added both backends, mirroring InstallWorkSweep.ts's pattern: bun+gh PATH detection (so the github collector's gh calls resolve inside the unit's restricted PATH) and loginctl enable-linger via id -un so the Linux timer survives logout.
  • AtlasEventCapture.hook.ts — added the systemd-equivalent Bash/file matchers (systemctl --user commands, unit-file edits under ~/.config/systemd/user/) alongside the existing launchctl ones.
  • Launchd.ts — small robustness fix found while testing this on a real Linux box: plutil is only called if AGENTS_DIR exists, but a directory existing doesn't imply the platform's own tool does (a Linux machine can have stray com.lifeos.*.plist files under ~/Library/LaunchAgents — e.g. left over from cross-platform experimentation — with no plutil binary to read them). Wrapped the spawn in try/catch, degrading to null the same way Github.ts already treats a missing gh — absent tool is a normal state, not a fault this collector should throw on.
  • AtlasSystem.md — documented the new collector and the cross-platform installer.

Testing evidence

Verified end-to-end on a real Linux host (Ubuntu, systemd):

$ bun ~/.claude/LIFEOS/ATLAS/InstallAtlas.ts
[InstallAtlas] detected bun at /home/.../bun, gh at /home/.../gh
[InstallAtlas] wrote /home/.../.config/systemd/user/com.lifeos.atlas.service and .../com.lifeos.atlas.timer
[InstallAtlas] systemd timer enabled — com.lifeos.atlas.timer active
[InstallAtlas] com.lifeos.atlas.timer state: active

$ systemctl --user list-timers com.lifeos.atlas.timer --no-pager
NEXT                          LEFT LAST                               PASSED UNIT
Mon ... 09:42:47 CEST        13min Mon ... 09:27:47 CEST 1min 50s ago com.lifeos.atlas.timer com.lifeos.atlas.service

$ bun ~/.claude/LIFEOS/ATLAS/Atlas.ts sync
✓ systemd: 15 assets, 14 edges (run 19, complete, swept)
[... other collectors ...]

$ bun ~/.claude/LIFEOS/ATLAS/Atlas.ts tick
tick: nothing to do

Also confirmed the Launchd.ts fix: before the try/catch, atlas sync threw Executable not found in $PATH: "plutil" on a Linux box with 2 stray com.lifeos.*.plist files under ~/Library/LaunchAgents; after the fix it degrades cleanly (complete: false) instead of aborting the whole sync run.

The macOS/launchd path is unchanged — only new if (IS_LINUX)-shaped branches were added (same invariant #1692 documented), and Launchd.ts's fix only changes behavior on the ENOENT path, which never fires when plutil is actually present.

🤖 Generated with Claude Code

Atlas's launchd collector and its com.lifeos.atlas background service
had no Linux equivalent - the asset graph simply couldn't discover
background services or schedule its own tick on Linux, mirroring the
gap PR danielmiessler#1692 fixed for WorkSweep.

- collectors/Systemd.ts: reads
  ~/.config/systemd/user/com.{lifeos,pai}.*.{service,timer} unit files
  directly (no subprocess - OnUnitActiveSec/OnBootSec/Persistent are
  plain text in the .timer file), registered alongside launchd in
  Atlas.ts. Same asset/edge shape as the launchd collector (service +
  RUNS_ON), so blast/owns/exposed queries work identically regardless
  of platform.
- InstallAtlas.ts + com.lifeos.atlas.{plist,service,timer}.template:
  Atlas had no installer for its own tick service at all (macOS or
  Linux) - this adds both backends, mirroring InstallWorkSweep.ts's
  pattern (bun+gh PATH detection so the github collector resolves gh
  from the unit's restricted PATH, `loginctl enable-linger` via
  `id -un` so the Linux timer survives logout).
- AtlasEventCapture.hook.ts: added the systemd-equivalent Bash/file
  matchers (systemctl --user commands, unit-file edits under
  ~/.config/systemd/user/) alongside the existing launchctl ones.
- Launchd.ts: plutil is only called if AGENTS_DIR exists, but a
  directory existing doesn't imply the platform's own tool does (found
  testing this PR: a Linux box can have stray com.lifeos.*.plist files
  under ~/Library/LaunchAgents with no plutil to read them). Wrapped
  the spawn in try/catch, degrading to null the same way Github.ts
  already treats a missing `gh` - absent tool is a normal state, not a
  fault this collector should throw on.

Verified end to end on a real Linux host: full atlas sync picks up
the systemd collector's services correctly, `atlas tick` and the
installed timer both confirmed working via `systemctl --user
list-timers`, IntegrityCheck-equivalent manual review found no
regressions on the darwin path (only new `if (IS_LINUX)`-shaped
branches were added, same invariant danielmiessler#1692 documented).
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.

1 participant