SPB Git

Repository Public

implementation-log.md 9.5 KB

# Implementation log

Chronological record of the build and first deployment of Claude Remote, 2026-11-09 evening → 2026-11-10. Times are Eastern Time. Commit hashes are those of this repository; steps without a hash left no commit (work on the node, on the gateway, or verification only).

Entries state what was done and what was observed. Things not done are listed at the end.

# 2026-11-09

Time Step Result Commit
~21:40 Discovery. Live mld scan from the gateway and direct inspection of candidate nodes; historical inventories were not trusted 29 Macs, 27 reachable; no local model endpoint, no 10 GbE, no NAS (infrastructure-inventory.md) c50198c
Node selection. M5M36 chosen: always-on Mac Studio, no competing workload, already a tunnel peer node-selection.md c50198c
21:47 Scaffold. pnpm workspace, packages/shared (state machines, DTOs, wire formats, IPC types), database schema, the binding engineering contract CONTRACT.md, apps/server/migrations/001_init.sql 346f04f, e2ad1fe, 8b3811d
Node tooling. On M5M36: tmux 3.8, ripgrep, PostgreSQL 17 (Homebrew service), Claude Code CLI 2.1.322 installed; Claude Code not authenticated —
21:54–21:57 Network architecture, deployment plan, migration manifest (laptop environment classified item by item); backup/restore scripts, mld manifest generator, local CI gate docs/*.md, deploy/*, scripts/ci.sh c50198c, 16ac05d, d24ea6c
22:00–23:05 Four parallel workstreams against the frozen shared contract:
· runtime-core — tmux sessions, PTY attach, durable tasks, hooks, events, host metrics bd15514
· runtime-fs — path safety, fs, git, search, Claude introspection, skills, MCP bd15514
· server — authentication (password + TOTP / passkey), sessions, WebSocket proxy, files, tasks, cluster, skills/MCP, system, OpenAPI 6e27f12
· web — terminal workspace, mobile toolbar, PWA shell, sessions, files/editor, tasks, cluster, skills/MCP, system, settings d0d5579
22:53 Integration against the real runtime. The server suite gained end-to-end tests that start the real runtime (own tmux socket, real claude binary, no prompt). 14 mismatches between server and runtime were found and fixed, including: RECOVERABLE detection after the tmux session is lost (a session.exit without an exit code is now resolved against the runtime), and reuse of the hook token across runtime restarts while sessions survive. scripts/dev-up.sh / dev-down.sh added integration suite green (16 tests at that commit, 17 now) 8bc5803
23:06 packageManager pin dropped (the node runs pnpm 12) ca47aeb
~23:10–23:59 Defect round. (1) Browser scrollback: tmux attach skips intermediate output, so the scrollback is now a runtime-written mirror of the pane history (core-scrollback.ts). (2) Attach-state tracking: RUNNING / DETACHED follow proxied streams immediately and are confirmed with the runtime. (3) Rate-limit buckets: page loads and health checks separated from the credential budget. (4) CSP frame-src 'self' blob: for the PDF preview suites green cf56e38
Security finding during the build. The first conversation-id pattern would have accepted a string beginning with -- as the value after --resume, i.e. a way to smuggle an option into the claude command line. Fixed before deployment in both layers: server pattern ^[A-Za-z0-9][A-Za-z0-9-]{7,63}$ (also applied to ids reported by hooks) and a runtime check that refuses option-like --resume values covered by tests in both packages fixed pattern present from 6e27f12 (server); runtime check added in 8bc5803
CI gate. scripts/ci.sh: build shared → typecheck → runtime, server, web tests → full build green

Test results recorded on the laptop (macOS, real tmux, real PostgreSQL, real claude binary without prompts): runtime 209 passed; server 175 passed, including 17 integration tests against the real runtime; web unit 12 passed; Playwright 19 passed against the dev mock (15 Chromium desktop, 4 WebKit iPhone 15 emulation) and 2 passed against the real backend.

# Migration and deployment (completed by 2026-11-10 00:05; deployment dated 2026-11-10)

Steps in the order they were carried out; individual times were not recorded.

Step Result
Migration to the node (per migration.md) Copied: sanitized global CLAUDE.md (sudo password removed), jof250 skill, 7 agents, 10 jof-* commands, 5 MCP server scripts without API keys, settings limited to model + fast mode. Not copied: memory notes, MCP API keys, the laptop's auto-approve-everything PreToolUse hook, cluster slash commands
Node SSH key A dedicated key generated on M5M36; authorised on the git server restricted to git-shell, and with a full shell on compute nodes M3U96a and M3U96b; CR_EXEC_HOSTS=M3U96a,M3U96b
Manifest generated on the gateway with deploy/make-manifest.py (secrets generated there, never committed) pinned to M5M36; three PM2 processes
Deploy without public route (mld) build, migrations, PM2 start on the node; local health 200
LAN verification 10 API routes + a POST + a WebSocket upgrade → 401 without a session; POST with a foreign Origin → 403; wrong setup token → 401; security headers present (CSP with per-document script hashes and no unsafe-inline for scripts, HSTS, nosniff, X-Frame-Options: DENY, Referrer-Policy); data directory, socket and token files 0600/0700; only listeners: node on 8620 and PostgreSQL on loopback
Runtime on the node Claude Code hooks (Notification, Stop, UserPromptSubmit, SessionStart) installed in the node's ~/.claude/settings.json; the real claude 2.1.322 launched in a tmux session through the runtime and rendered its welcome screen; no prompt sent
Backup + restore drill first encrypted backup produced (about 20 KB); restored into a scratch database: schema and row counts printed
Tunnel route https://claude.spboucher.ai → Caddy on the gateway → WireGuard → M5M36:8620
Public verification /api/v1/health 200 publicly; valid TLS, HTTP/2, HTTP→HTTPS 308; audit log records the real client public address through the tunnel (X-Forwarded-For trusted only from the gateway)
Git adoption ~/apps/claude-remote on the node is a git clone with origin = the cluster git server (claude-remote.git); the node directory is the source of truth
Documentation set written (this directory) status as of 2026-11-10 00:05

State at 2026-11-10 00:05: service deployed and answering; mld heal (every 5 minutes) restarts missing PM2 processes; nightly backup scheduled at 03:35. No administrator account exists yet and Claude Code on the node is not authenticated — both are owner steps.

# Deviations from the original brief, recorded as such

Topic What was done instead Why
Dedicated OS identity Runs under the existing cluster operator macOS account A dedicated account breaks the cluster's mld/PM2 conventions and Claude Code's Keychain login. Isolation is by process separation, a 0600 unix socket and workspace-root confinement of the file API — not by OS user; the terminal is a full shell of that account by design
VPN-only access Public HTTPS with password + mandatory second factor Access from any phone without a client (network-architecture.md)
Web Push In-app notifications only Not implemented
Skills-registry install Read-only search adapter; install answers 501 By design for this milestone
Local model management Empty panel No local model endpoint exists on the cluster
Research integrations (LaTeX, Jupyter) None as dedicated features They work only through the terminal or tasks if the tools are installed; TeX is not installed on the node. The editor has a PDF file preview
Hosted CI Local gate scripts/ci.sh The git server hosts bare repositories only

# Not done / not verified at the time of writing

  • Real iPhone Safari and PWA standalone mode (WebKit emulation only).
  • The laptop-independence test (laptop off, phone on cellular) — needs the owner.
  • Passkey login in a real browser (server verified with a software authenticator only).
  • WAITING_FOR_INPUT from a real Claude conversation (hook relay tested end to end with the real hook script; no paid conversation was run).
  • Remote SSH tasks against real nodes from the application (the node key works by hand on M3U96a and M3U96b; in-app only with a fake ssh).
  • Project "clone" mode against the real git server (git clone with the node key works by hand).
  • MCP add / remove / test and skills enable / disable / remove against a real configuration (mock / fake only).
  • The 4429 backpressure close under real saturation.
  • Off-node copy of backups (they stay on M5M36).
  • A real reboot of M5M36 and a rehearsal of the disaster-recovery runbook.

# Open follow-ups

  1. Owner: first-run steps (authentication.md), including copying the backup passphrase.
  2. Owner: laptop-independence test; real-device mobile pass.
  3. Copy backups to another node; keep the passphrase elsewhere.
  4. Reconcile the small documentation/code gaps listed in architecture.md ("Limitations") — data-directory sub-folders named in the contract, execAllowed semantics, the unconfigured lint script.
Files main
Menu

Theme