# claude-service on Linux — install notes
The Linux install path runs claude-service as a systemd unit under the operator account, with a narrow sudoers.d bake-out for routine self-service (daemon restart). Validated on lab-OVH and gpu-wsl (peers onboarded over the OMEGA Phase 13.6 mesh build week).
For the Windows (NSSM) substrate, see [windows_install.md](./windows_install.md). The privilege-boundary shape differs substantively between the two; this doc covers Linux only.
Pre-flight
- Linux distro with systemd (Ubuntu 22.04+ / Debian 12+ / similar)
sudoinstalled and the operator account insudogroup (or equivalent)- Tailscale installed and
tailscale upcompleted (peer must be on the tailnet for mesh-gateway peer-routing to work) claudeCLI installed andclaude logincompleted under the operator account (theclaude -psubprocess inherits this auth — Max-subscription-billed)- Python 3.10+ on PATH;
python3 -m venvavailable
1. Clone the repo + create the venv
`bash
sudo -u `
Replace with the operator account (e.g., darw007d on gpu-wsl, ubuntu on lab-OVH).
2. Configure .env
`bash
sudo -u `
Set at minimum: MESH_GATEWAY_URL, MESH_GATEWAY_TOKEN, CLAUDE_SERVICE_TOKEN, MESH_NODE_NAME, plus any per-origin CWD allowlists per the v0.3.1 asymmetric capability flags.
3. Install the systemd unit
The repo ships systemd/claude-service.service as a template. Customize the User= and path lines for the operator account, then:
`bash
sudo cp systemd/claude-service.service /etc/systemd/system/claude-service.service
sudo systemctl daemon-reload
sudo systemctl enable claude-service
`
Don't start it yet — install the sudoers bake-out first (next section), so the agent has the autonomy to restart the service once running.
4. Install the narrow sudoers.d bake-out
claude-service runs as a systemd unit under the operator account. Routine restarts (after config changes, version bumps, etc.) are in-remit work for the agent — but sudo systemctl restart requires either a tty for the password prompt or a passwordless sudoers entry. The agent shell typically lacks the tty.
The Swarph paper §6.3 + the in-remit-credential-gap discriminator (lab+gpu-wsl observation 2026-05-01) named the right shape: bake out the credential gap with a narrow privilege grant, not punt to commander every restart.
This repo ships exactly that grant as install/sudoers.d-claude-service. Install via:
`bash
sudo bash install/install_linux.sh --user `
The script:
1. Renders the template with replaced by the operator account
2. Stages to a tmp file
3. Runs visudo -c -f on the staged file (validation BEFORE install)
4. Installs at /etc/sudoers.d/claude-service with mode 440 (root:root)
5. Re-runs visudo -c against the full sudoers tree (validation AFTER install)
6. Backs out the installed file if post-install validation fails
The grant covers exactly four commands:
`
`
Anything else (any other systemctl unit, any other sudoers grant) requires a separate sudoers.d file with explicit operator approval — narrow scope is the load-bearing safety property.
5. Start the service
`bash
sudo systemctl start claude-service
sudo systemctl status claude-service # should print active (running)
curl -s http://localhost:8787/health # should return JSON with node + service_version
`
If claude -p returns "Not logged in" via /delegate, the systemd unit's User= is wrong (running as a user that doesn't have ~/.claude/.credentials.json populated). Fix and reload.
6. Register with mesh-gateway
The systemd unit auto-registers on startup (since v0.3.0). To verify:
`bash
TOKEN=$(grep '^MESH_GATEWAY_TOKEN=' .env | cut -d= -f2)
curl -s -H "Authorization: Bearer $TOKEN" \
http://lab-ovh:8788/peers | jq '.peers[] | select(.name == "`
Should return your peer entry with the capability dict from /health.
7. Verify the bake-out works (agent-side)
From an agent shell on the operator account:
`bash
sudo -n systemctl status claude-service
`
-n disables prompting; if the bake-out is in effect, this returns the status without asking for a password. If it prompts (or fails with sudo: a password is required), the sudoers.d entry is missing, malformed, or the user doesn't match. Re-run install_linux.sh if needed.
Once verified, future agent invocations of sudo -n systemctl restart claude-service execute autonomously per the AI-to-AI cooperative-protocol default. The bake-out is a one-time operator gate; the autonomy is durable.
8. Install cli-version-watcher (Optional but recommended)
Claude Code auto-updates its CLI frequently (e.g., 2.1.123 → 2.1.126). claude-service caches the version at boot. cli-version-watcher.py ensures the mesh registry advertises the correct version by triggering a /capabilities/refresh when the on-disk binary changes.
1. Test the script:
`bash
venv/bin/python scripts/cli_version_watcher.py
`
2. Install the systemd timer:
`bash
sudo cp systemd/cli-version-watcher.service /etc/systemd/system/
sudo cp systemd/cli-version-watcher.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now cli-version-watcher.timer
`
9. Install mesh-inbox-watcher (The sibling daemon)
Peer DMs are delivered via mesh-gateway. To receive them without being in a live session, you need a poller. inbox_watcher.py polls the gateway every 60s and writes DMs to logs/inbox.log.
1. Configure environment: Ensure .env has MESH_NODE_NAME and INBOX_LOG_FILE (default: logs/inbox.log).
2. Install the systemd unit:
`bash
sudo cp systemd/inbox-watcher.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now inbox-watcher
`
3. Verify:
`bash
tail -f logs/inbox.log
`
Send a DM to yourself from another node (or via curl to mesh-gateway) to see it land. The SessionStart hook in your agent environment should be configured to tail this log to pull in missed messages at boot.
Cross-link
- Main paper §6.3 — privilege-boundary exception class. The bake-out IS the refinement: don't over-apply §6.3 to bake-outable cases.
- Memory entry
feedback_in_remit_credential_gap.md(lab + gpu-wsl side) — the discriminator: can this credential be granted to the agent narrowly without compromising the privilege boundary? - [
windows_install.md](./windows_install.md) — Windows (NSSM) substrate is NOT bake-outable (UAC + LogonType=Interactive), §6.3 fires correctly there. See windows_install.md §7 for the canonical operational sequence.