Skip to content

fix(daemon): bake PATH into systemd unit so shell tool can resolve netclaw CLI#948

Merged
Aaronontheweb merged 3 commits intonetclaw-dev:devfrom
Aaronontheweb:fix/systemd-unit-path-resolution
May 9, 2026
Merged

fix(daemon): bake PATH into systemd unit so shell tool can resolve netclaw CLI#948
Aaronontheweb merged 3 commits intonetclaw-dev:devfrom
Aaronontheweb:fix/systemd-unit-path-resolution

Conversation

@Aaronontheweb
Copy link
Copy Markdown
Collaborator

Summary

systemctl --user services start with a sanitized PATH that doesn't include ~/.local/bin or any custom install directory. Because ShellTool and BackgroundJobExecutionActor invoke bash -c with the daemon's inherited environment, the agent can't resolve netclaw (or other user-installed binaries) when the daemon is installed via netclaw daemon install. Running the daemon directly from a shell hides the bug because the operator's PATH is inherited.

This PR fixes the install template and adds a doctor check for stale unit files.

Changes

  • DaemonManager.InstallAsync now bakes Environment=PATH={installDir}:{home}/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin into the generated unit file. installDir comes first (so the daemon's bundled CLI wins) and ~/.local/bin is included for the typical user-tools location.
  • Centralized systemd unit path in DaemonManager.SystemdUserUnitDirectory / SystemdUserUnitFilePath — used by Install, Uninstall, and the new doctor check.
  • Conditional upgrade hint: the install success message only mentions "unit file refreshed" when an existing unit was overwritten, not on fresh installs.
  • New SystemdUnitPathDoctorCheck — Linux-only; parses ~/.config/systemd/user/netclaw.service and warns if Environment=PATH= is missing or doesn't include the daemon's install directory. Skips silently on non-Linux and on Linux hosts without a service installed (e.g., users running the daemon directly via daemon start).
  • netclaw-operations skill bumped 1.27.0 → 1.28.0 with a new diagnostic-table row pointing the agent at the new check.

Migration for existing installs

Existing service installs need the unit file refreshed:

netclaw daemon uninstall && netclaw daemon install
systemctl --user restart netclaw

netclaw doctor will surface a Warning for any stale unit on Linux until this is done.

Test plan

  • dotnet build src/Netclaw.Cli/Netclaw.Cli.csproj clean
  • dotnet test src/Netclaw.Cli.Tests/Netclaw.Cli.Tests.csproj — 630 passed (including 7 new tests for the doctor check)
  • dotnet slopwatch analyze — 0 issues
  • Manual header verification on changed .cs files (PowerShell not installed locally to run Add-FileHeaders.ps1)
  • Verify on the affected VM after merge: cat /proc/$(pgrep -u "$USER" netclawd)/environ | tr '\0' '\n' | grep PATH should show installDir present after daemon uninstall && daemon install && systemctl --user restart netclaw

…tclaw CLI

systemctl --user services start with a sanitized PATH that excludes
~/.local/bin and any custom install directory, so ShellTool and
BackgroundJobExecutionActor (which invoke `bash -c`) failed to resolve
the `netclaw` CLI when the daemon was installed via `netclaw daemon
install`. Compose Environment=PATH explicitly in the unit template:
installDir first (so the daemon's bundled CLI wins), then ~/.local/bin,
then the standard system PATH.

Add SystemdUnitPathDoctorCheck to flag stale unit files that lack the
PATH directive on existing installs. The check is Linux-only and
returns Pass when no systemd user unit is present (e.g., daemon started
directly via `netclaw daemon start`), so it does not produce noise for
users who don't install as a service.

Existing installs need `netclaw daemon uninstall && netclaw daemon
install` to refresh the unit file; the install command's success
message now mentions this.

Update netclaw-operations skill (1.27.0 -> 1.28.0) with a diagnostic
table entry pointing at the new check.
Windows test runs failed because Path.GetDirectoryName() normalizes
Unix-style paths to backslash separators on Windows, breaking the
installDir comparison in SystemdUnitPathDoctorCheck. systemd unit
files always use forward-slash paths regardless of host OS, so parse
them with literal '/' semantics instead of the platform-aware Path API.
@Aaronontheweb Aaronontheweb added enhancement New feature or request tui Terminal UI (Termina) issues labels May 9, 2026
@Aaronontheweb Aaronontheweb merged commit 6e4b466 into netclaw-dev:dev May 9, 2026
6 of 7 checks passed
Aaronontheweb added a commit to netclaw-dev/netclaw-website that referenced this pull request May 9, 2026
…17)

The netclaw daemon install command now bakes Environment=PATH= into
the generated systemd user unit so the agent's shell tool can resolve
netclaw and other user-installed binaries (netclaw-dev/netclaw#948).
Update the systemd deployment doc to match:

- Add a "Why user-level systemd?" section that frames the choice as
  security-neutral relative to running the daemon from the shell —
  same privileges, same blast radius, plus operational reliability.
  The contrast that matters is user-level vs. system-level.
- Update both unit-file examples (auto-generated and manual) with
  the Environment=PATH= line and explain why it's needed.
- Add a troubleshooting entry for "shell tool can't find netclaw"
  on upgraded installs that still have the older unit file, with
  uninstall+install migration steps and a /proc verification command.

Also surface the security framing one click away from where users
encounter the install command:

- Make the daemon install/uninstall rows in cli/overview.md link
  to the systemd page.
- Add a one-line note under the Daemon Management table summarizing
  the user-level model.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request tui Terminal UI (Termina) issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant