Repository Public
1# MacLustr cluster integration23Claude Remote runs on one node (M5M36) and touches the rest of the cluster through four narrow,4explicitly authorised channels. Inventory: [infrastructure-inventory.md](infrastructure-inventory.md).56| Channel | Direction | Authorisation | Used for |7|---|---|---|---|8| agentd telemetry API | M5M36 → agentd node (LAN, HTTP, bearer token) | token held server-side only | Cluster page: read-only inventory and metrics |9| SSH to compute nodes | M5M36 → hosts in `CR_EXEC_HOSTS` | a key generated on M5M36, authorised on M3U96a and M3U96b | remote background tasks |10| Git server (`gitsrv`) | M5M36 → gateway M1M32 | the same key, restricted to `git-shell` (no interactive shell) | clone / push of repositories |11| `mld` + tunnel | gateway → M5M36 | cluster orchestrator | deployment, health, public route (see [deployment.md](deployment.md)) |1213Claude Remote does not run `mld`, does not deploy applications, and has no sudo.1415## 1. Cluster telemetry (read-only)1617Source: `apps/server/src/modules/cluster/{service,mapping,index}.ts`, page `apps/web/src/app/(app)/cluster`.1819- When `CR_AGENTD_URL` is set, the server polls agentd's `/api/cluster` every 20 s and maps the payload to20 `ClusterOverview` (`packages/shared/src/dto.ts`).21- The mapping is defensive: unknown fields are ignored, missing values become `null`, and nothing that22 identifies the network or the operator is copied through — no IP addresses, public IP, LAN map,23 usernames, hostnames or serial numbers. Display strings that could carry an address are dropped.24- Freshness: a node whose metrics are older than 90 s is `stale` with health `UNKNOWN` — never `ONLINE`.25 When agentd stops answering, the last good snapshot is kept with its timestamp and ages into `UNKNOWN`.26- Without agentd (`source: "none"`) the overview contains only the host, measured by the runtime27 (`GET /host/metrics`), and the cluster health is `UNKNOWN`.28- Persistence: inventory upserts into `cluster_nodes` and at most one downsampled row per node per minute29 into `node_metrics` (kept 14 days). `GET /cluster/nodes/:id` returns a node with its recorded metrics30 for the last 24 hours; `GET /cluster/metrics` returns the recorded series.31- Topology edges are limited to two facts: `lan` (host → every other Mac in the inventory) and `tunnel`32 (host → the public gateway, only when agentd lists it).33- `isHost` marks the node whose alias equals `CR_NODE_ALIAS` (or the runtime's short hostname).34- `execAllowed` is true for aliases listed in `CR_EXEC_HOSTS`.3536The System page reports the agentd link as a component: `ONLINE` when fresh, `UNKNOWN` when only old37data exists or agentd is not configured, `OFFLINE` when it never answered.3839## 2. Remote tasks over SSH4041Source: `apps/server/src/modules/tasks/index.ts`, `apps/runtime/src/core-task-manager.ts`,42`core-task-script.ts`.4344- A task has a `host`: `local` or a node alias. A non-local host must match `^[A-Za-z0-9._-]+$` and be45 listed in `CR_EXEC_HOSTS`; the check is done by the server (403 `host_not_allowed`, audited) and again46 by the runtime.47- Production value: `CR_EXEC_HOSTS=M3U96a,M3U96b`. An empty list means local tasks only.48- The runner (a static POSIX script copied to `tasks/<id>/run.sh`, executed in tmux session49 `crt-<uuid>`) runs:5051 ```52 ssh -o BatchMode=yes -o ConnectTimeout=10 <host> -- "<command>"53 ```5455 The command text reaches the script through a file, the host through an environment variable — never56 by interpolation into the script. stdout and stderr go to `tasks/<id>/log` on M5M36 (capped, 256 MiB by57 default); the `ssh` exit code is the task's exit code.58- Timeout, cancel (TERM to the runner's process group, KILL after 10 s) and state tracking are the same as59 for local tasks. CPU time and peak memory are reported for local tasks only (`/usr/bin/time -l`); for60 remote tasks they stay `null`.61- The task's working directory setting applies to local tasks. For a remote task the command runs in the62 remote account's default directory; put a `cd` in the command if needed.63- Artifacts (`artifactGlobs`) are collected from the local working directory through the file API; files64 produced on a remote node are not collected.65- `ssh` resolves the alias through the node account's own SSH configuration; Claude Remote does not66 manage `~/.ssh/config`.6768From a terminal session the administrator can of course run `ssh` by hand to any host the node key is69authorised on; that is outside the task allowlist and is a property of the shell, not of the application.7071## 3. Git server7273- "Clone" mode of project creation (`POST /api/v1/projects`, `mode: "clone"`) accepts only `https://…`,74 `ssh://…`, `git@host:path` and `gitsrv:path` URLs, never a string starting with `-`75 (validated by the server and by the runtime). The runtime runs `git clone` with76 `GIT_TERMINAL_PROMPT=0`, a 10-minute timeout, and never forwards credentials or host paths in errors.77 Credentials embedded in a remote URL are stripped before it is stored or shown.78- Cluster repositories are cloned as `gitsrv:srv/git/<name>.git` (the SSH alias present on every node).79- The node key is authorised on the git server with a `git-shell` restriction: it can fetch and push, it80 cannot open an interactive shell on the gateway.81- Project pages show branch, ahead/behind, dirty state and diffs (`git status`, `git diff` through the82 runtime, with fsmonitor and hooks disabled so that inspecting a repository never runs repository code).83 Commits and pushes are done in the terminal.8485## 4. Local models8687`GET /cluster/models` lists the endpoints declared in `CR_LOCAL_MODELS` (a JSON array of88`{id, name, host, engine, endpoint}`), each with a health probed by a real `GET <endpoint>/v1/models`89(5 s timeout, cached 30 s). `POST /cluster/models/:id/test` sends one small chat completion and reports90latency, and tokens per second only when the endpoint returns usage. Endpoint URLs are reduced to a host91label before reaching the browser.9293No model is ever invented: the cluster currently serves no local model (the former gateway was dismantled94in June 2026), `CR_LOCAL_MODELS` is unset, and the panel is empty by design.9596## 5. Skills registry9798`GET /skills/registry/search?q=` is an anonymous, read-only search adapter for the cluster's skills99registry (`CR_SKILLS_REGISTRY_URL`). If the registry is unreachable the answer is `{connected: false}`.100Installing from the registry is not implemented: `POST /skills/op` with a registry source answers 501 and101downloads nothing. Skills can be installed only from a directory inside a project.102103## What was migrated to the node104105Summary (details and classification in [migration.md](migration.md)): Claude Code CLI; a sanitized global106`CLAUDE.md` (sudo password removed); the `jof250` skill, 7 agents and 10 `jof-*` commands; 5 MCP server107scripts **without** API keys; settings limited to model and fast mode. Not copied: 20 credential-bearing memory notes (the other 251 were copied on 2026-11-10), MCP API108keys, the laptop's auto-approve `PreToolUse` hook, the cluster slash commands.109110## Limitations / not verified111112- Remote tasks were tested in the application only with a fake `ssh` on `PATH`113 (`apps/runtime/src/tasks.test.ts`). The node key was verified by hand on M3U96a and M3U96b; no remote114 task has been launched from the production application.115- Whether cancelling a remote task stops the command on the remote node (and not only the local `ssh`116 client) has not been verified.117- Project "clone" mode was not tested against the real git server from the application; `git clone` with118 the node key works by hand.119- `execAllowed` reflects the allowlist only; it does not test SSH reachability, although the DTO comment120 says "reachable with the runtime key".121- The Cluster page is read-only: no node control, no deployment, no `mld` action.122- Notification kinds `node.offline` and `storage.low` exist in the schema but nothing emits them.123- The three iPhone "mobile nodes" of the cluster are out of scope.124- The cluster slash commands were not migrated, so Claude Code on the node has no built-in cluster tooling125 beyond what the sanitized `CLAUDE.md` describes and what `ssh` to the two allow-listed nodes permits.126