Skip to content

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 ​

bash
# 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-box

Three安全红线:

  • Never enable agent forwarding — fm-on.sh actively disables it on each call; the forwarded setup uses the SendEnv mode;
  • Bounded dead-link detection — fm-on.sh arming 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:

bash
# 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.sh

The 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:

text
~/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:

text
<remote-root>/bin → ~/.local/bin → nvm default bin → asdf/mise shims
→ Nix directories → Homebrew directories → /usr/bin:/bin:/usr/sbin:/sbin

If 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:

bash
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-axi

15.2 Provisioning Remote Routing and Daily Start/Stop ​

First fill in the普通 secondmate charter locally, then one command provisions the remote routing:

bash
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-projects

The seed process does these things:

text
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:

bash
bin/fm-on.sh <id>        # Start
bin/fm-off.sh <id>       # Stop

15.3 Relay: Public Mention应答安全模型 ​

Relay lets the同一支舰队回答你在 X (Twitter) and Discord上的公开提及. It is enabled with a local .env paired token:

bash
# .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契约与工具带日常 ​

舰队日常运转依赖两条契约:

  1. Backlog契约: 所有待办工作项记录在 data/backlog.md,格式为 id | kind | status | brief;firstmate 的调度引擎按此队列派生任务;
  2. 工具带脚本: bin/fm-*.sh 数十个脚本覆盖会话生命周期、任务派发、监督引擎、配置合并等全部操作面.

关键日常命令:

bash
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-*.sh toolbelt; start/stop with fm-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 ​

  1. 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.
  2. Enable Relay with a test X/Discord token; mention your bot in公开 and verify the fleet responds in公开上下文 without exposing internal state.
  3. 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.