SPB Git

Repository Public

implementation-log.md 9.5 KB 91 lines markdown

1# Implementation log23Chronological record of the build and first deployment of Claude Remote, 2026-11-09 evening →42026-11-10. Times are Eastern Time. Commit hashes are those of this repository; steps without a hash5left no commit (work on the node, on the gateway, or verification only).67Entries state what was done and what was observed. Things not done are listed at the end.89## 2026-11-091011| Time | Step | Result | Commit |12|---|---|---|---|13| ~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](infrastructure-inventory.md)) | `c50198c` |14| | **Node selection.** M5M36 chosen: always-on Mac Studio, no competing workload, already a tunnel peer | [node-selection.md](node-selection.md) | `c50198c` |15| 21:47 | **Scaffold.** pnpm workspace, `packages/shared` (state machines, DTOs, wire formats, IPC types), database schema, the binding engineering contract | [CONTRACT.md](CONTRACT.md), `apps/server/migrations/001_init.sql` | `346f04f`, `e2ad1fe`, `8b3811d` |16| | **Node tooling.** On M5M36: tmux 3.8, ripgrep, PostgreSQL 17 (Homebrew service), Claude Code CLI 2.1.322 | installed; Claude Code not authenticated | — |17| 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` |18| 22:00–23:05 | **Four parallel workstreams** against the frozen shared contract: | | |19| | · runtime-core — tmux sessions, PTY attach, durable tasks, hooks, events, host metrics | | `bd15514` |20| | · runtime-fs — path safety, fs, git, search, Claude introspection, skills, MCP | | `bd15514` |21| | · server — authentication (password + TOTP / passkey), sessions, WebSocket proxy, files, tasks, cluster, skills/MCP, system, OpenAPI | | `6e27f12` |22| | · web — terminal workspace, mobile toolbar, PWA shell, sessions, files/editor, tasks, cluster, skills/MCP, system, settings | | `d0d5579` |23| 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` |24| 23:06 | `packageManager` pin dropped (the node runs pnpm 12) | | `ca47aeb` |25| ~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` |26| | **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` |27| | **CI gate.** `scripts/ci.sh`: build shared → typecheck → runtime, server, web tests → full build | green | |2829Test results recorded on the laptop (macOS, real tmux, real PostgreSQL, real `claude` binary without30prompts): runtime 209 passed; server 175 passed, including 17 integration tests against the real runtime;31web unit 12 passed; Playwright 19 passed against the dev mock (15 Chromium desktop, 4 WebKit iPhone 1532emulation) and 2 passed against the real backend.3334## Migration and deployment (completed by 2026-11-10 00:05; deployment dated 2026-11-10)3536Steps in the order they were carried out; individual times were not recorded.3738| Step | Result |39|---|---|40| **Migration to the node** (per [migration.md](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 |41| **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` |42| **Manifest** generated on the gateway with `deploy/make-manifest.py` (secrets generated there, never committed) | pinned to M5M36; three PM2 processes |43| **Deploy without public route** (`mld`) | build, migrations, PM2 start on the node; local health 200 |44| **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 |45| **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 |46| **Backup + restore drill** | first encrypted backup produced (about 20 KB); restored into a scratch database: schema and row counts printed |47| **Tunnel route** | `https://claude.spboucher.ai` → Caddy on the gateway → WireGuard → M5M36:8620 |48| **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) |49| **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 |50| **Documentation set** written (this directory) | status as of 2026-11-10 00:05 |5152State at 2026-11-10 00:05: service deployed and answering; `mld heal` (every 5 minutes) restarts missing53PM2 processes; nightly backup scheduled at 03:35. **No administrator account exists yet and Claude Code54on the node is not authenticated** — both are owner steps.5556## Deviations from the original brief, recorded as such5758| Topic | What was done instead | Why |59|---|---|---|60| 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 |61| VPN-only access | Public HTTPS with password + mandatory second factor | Access from any phone without a client ([network-architecture.md](network-architecture.md)) |62| Web Push | In-app notifications only | Not implemented |63| Skills-registry install | Read-only search adapter; install answers 501 | By design for this milestone |64| Local model management | Empty panel | No local model endpoint exists on the cluster |65| 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 |66| Hosted CI | Local gate `scripts/ci.sh` | The git server hosts bare repositories only |6768## Not done / not verified at the time of writing6970- Real iPhone Safari and PWA standalone mode (WebKit emulation only).71- The laptop-independence test (laptop off, phone on cellular) — needs the owner.72- Passkey login in a real browser (server verified with a software authenticator only).73- `WAITING_FOR_INPUT` from a real Claude conversation (hook relay tested end to end with the real hook74  script; no paid conversation was run).75- Remote SSH tasks against real nodes from the application (the node key works by hand on M3U96a and76  M3U96b; in-app only with a fake `ssh`).77- Project "clone" mode against the real git server (`git clone` with the node key works by hand).78- MCP add / remove / test and skills enable / disable / remove against a real configuration (mock / fake only).79- The 4429 backpressure close under real saturation.80- Off-node copy of backups (they stay on M5M36).81- A real reboot of M5M36 and a rehearsal of the disaster-recovery runbook.8283## Open follow-ups84851. Owner: first-run steps ([authentication.md](authentication.md)), including copying the backup passphrase.862. Owner: laptop-independence test; real-device mobile pass.873. Copy backups to another node; keep the passphrase elsewhere.884. Reconcile the small documentation/code gaps listed in [architecture.md](architecture.md)89   ("Limitations") — data-directory sub-folders named in the contract, `execAllowed` semantics, the90   unconfigured `lint` script.91
Files main
Menu

Theme