docs(infra): fix the agent login command — cd before npx

The documented command fails:

  npm error Error: spawn sh EACCES
  npm error path: '/home/grenade'

`-H` sets HOME, but sudo leaves the working directory where it was invoked —
the operator's own 0700 home, which the service account cannot read. npx then
fails spawning its `sh -c claude` there. The error names npm and a package, so
it reads like a broken install rather than a directory permission, which is a
bad first experience of the one step in stage 0 that has to be done by hand.

Add the cd, use `bash -c` rather than `sudo -iu` (the account's shell is nologin
by design), explain why both are needed, and point at CLAUDE_PACKAGE so the
version logged in with cannot drift from the version the runner npx's.

Verified on bob: the pinned package now runs as the service account and reports
2.1.220.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TxK1CWPkFXqdcXMJ4hVe6
This commit is contained in:
rob thijssen
2026-08-07 17:54:59 +03:00
parent ffa2ad7f72
commit bd658392ab

View File

@@ -293,9 +293,26 @@ Remaining one-time steps (operator, on the target host):
The OAuth flow is interactive and must be completed *as the service account*,
because Claude Code reads credentials from $HOME:
sudo -u tireless -H /usr/bin/npx -y @anthropic-ai/claude-code@2.1.220
sudo -u tireless -H bash -c 'cd /var/lib/tireless && \
npx -y @anthropic-ai/claude-code@2.1.220'
# then: /login, and complete the browser flow
The `cd` is load-bearing. `-H` sets HOME, but sudo leaves the working
directory where you invoked it — typically your own 0700 home, which
`tireless` cannot read. npx then fails spawning its `sh -c claude` there:
npm error Error: spawn sh EACCES
npm error path: '/home/grenade'
which reads like a broken install rather than a directory permission.
`bash -c` rather than `sudo -iu tireless`, because the account's shell is
nologin by design and a login shell will not start.
Keep the pinned version in step with `CLAUDE_PACKAGE` in
crates/tireless-agent/src/claude.rs — the runner npx's that exact version,
and logging in with a different one warms the wrong cache.
This writes /var/lib/tireless/.claude.json. The token refreshes in place,
which is why the unit declares StateDirectory=tireless (systemd creates
it, owns it as the service account, and keeps it writable).