⚡ Swarm Architecture

claude-service on Linux — install notes

# 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)
  • sudo installed and the operator account in sudo group (or equivalent)
  • Tailscale installed and tailscale up completed (peer must be on the tailnet for mesh-gateway peer-routing to work)
  • claude CLI installed and claude login completed under the operator account (the claude -p subprocess inherits this auth — Max-subscription-billed)
  • Python 3.10+ on PATH; python3 -m venv available

1. Clone the repo + create the venv

`bash sudo -u bash -c ' cd ~ git clone https://github.com/darw007d/claude-service.git cd claude-service python3 -m venv venv venv/bin/pip install -r requirements.txt ' `

Replace with the operator account (e.g., darw007d on gpu-wsl, ubuntu on lab-OVH).

2. Configure .env

`bash sudo -u cp ~/claude-service/.env.example ~/claude-service/.env sudo -u ${EDITOR:-nano} ~/claude-service/.env `

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:

` ALL=(ALL) NOPASSWD: /bin/systemctl restart claude-service ALL=(ALL) NOPASSWD: /bin/systemctl status claude-service ALL=(ALL) NOPASSWD: /bin/systemctl restart inbox-watcher ALL=(ALL) NOPASSWD: /bin/systemctl status inbox-watcher `

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.

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.

  • 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.