---
name: vantaloom-fleet
description: Drive a fleet of machines through Vantaloom as a keyless, zero-config SSH replacement. Use when you need to run commands, open interactive terminals, or read/write/move files on ANOTHER machine (or the local one) that is reachable via the Vantaloom mesh — without SSH keys, port forwarding, or a public IP. Trigger when the user mentions a remote machine by name, asks to run something "on my other computer / server / laptop", needs to move a file between machines, or references Vantaloom, loomnet, or a specific fleet host.
---

# Vantaloom Fleet — cross-machine execution channel

Vantaloom is a local AI-agent workbench whose substrate is a private device mesh
(**loomnet**: QUIC + mTLS, LAN-direct / public-direct / NAT-hole-punched relay —
**no public IP, no port forwarding**). Every machine signed into the same
Vantaloom account appears in one directory with live online status, and any of
them can run commands, stream terminals, and exchange files with the others.

This skill exposes that mesh to you through the `vantaloom-fleet` MCP server. It
is a **keyless, zero-config SSH replacement**: no key management, no network
setup, and every action goes through the local Vantaloom API (`127.0.0.1:8780`),
so it is auditable and — under multi-user collaboration — capability-gated.

> 中文对照见文末「中文说明」。

## When to use this skill

Use it whenever the target of an action is a machine reachable over Vantaloom —
including the local host — and you want SSH-like reach without SSH:

- Run a build / test / script on a remote build box.
- Inspect logs or processes on a server that has no public IP.
- Move an artifact from one machine to another.
- Open a long-lived shell on a remote machine and interact with it.

Do **not** use it for actions on the machine you are already running on when a
direct local tool is simpler — reach for the fleet when the mesh is the point.

## The one rule: discover before you act

**Always call `machines_list` first.** It returns every machine with its `id`,
`name`, `platform`, `arch`, and live `status`/`reachable`. You need a valid
`machine_id` for every other tool, and only machines with `status: "online"` and
`reachable: true` can act right now. `machine_id` `"local"` always means the
machine hosting this MCP server.

```
machines_list()  ->  { machines: [ { id, name, platform, arch, status, reachable, isLocal }, ... ] }
```

Pick the target by `name`/`platform`, then use its `id` (or `"local"`).

## Reaching a machine that isn't in your fleet yet — connection codes

If the target machine is **not** in `machines_list` (its owner is not signed into
the same Vantaloom account / not logged into any Hub), you can still reach it with
a **temporary connection code**. The owner opens Vantaloom's「临时连接」panel (on
the login screen, or 设置 → 网络与设备 → 临时连接) and reads you a long
`vtl1-…` string — valid ~8 minutes, single use.

```
machine_trust_with_code(code)  ->  { ok, machineId, name }
```

This host redeems the code over the encrypted loomnet mesh (mutual-TLS, one-time
secret). On success the target is added to your fleet and returns its `machineId`.
From then on drive it with `run_command` / `terminal_*` / `file_*` using that
`machineId`, **exactly like any other fleet machine — without opening the
target's Vantaloom UI**. Trust auto-expires after ~1 hour idle; if calls start
failing with "not trusted", ask for a fresh code and redeem again.

Typical flow: `machine_trust_with_code(code)` → note `machineId` → `machines_list`
(the new machine now appears) → `run_command(machineId, …)`.

This works even when the target is behind NAT with no public address and no Hub
account: since 0.15.37 both sides meet through the Hub's relay using a rendezvous
key carried in the code (the relay only ever sees ciphertext and a hash of that
key). Both machines must be on **0.15.37 or newer**; against an older target,
redemption only succeeds if you can reach it directly (same LAN, or it has a
configured public address).

## Choosing a tool

| Goal | Tool | Notes |
|---|---|---|
| Trust a machine NOT in your fleet, via its temp code | `machine_trust_with_code` | Redeem a `vtl1-…` code → target joins the fleet. |
| Run one command, get the result | `run_command` | Synchronous. Best default. ~55s cap for remote. |
| Interactive / long-running / REPL / server | `terminal_open` + `terminal_write` + `terminal_read` + `terminal_close` | Stateful PTY session. |
| Find/clean up terminal sessions you lost track of | `terminal_list` | Sessions outlive YOUR restart — see Lifecycle. |
| Read a file | `file_read` | Text → UTF-8; binary → base64. 16MB cap. |
| Create/overwrite a file | `file_write` | `base64: true` for binary. |
| Copy/move a file between machines | `file_transfer` | Bytes stream through this host over the mesh. |

### `run_command` — the workhorse

```
run_command(machine_id, command, cwd?, timeout_sec?)
  -> { machineId, command, status, exitCode, durationMs, stdout, stderr }
```

- `stdout` carries the **merged stdout+stderr** stream — the runtime captures a
  command's output as one stream, exactly like a terminal. `stderr` is only set
  on a transport/setup failure (e.g. machine offline), never for a command's own
  diagnostics.
- Remote commands are capped near **55s** by the cross-machine channel. If a job
  is longer or interactive, use a terminal session instead.
- `cwd` is optional; for local execution it defaults to the target's home dir.
  For a remote target, pass an absolute `cwd` the target OS understands.

### `terminal_*` — interactive sessions

1. `terminal_open(machine_id, cwd?, shell?, cols?, rows?, title?)` → `{ id, ... }`.
   Keep the `id`.
2. `terminal_write(session_id, data)` — `data` is sent **verbatim**. Append
   `"\n"` (or `"\r\n"` on Windows shells) to submit a line. Control characters
   pass through (`"\u0003"` = Ctrl-C, `"\u0004"` = Ctrl-D).
3. `terminal_read(session_id, after?, timeout_ms?)` →
   `{ events, lastSequence, output }`. **Incremental**: pass `after: 0` first,
   then feed the previous response's `lastSequence` as the next `after` to get
   only new output. (The key is camelCase `lastSequence` — the whole tool surface
   returns the backend's camelCase JSON verbatim.) The call blocks up to
   `timeout_ms` (default 1500) collecting output, then returns whatever arrived —
   an empty `output` just means nothing new yet; write, wait, read again.
4. `terminal_close(session_id)` — **always** close when done.
5. `terminal_list(all?)` → `{ sessions: [...] }`. Lists the sessions THIS bridge
   opened (add `all: true` to see every terminal on the host, including ones the
   Vantaloom UI owns — do not close those). Use it to recover from a lost
   `session_id`; without it an orphaned PTY lives until the runtime restarts.

Do not poll `terminal_read` in a tight loop. Write, then read with a sensible
`timeout_ms`; if you expect a slow command, raise `timeout_ms` rather than
spamming reads.

### `file_read` / `file_write` / `file_transfer`

```
file_read(machine_id, path)                    -> { content, encoding, size, truncated, ... }
file_write(machine_id, path, content, base64?) -> file metadata
file_transfer(source_machine_id, source_path, dest_machine_id, dest_dir, move?, overwrite?)
```

- Paths are absolute and interpreted on the **target** machine (mind the OS from
  `machines_list`: `C:\...` on Windows, `/home/...` on Linux/Mac).
- `file_read` returns `encoding: "base64"` for binary; decode accordingly.
- `file_write` is create-or-overwrite. For binary content, base64-encode it and
  set `base64: true`.
- `file_transfer` needs `dest_dir` to be an **existing directory** on the
  destination. It streams bytes through this host — no shared filesystem needed.

## Lifecycle — what survives what

The MCP server and the Vantaloom runtime are **two independent processes**, and
knowing which state lives where saves you a class of confusing failures:

- **This MCP server** is launched by YOUR client (Claude Code / Cursor / Codex)
  and dies with it. It holds no state — every call is a fresh HTTP request to
  `127.0.0.1:8780`. Restarting your client costs nothing.
- **The Vantaloom runtime** starts and stops on its own schedule (it is a
  background service, often auto-started at login). It can be running before
  your client starts, or start later — the tools recover on their own, no
  reconnect dance needed.
- **Terminal sessions live in the runtime, not here.** Two consequences:
  - Your own restart does NOT kill them — but you lose the `session_id`, so
    call `terminal_list` to find them again and `terminal_close` the strays.
    There is no idle reaper; an orphan lives until the runtime exits.
  - A runtime restart (e.g. a Vantaloom update) DOES kill them: every
    `session_id` becomes invalid at once. Re-open rather than retrying.
- After a runtime update the on-disk MCP binary is replaced, but the running
  process keeps the old code until your client restarts it. Harmless in
  practice; restart the client if a newly-documented tool is missing.

## Where the binary lives / registering it

The MCP server ships with the Vantaloom runtime at `<prefix>/bin/vantaloom-mcp`
(`.exe` on Windows). Default prefixes: `D:\Vantaloom` (Windows, `C:\Vantaloom`
when there is no D: drive), `~/Applications/Vantaloom` (macOS),
`~/.vantaloom` or the `VANTALOOM_HOME` override.

Do not hunt for it by hand — the CLI answers all of this:

```bash
vantaloom mcp                 # binary path + per-client registration status
vantaloom mcp path            # just the absolute path
vantaloom mcp print           # config snippets to paste
vantaloom mcp install         # register with Claude Code / Cursor / Codex + install this skill
```

## Limits worth knowing

- `run_command`: remote execution is capped near **55s** by the cross-machine
  channel — longer work belongs in a terminal session.
- `file_read`: 16MB (`truncated: true` when hit).
- `file_transfer`: streams through this host — **192MB** per transfer, and for a
  directory **1000 files / 512MB**. `dest_dir` must already exist.
- `file_write` does not create missing parent directories; `mkdir` first via
  `run_command` (or open a terminal) if the tree is not there.
- Command syntax is interpreted by the **target** machine's default shell
  (PowerShell on Windows, the login shell on macOS/Linux). Check `platform` from
  `machines_list` before writing a command line, not just before writing a path.

## Reading `machines_list` output

Two fields decide whether a machine is worth targeting, plus two that explain
what a machine *is*:

- `status: "online"` + `reachable: true` — can act right now.
- `isLocal: true` — this is the host running the MCP server (`machine_id`
  `"local"` also always resolves to it).
- `role: "worker"` — a headless被控端 (PC worker node). It executes commands and
  serves files like any other machine, but has no UI and no conversations.
- `temporary: true` — trusted via a connection code (see above). Trust lapses
  after ~1h idle; when calls start failing, ask for a fresh code.

## Error handling

- If a tool result says the machine is offline / unreachable, re-run
  `machines_list` — status may have changed, or you picked the wrong `id`.
- If a call returns a permission error mentioning a capability, this account is
  in **multi-user collaboration** mode and your user lacks the "terminal / command"
  capability. That is a deliberate access boundary — report it; do not try to
  route around it.
- If everything fails to connect, the local Vantaloom runtime may not be running.
  Ask the user to start Vantaloom (the MCP server only talks to `127.0.0.1:8780`).
- Run `scripts/healthcheck.mjs` (or hit `machines_list`) to confirm connectivity.

## Safety boundary (read this)

- The MCP server only ever connects to **`127.0.0.1:8780`** on this host. It
  holds no cross-machine credentials; every hop to another machine is carried by
  Vantaloom's own loomnet mTLS tunnel.
- On a single-user runtime (the default desktop mode, `authMode disabled-local`),
  local callers are trusted and these tools are **not** individually approved —
  they run with the same reach as the user's own Vantaloom session. Treat
  `run_command` / `terminal_*` as a real shell on the target: do not run
  destructive commands on a remote machine without explicit user intent.
- Under multi-user collaboration, `/v1/bridge/exec` and terminals are gated by
  the "terminal use" capability, enforced on the machine that runs the command.

## 中文说明

- **先列后用**：任何操作前先 `machines_list` 拿到 `machine_id` 与在线状态；
  `"local"` 恒指承载本 MCP 的这台机器。只有 `status:"online"` 且 `reachable:true`
  的机器此刻可执行。
- **连接码建立信任**：目标不在 `machines_list`（对方没登录同账号/未登录 Hub）时，
  让对方在 Vantaloom「临时连接」面板读出 `vtl1-…` 连接码（8 分钟有效、单次使用），
  调 `machine_trust_with_code(code)` 即可在本机建立信任并把对方加入机器列表，返回
  其 `machineId`；之后用该 id 像操作其他机器一样操控对面，全程无需打开对面的
  Vantaloom 界面。信任约 1 小时无操作自动过期，失效后换新码重兑即可。
- **选工具**：一次性命令用 `run_command`（同步，远端约 55s 上限）；交互/长任务用
  `terminal_open/write/read/close`；文件用 `file_read/write/transfer`。
- **合并流**：`run_command` 的 `stdout` 是 stdout+stderr 合并后的完整输出（运行时
  按终端语义合并捕获）；`stderr` 只在传输层出错时才有值。
- **增量读终端**：`terminal_read` 首次 `after:0`，之后把上次的 `lastSequence`（驼峰）
  作为下次 `after`；调用会阻塞至多 `timeout_ms` 收集输出，空输出只表示暂无新内容。
  丢了 session_id 用 `terminal_list` 找回来——终端活在运行时里，不随本 MCP 退出。
- **生命周期**：本 MCP 由 agent 客户端拉起、随其退出，**与 Vantaloom 运行时各自
  独立**（谁先起都行）。终端会话活在运行时里：自己重启不会杀掉它们（但会丢
  session_id，用 `terminal_list` 找回），运行时重启则一次性全部作废。
- **二进制位置**：`<安装目录>/bin/vantaloom-mcp`（Windows 加 `.exe`；默认
  `D:\Vantaloom`、macOS `~/Applications/Vantaloom`）。别去翻目录——
  `vantaloom mcp` 打印路径与各客户端注册状态，`vantaloom mcp install` 一键注册。
- **限额**：远端命令约 55s；`file_read` 16MB；`file_transfer` 单次 192MB、目录
  1000 文件/512MB 且 `dest_dir` 必须已存在；`file_write` 不建父目录。命令语法由
  **目标机**的默认 shell 解释（Windows 是 PowerShell），跨机时先看 `platform`。
- **信任边界**：MCP 只连本机 `127.0.0.1:8780`，跨机由 Vantaloom 经 loomnet 的 mTLS
  通道代理；本 MCP 不持任何跨机凭据。单人模式下本机可信、工具即等同用户自己的
  Vantaloom 权限；多人协同下跨机命令受「使用终端」能力位约束。请勿在远程机器上
  未经用户明确意图执行破坏性命令。
