SPB Git

Repository Public

operations-runbook.md 8.3 KB

# Operations runbook

Routine procedures for the production instance on M5M36. Diagnosis by symptom is in troubleshooting.md; deployment mechanics in deployment.md; recovery in backup-recovery.md.

Conventions: commands run on the node as the cluster operator account (ssh M5M36), in ~/apps/claude-remote unless stated. mld … commands run from the gateway (or its laptop wrapper). Nothing here needs sudo.

# Reference

Item Value
Public URL https://claude.spboucher.ai
Local health http://127.0.0.1:8620/api/v1/health
PM2 processes claude-remote-runtime, claude-remote-server, claude-remote-backup (03:35 nightly)
Code ~/apps/claude-remote (git; source of truth)
Data ~/claude-remote-data (0700)
Workspace ~/workspace
Backups ~/backups/claude-remote/*.tar.gz.enc (14 kept)
Backup passphrase ~/.config/claude-remote/backup.pass
tmux socket tmux -L claude-remote …
Database psql "$DATABASE_URL" after set -a && . ./.env && set +a
Manifest and secrets gateway: ~/dispatch/apps/claude-remote.json

# Daily / weekly checks

Check How Expected
Service up curl -fsS https://claude.spboucher.ai/api/v1/health {"ok":true,"version":"…"}
Processes pm2 list runtime and server online, low restart counts; backup stopped between runs is normal
Components System page in the application database, runtime, tmux ONLINE; claude ONLINE once authenticated; agentd ONLINE if configured
Last backup ls -lt ~/backups/claude-remote | head -3 a file dated last night
Disk System page (disk free) or df -h ~ comfortable margin; task logs can reach 256 MiB each
Audit trail System page → audit, or GET /api/v1/system/audit no unexpected auth.login failures or unknown client addresses
Active browser sessions Settings → Security only your devices

There is no external alerting: these checks are manual.

# Start, stop, restart

bash
pm2 restart claude-remote-server      # browsers reconnect; sessions and tasks untouched
pm2 restart claude-remote-runtime     # attach clients dropped, then re-attached; tmux untouched
pm2 stop claude-remote-server         # the public site stops answering (gateway error)
pm2 logs claude-remote-server --lines 100 --nostream
  • Both can be restarted in one command, as in the update procedure. The server reconnects to the runtime by itself with a 1–10 s backoff, in whatever order they come up.
  • Restart the server after every rebuild of the web application (the CSP hashes are computed at start).
  • Never run tmux -L claude-remote kill-server on production: it ends every session and task process. Sessions become RECOVERABLE, running tasks LOST.
  • From the gateway, mld heal restarts missing processes and checks the route; it also runs every 5 minutes on its own.

# Planned node reboot

  1. Tell yourself what will be lost: every running process inside sessions and tasks.
  2. In the application, let running tasks finish or cancel them; note which Claude sessions matter.
  3. Reboot. The node logs in automatically; PM2 and mld heal bring the processes back.
  4. Check health, then open the Sessions page: former live sessions are RECOVERABLE. Restart the ones you need (claude --resume for known conversations) and close the others.

# Update the application

Remote-first, on the node — full procedure in deployment.md:

bash
cd ~/apps/claude-remote && git status
# edit
pnpm -r build
set -a && . ./.env && set +a && node apps/server/dist/migrate.js
pm2 restart claude-remote-runtime claude-remote-server
curl -fsS http://127.0.0.1:8620/api/v1/health
git add -A && git commit -m "…" && git push origin main

# Sessions and tasks from the shell

bash
tmux -L claude-remote list-sessions            # cr-<uuid> = terminal sessions, crt-<uuid> = tasks
tmux -L claude-remote capture-pane -p -t '=cr-<uuid>:' | tail -40      # read-only look at a pane
ls ~/claude-remote-data/tasks/<id>/            # command, log, exit, meta.json

Prefer the application for terminating sessions and cancelling tasks: it records the state transition and the audit event. A tmux session killed by hand shows up as RECOVERABLE (or the task as LOST).

Do not tmux attach to a cr-* session from an SSH terminal while a browser is using it: the window follows the most recently used client's size.

# Backups

bash
set -a && . ./.env && set +a && deploy/backup.sh          # manual backup; prints "backup ok: <file> (<bytes>)"
pm2 logs claude-remote-backup --lines 20 --nostream       # result of the nightly run

Restore drill and disaster recovery: backup-recovery.md. Recommended monthly: one restore drill into a scratch database. Outstanding: copy archives off the node; store the passphrase elsewhere.

# Access management

Task How
Revoke a browser session Settings → Security → sessions list (its WebSockets close immediately)
Change the password Settings → Security (revokes all other sessions)
Add / remove a passkey, enable / disable TOTP Settings → Security (the last second factor cannot be removed)
Emergency lock-out of everyone psql "$DATABASE_URL" -c "UPDATE auth_sessions SET revoked_at = now() WHERE revoked_at IS NULL" — open sockets are closed within 60 s; then change the password
Take the site off the Internet from the gateway: mld tunnel rm claude-remote (route removed; the service keeps running locally), or pm2 stop claude-remote-server on the node
Lost second factors break-glass in backup-recovery.md

# Configuration changes

Change Where Then
Any CR_* variable, CR_EXEC_HOSTS, agentd URL manifest on the gateway (regenerate with deploy/make-manifest.py, which keeps the secrets, or edit) redeploy through mld; verify health
Allow a new remote task host authorise the node's SSH key on that host (owner decision — it grants a shell there), add the alias to CR_EXEC_HOSTS redeploy; test with a trivial task
Add a workspace root CR_WORKSPACE_ROOTS (label=/abs/path, labels unique; server and runtime must agree) redeploy
Rotate CR_RUNTIME_TOKEN remove it from the manifest's environment and regenerate redeploy (both processes restart; sessions survive)
Rotate CR_SECRET_KEY invalidates the enrolled TOTP secret only together with a TOTP re-enrolment (keep a passkey registered first)
Claude Code upgrade npm install -g @anthropic-ai/claude-code on the node new sessions use it; check the System page, the MCP page and a hook-driven WAITING_FOR_INPUT, because the application parses CLI output formats observed with 2.1.x

# Data housekeeping

Automatic (daily janitor in the server): lifecycle events (90 days by default, user setting), node metrics (14 days), login attempts (30 days), audit events (400 days), notifications (90 days), expired auth sessions (7 days after expiry).

Manual:

  • ~/claude-remote-data/tasks/<id>/ directories are never deleted by the application. Remove old ones by hand when disk space matters (the task row keeps its metadata; its log is then gone).
  • Archived sessions stay in the database; there is no purge of terminal_sessions rows.
  • Unregistering a project never deletes its files; remove the directory in a shell if wanted.

# Incident notes

When something unexpected happens, record: time, what the System page showed, pm2 list, the last 100 lines of both logs (they contain no secrets, cookies or terminal data by construction), and the audit entries around the time. Add the outcome to implementation-log.md.

# Limitations / not verified

  • No monitoring or alerting beyond mld heal and the cluster's own node telemetry; a failed backup or a full disk is not signalled.
  • The planned-reboot procedure has not been executed on M5M36.
  • mld tunnel rm and the redeploy-after-manifest-change path are cluster-orchestrator operations taken from the cluster documentation; they were not re-run for this document.
  • Task directory cleanup and session purge are manual; no tooling exists for them.
  • Rotation procedures for CR_RUNTIME_TOKEN and CR_SECRET_KEY are derived from make-manifest.py and the code; they were not rehearsed.
Files main
Menu

Theme