service install/uninstall: built-in cross-platform daemon setup #71

Open
opened 2026-05-23 20:55:30 +00:00 by coilysiren · 0 comments
Owner

Originally filed by @coilysiren on 2026-04-29T03:06:14Z - https://github.com/coilysiren/repo-recall/issues/31

The gap

Today the README's only documented daemon path is `brew services` (macOS only). Linux users have to write systemd units by hand. Windows users land on NSSM (a service-control-manager wrapper), which is the wrong primitive for this app — repo-recall is a single-user, loopback-only dashboard that consults user-scoped state (`~/.claude/projects/`, gh keyring, git working trees owned by the user). SCM services are system-scoped, run before login, run as a service identity by default, and don't load the user profile. That semantic mismatch caused real failures on a fresh Windows install: gh keyring inaccessible (DPAPI master keys not loaded), `git` refusing to operate (`safe.directory` ownership check), `APPDATA` unset, etc. Every workaround was downstream of "this thing wants to be a user agent, not a system service."

The macOS-side equivalent makes the framing clearer: `brew services` on macOS uses launchd user agents, not LaunchDaemons. A user agent runs only while that user is logged in, in their session, with their environment. That's exactly the semantic this app wants on every OS.

Proposal

Add `repo-recall service install` and `repo-recall service uninstall` subcommands. Platform-aware:

  • macOS — write `~/Library/LaunchAgents/com.coilysiren.repo-recall.plist`, call `launchctl bootstrap gui/`. Same semantic as `brew services start` but doesn't require the Homebrew formula.
  • Linux — write `~/.config/systemd/user/repo-recall.service`, call `systemctl --user daemon-reload && systemctl --user enable --now repo-recall`. `loginctl enable-linger` if the user wants persistence past logout (off by default).
  • Windows — create a Task Scheduler entry via `schtasks /Create /SC ONLOGON /RU $env:USERNAME /TN repo-recall /TR ` (or the COM `ITaskService` API for richer config). "Run only when user is logged on," no stored password, full profile loaded. The S4U logon type lets a task run under the user account without storing the password — direct fix for the keyring-access problem we hit.

The Brew formula stays — it's still the lightest install on macOS — but `repo-recall service install` becomes the universal advice, with the formula running it under the hood.

Citations supporting Task Scheduler over Windows Services for this shape of app

  1. LogonTrigger object | Microsoft Learn — official Microsoft docs for the primitive. "When the Task Scheduler service starts, all logged-on users are enumerated and any tasks registered with logon triggers that match the logged-on user are run." This is the direct semantic equivalent of a launchd user agent.
  2. Security Contexts for Running Tasks | Microsoft Learn — covers the S4U logon type, which lets a task run under a specific user without storing the password (`TASK_LOGON_S4U`). Exactly the gap that NSSM-as-service forces you to fill with credential storage in LSA secrets.
  3. //TODONT: Use a Windows Service just to run a scheduled process (Jon Galloway, MSDN/ASP.NET blog) — long-standing strategic argument that services are over-used when scheduled tasks are the correct primitive. The framing applies cleanly here: repo-recall doesn't need to start before login, doesn't accept external network connections, doesn't run as a system identity. Forcing it into SCM-service shape adds operational complexity without buying anything.

Bonus, for the "what does brew-services actually mean" framing: Creating Launch Daemons and Agents | Apple Developer Archive — "A user agent is essentially identical to a daemon, but is specific to a given logged-in user and executes only while that user is logged in." That's the spec we want to hit on all three OSes.

Out of scope

  • `repo-recall service install` shouldn't try to manage "start before login" semantics on any platform. If a user wants that, they can install as a system service themselves.
  • No GUI launcher work. `schtasks` and `launchctl` and `systemctl --user` are all CLI; the subcommand just wraps them.
  • Should compose cleanly with the existing brew formula — formula-installed repo-recall and `repo-recall service install` should not fight each other (detect and bail with a clear message).

Related: #29 (Windows install friction generally), #30 (scheduled auto-mode agents).

_Originally filed by @coilysiren on 2026-04-29T03:06:14Z - [https://github.com/coilysiren/repo-recall/issues/31](https://github.com/coilysiren/repo-recall/issues/31)_ ## The gap Today the README's only documented daemon path is \`brew services\` (macOS only). Linux users have to write systemd units by hand. Windows users land on NSSM (a service-control-manager wrapper), which is the wrong primitive for this app — **repo-recall is a single-user, loopback-only dashboard that consults user-scoped state (\`~/.claude/projects/\`, gh keyring, git working trees owned by the user)**. SCM services are system-scoped, run before login, run as a service identity by default, and don't load the user profile. That semantic mismatch caused real failures on a fresh Windows install: gh keyring inaccessible (DPAPI master keys not loaded), \`git\` refusing to operate (\`safe.directory\` ownership check), \`APPDATA\` unset, etc. Every workaround was downstream of \"this thing wants to be a user agent, not a system service.\" The macOS-side equivalent makes the framing clearer: \`brew services\` on macOS uses **launchd user agents**, not LaunchDaemons. A user agent runs only while that user is logged in, in their session, with their environment. That's exactly the semantic this app wants on every OS. ## Proposal Add \`repo-recall service install\` and \`repo-recall service uninstall\` subcommands. Platform-aware: - **macOS** — write \`~/Library/LaunchAgents/com.coilysiren.repo-recall.plist\`, call \`launchctl bootstrap gui/<uid>\`. Same semantic as \`brew services start\` but doesn't require the Homebrew formula. - **Linux** — write \`~/.config/systemd/user/repo-recall.service\`, call \`systemctl --user daemon-reload && systemctl --user enable --now repo-recall\`. \`loginctl enable-linger\` if the user wants persistence past logout (off by default). - **Windows** — create a Task Scheduler entry via \`schtasks /Create /SC ONLOGON /RU \$env:USERNAME /TN repo-recall /TR <path>\` (or the COM \`ITaskService\` API for richer config). \"Run only when user is logged on,\" no stored password, full profile loaded. The S4U logon type lets a task run under the user account *without storing the password* — direct fix for the keyring-access problem we hit. The Brew formula stays — it's still the lightest install on macOS — but \`repo-recall service install\` becomes the universal advice, with the formula running it under the hood. ## Citations supporting Task Scheduler over Windows Services for this shape of app 1. **[LogonTrigger object | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/taskschd/logontrigger)** — official Microsoft docs for the primitive. \"When the Task Scheduler service starts, all logged-on users are enumerated and any tasks registered with logon triggers that match the logged-on user are run.\" This is the direct semantic equivalent of a launchd user agent. 2. **[Security Contexts for Running Tasks | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/taskschd/security-contexts-for-running-tasks)** — covers the S4U logon type, which lets a task run under a specific user *without storing the password* (\`TASK_LOGON_S4U\`). Exactly the gap that NSSM-as-service forces you to fill with credential storage in LSA secrets. 3. **[//TODONT: Use a Windows Service just to run a scheduled process](https://weblogs.asp.net/jongalloway/428303/) (Jon Galloway, MSDN/ASP.NET blog)** — long-standing strategic argument that services are over-used when scheduled tasks are the correct primitive. The framing applies cleanly here: repo-recall doesn't need to start before login, doesn't accept external network connections, doesn't run as a system identity. Forcing it into SCM-service shape adds operational complexity without buying anything. Bonus, for the \"what does brew-services actually mean\" framing: **[Creating Launch Daemons and Agents | Apple Developer Archive](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html)** — \"A user agent is essentially identical to a daemon, but is specific to a given logged-in user and executes only while that user is logged in.\" That's the spec we want to hit on all three OSes. ## Out of scope - \`repo-recall service install\` shouldn't try to manage \"start before login\" semantics on any platform. If a user wants that, they can install as a system service themselves. - No GUI launcher work. \`schtasks\` and \`launchctl\` and \`systemctl --user\` are all CLI; the subcommand just wraps them. - Should compose cleanly with the existing brew formula — formula-installed repo-recall and \`repo-recall service install\` should not fight each other (detect and bail with a clear message). Related: #29 (Windows install friction generally), #30 (scheduled auto-mode agents).
coilysiren added
P4
and removed
P3
labels 2026-05-31 07:01:09 +00:00
Sign in to join this conversation.
No labels
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/repo-recall#71
No description provided.