Chapter 15 · Remote Fleet, Relay, and the Ops Toolbelt
Chapter goals: Master方法 for placing the entire secondmate home on an SSH-reachable remote host, understand the安全模型 of Relay public-mention应答, and learn to维持舰队日常运转 with the backlog契约 and toolbelt scripts.
15.1 Remote Secondmate: Moving the Entire Home to Another Machine
Chapter 14's secondmate and the主 home are on the same机. When the fleet scale grows, or when算力隔离 is needed, you can place theentire secondmate home on an SSH-reachable remote host. The official docs (docs/remote-secondmates.md) define the complete setup, routing, migration, recovery, and安全行为.
Prerequisites: Hard Requirements for the SSH Channel
# 1. Configure a plain SSH alias in the主 account's ~/.ssh/config
Host fleet-box
HostName 203.0.113.10
User fleet
IdentityFile ~/.ssh/id_ed25519
# 2. Use plain pubkey auth + strict host key verification; prefer a dedicated remote account
ssh-keygen -t ed25519
ssh-copy-id fleet-boxThree安全红线:
- Never enable agent forwarding —
fm-on.shactively disables it on each call; the forwarded setup uses theSendEnvmode; - Bounded dead-link detection —
fm-on.sharming SSH dead-peer detection; host reboot or link loss fails within a bounded window, not hangs无限; - The remote host musthave its own toolchain, the selected worker runtime, the selected session backend, and available credentials.
Remote Entry and Job Worker
On the remote host, clone firstmate at an absolute path and expose the固定入口 to that account's non-interactive SSH PATH:
# Execute on the remote host
mkdir -p ~/.local/bin
ln -s /absolute/path/to/firstmate/bin/fm-remote-entrypoint.sh \
~/.local/bin/fm-remote-entrypoint.shThe entrypoint only accepts encoded argv and仅限真正可执行的 bin/fm-*.sh files — it never接受 shell command strings. Each subsequent command validates the remote job worker,暂存 argv and stdin, waits for结果, and回传 stdout/stderr/exit code separately.
On macOS, this worker is a LaunchAgent in the Aqua session:
~/Library/LaunchAgents/dev.firstmate.remote-job.plist
Log directory: ~/Library/Logs/The worker's行为契约 worth remembering: runs only one暂存 job at a time; as soon as a non-long-poll command enters the queue, it抢占 the正在运行的 reply long-poll — interactive commands never排队 behind poll windows. The worker also self-terminates: once its code root is no longer the firstmate checkout (e.g., started from a worktree), it自动停止; bin/fm-remote-job-reap-orphans.sh负责清走这类遗留 worker.
Non-Interactive Tool Discovery: No Shell Startup Files Read
The remote host never runs a login shell, so ~/.profile, ~/.bashrc, ~/.zshrc have zero contribution to the runtime PATH. The worker's PATH is built from filesystem discovery:
<remote-root>/bin → ~/.local/bin → nvm default bin → asdf/mise shims
→ Nix directories → Homebrew directories → /usr/bin:/bin:/usr/sbin:/sbinIf a tool can only be found via the version manager's shell hook, fm-remote-doctor.sh --fix can generate firstmate's own wrapper in ~/.local/bin (never overwrites non-firstmate files, never installs packages). Writing one manually has the same shape:
cat > ~/.local/bin/tasks-axi <<'SH'
#!/usr/bin/env bash
tool_bin="$HOME/.nvm/versions/node/v22.12.0/bin"
PATH="$tool_bin:$PATH"
exec "$tool_bin/tasks-axi" "$@"
SH
chmod +x ~/.local/bin/tasks-axi15.2 Provisioning Remote Routing and Daily Start/Stop
First fill in the普通 secondmate charter locally, then one command provisions the remote routing:
bin/fm-remote-home-seed.sh <id> <ssh-alias> <remote-root> <remote-home> \
myapp=https://github.com/you/myapp.git
# Or without projects: replace the last arg with --no-projectsThe seed process does these things:
1. Record the host:root:home triplet in data/secondmates.md
2. Run a read-only readiness check on the host; auto--fix gaps found, then run a second read-only check for结论
3. Send a bounded manifest, letting the remote host自行 clone its firstmate home and project origins
4. Write .fm-secondmate-parent as a persistent record (marking whether the parent route is local or remote)Key安全语义:
- Projects are cloned on the remote host, not copied from the主 machine; each project's origin must be reachable from the remote account;
- Unregistered or local-only projects are拒绝 provisioning — the delivery mode still follows the主 machine's
data/projects.md; - Provisioning failure rolls back the新 routing;唯一保留路由的情况是 SSH exit code 255, because the remote completion status is未知, and must be reconciled on the same host;
- The主 home side's persistent registration only contains the routing and the charter brief under
data/<id>; launch records are created only after the真正 launch.
Daily start/stop uses the same command as local secondmates:
bin/fm-on.sh <id> # Start
bin/fm-off.sh <id> # Stop15.3 Relay: Public Mention应答安全模型
Relay lets the同一支舰队回答你在 X (Twitter) and Discord上的公开提及. It is enabled with a local .env paired token:
# .env (gitignored)
RELAY_X_TOKEN=xxx
RELAY_DISCORD_TOKEN=xxx安全模型核心: Relay only responds to公开提及, never主动 DM or私聊. The firstmate sees the mention, routes it through the normal intake契约, and replies in公开上下文 — never exposing internal state or crewmate工作细节.
15.4 Backlog契约与工具带日常
舰队日常运转依赖两条契约:
- Backlog契约: 所有待办工作项记录在
data/backlog.md,格式为id | kind | status | brief;firstmate 的调度引擎按此队列派生任务; - 工具带脚本:
bin/fm-*.sh数十个脚本覆盖会话生命周期、任务派发、监督引擎、配置合并等全部操作面.
关键日常命令:
bin/fm-brief.sh ship <id> --mode no-mistakes "修复登录bug" # 创建ship任务简报
bin/fm-spawn.sh ship <id> # 派生船员
bin/fm-peek.sh <id> # 偷看任务输出
bin/fm-send.sh <id> '继续方案B' # 投递转向指令
bin/fm-guard.sh # 拉取式中途警告15.5 Chapter Summary
- Remote secondmates: move the entire FM_HOME to an SSH-reachable host; the home is自包含 (state, backlog, projects, session lock);
- SSH hard requirements: no agent forwarding, bounded dead-link detection, dedicated remote account;
- Relay安全模型: only responds to公开提及; never主动 DM; never暴露内部状态;
- Daily ops: backlog契约 +
bin/fm-*.shtoolbelt; start/stop withfm-on.sh/fm-off.sh.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What is Relay in FirstMate?
2. What happens to projects during remote secondmate provisioning?
3. What is the bounded dead-link detection in fm-on.sh?
4. What is the role of the backlog契约 in daily fleet ops?
🛠️ Hands-On Exercises
- Set up an SSH alias to a remote host; provision a secondmate home there with
fm-remote-home-seed.sh; verify the独立 FM_HOME is created on the remote. - Enable Relay with a test X/Discord token; mention your bot in公开 and verify the fleet responds in公开上下文 without exposing internal state.
- Check the backlog契约: dispatch a task, verify it appears in
data/backlog.md; complete the task and verify it is marked done.
Remote fleet understood. Next, 实战一:GitHub 自动分诊 Agent.