MacLustr cluster integration
Claude Remote runs on one node (M5M36) and touches the rest of the cluster through four narrow, explicitly authorised channels. Inventory: infrastructure-inventory.md.
| Channel | Direction | Authorisation | Used for |
|---|---|---|---|
| agentd telemetry API | M5M36 → agentd node (LAN, HTTP, bearer token) | token held server-side only | Cluster page: read-only inventory and metrics |
| SSH to compute nodes | M5M36 → hosts in CR_EXEC_HOSTS |
a key generated on M5M36, authorised on M3U96a and M3U96b | remote background tasks |
Git server (gitsrv) |
M5M36 → gateway M1M32 | the same key, restricted to git-shell (no interactive shell) |
clone / push of repositories |
mld + tunnel |
gateway → M5M36 | cluster orchestrator | deployment, health, public route (see deployment.md) |
Claude Remote does not run mld, does not deploy applications, and has no sudo.
1. Cluster telemetry (read-only)
Source: apps/server/src/modules/cluster/{service,mapping,index}.ts, page apps/web/src/app/(app)/cluster.
- When
CR_AGENTD_URLis set, the server polls agentd's/api/clusterevery 20 s and maps the payload toClusterOverview(packages/shared/src/dto.ts). - The mapping is defensive: unknown fields are ignored, missing values become
null, and nothing that identifies the network or the operator is copied through — no IP addresses, public IP, LAN map, usernames, hostnames or serial numbers. Display strings that could carry an address are dropped. - Freshness: a node whose metrics are older than 90 s is
stalewith healthUNKNOWN— neverONLINE. When agentd stops answering, the last good snapshot is kept with its timestamp and ages intoUNKNOWN. - Without agentd (
source: "none") the overview contains only the host, measured by the runtime (GET /host/metrics), and the cluster health isUNKNOWN. - Persistence: inventory upserts into
cluster_nodesand at most one downsampled row per node per minute intonode_metrics(kept 14 days).GET /cluster/nodes/:idreturns a node with its recorded metrics for the last 24 hours;GET /cluster/metricsreturns the recorded series. - Topology edges are limited to two facts:
lan(host → every other Mac in the inventory) andtunnel(host → the public gateway, only when agentd lists it). isHostmarks the node whose alias equalsCR_NODE_ALIAS(or the runtime's short hostname).execAllowedis true for aliases listed inCR_EXEC_HOSTS.
The System page reports the agentd link as a component: ONLINE when fresh, UNKNOWN when only old
data exists or agentd is not configured, OFFLINE when it never answered.
2. Remote tasks over SSH
Source: apps/server/src/modules/tasks/index.ts, apps/runtime/src/core-task-manager.ts,
core-task-script.ts.
-
A task has a
host:localor a node alias. A non-local host must match^[A-Za-z0-9._-]+$and be listed inCR_EXEC_HOSTS; the check is done by the server (403host_not_allowed, audited) and again by the runtime. -
Production value:
CR_EXEC_HOSTS=M3U96a,M3U96b. An empty list means local tasks only. -
The runner (a static POSIX script copied to
tasks/<id>/run.sh, executed in tmux sessioncrt-<uuid>) runs:textssh -o BatchMode=yes -o ConnectTimeout=10 <host> -- "<command>"The command text reaches the script through a file, the host through an environment variable — never by interpolation into the script. stdout and stderr go to
tasks/<id>/logon M5M36 (capped, 256 MiB by default); thesshexit code is the task's exit code. -
Timeout, cancel (TERM to the runner's process group, KILL after 10 s) and state tracking are the same as for local tasks. CPU time and peak memory are reported for local tasks only (
/usr/bin/time -l); for remote tasks they staynull. -
The task's working directory setting applies to local tasks. For a remote task the command runs in the remote account's default directory; put a
cdin the command if needed. -
Artifacts (
artifactGlobs) are collected from the local working directory through the file API; files produced on a remote node are not collected. -
sshresolves the alias through the node account's own SSH configuration; Claude Remote does not manage~/.ssh/config.
From a terminal session the administrator can of course run ssh by hand to any host the node key is
authorised on; that is outside the task allowlist and is a property of the shell, not of the application.
3. Git server
- "Clone" mode of project creation (
POST /api/v1/projects,mode: "clone") accepts onlyhttps://…,ssh://…,git@host:pathandgitsrv:pathURLs, never a string starting with-(validated by the server and by the runtime). The runtime runsgit clonewithGIT_TERMINAL_PROMPT=0, a 10-minute timeout, and never forwards credentials or host paths in errors. Credentials embedded in a remote URL are stripped before it is stored or shown. - Cluster repositories are cloned as
gitsrv:srv/git/<name>.git(the SSH alias present on every node). - The node key is authorised on the git server with a
git-shellrestriction: it can fetch and push, it cannot open an interactive shell on the gateway. - Project pages show branch, ahead/behind, dirty state and diffs (
git status,git diffthrough the runtime, with fsmonitor and hooks disabled so that inspecting a repository never runs repository code). Commits and pushes are done in the terminal.
4. Local models
GET /cluster/models lists the endpoints declared in CR_LOCAL_MODELS (a JSON array of
{id, name, host, engine, endpoint}), each with a health probed by a real GET <endpoint>/v1/models
(5 s timeout, cached 30 s). POST /cluster/models/:id/test sends one small chat completion and reports
latency, and tokens per second only when the endpoint returns usage. Endpoint URLs are reduced to a host
label before reaching the browser.
No model is ever invented: the cluster currently serves no local model (the former gateway was dismantled
in June 2026), CR_LOCAL_MODELS is unset, and the panel is empty by design.
5. Skills registry
GET /skills/registry/search?q= is an anonymous, read-only search adapter for the cluster's skills
registry (CR_SKILLS_REGISTRY_URL). If the registry is unreachable the answer is {connected: false}.
Installing from the registry is not implemented: POST /skills/op with a registry source answers 501 and
downloads nothing. Skills can be installed only from a directory inside a project.
What was migrated to the node
Summary (details and classification in migration.md): Claude Code CLI; a sanitized global
CLAUDE.md (sudo password removed); the jof250 skill, 7 agents and 10 jof-* commands; 5 MCP server
scripts 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 API
keys, the laptop's auto-approve PreToolUse hook, the cluster slash commands.
Limitations / not verified
- Remote tasks were tested in the application only with a fake
sshonPATH(apps/runtime/src/tasks.test.ts). The node key was verified by hand on M3U96a and M3U96b; no remote task has been launched from the production application. - Whether cancelling a remote task stops the command on the remote node (and not only the local
sshclient) has not been verified. - Project "clone" mode was not tested against the real git server from the application;
git clonewith the node key works by hand. execAllowedreflects the allowlist only; it does not test SSH reachability, although the DTO comment says "reachable with the runtime key".- The Cluster page is read-only: no node control, no deployment, no
mldaction. - Notification kinds
node.offlineandstorage.lowexist in the schema but nothing emits them. - The three iPhone "mobile nodes" of the cluster are out of scope.
- The cluster slash commands were not migrated, so Claude Code on the node has no built-in cluster tooling
beyond what the sanitized
CLAUDE.mddescribes and whatsshto the two allow-listed nodes permits.