# 原生节点、路由与文件同步

两种权威模式使用相同工具。`server` 是 `ssh_list_servers` 的名字；`@local` 是正在执行工具的伴随器电脑。`deviceId` 是原生身份的稳定 UUID，中心中的另一个 `@local` 不代表当前电脑。地址必须注明所在设备，不能把多台机器的 `127.0.0.1` 混为一处。

## 节点和连接

1. 用 `ssh_node_manage install` 安装被授权的服务器节点，或用 `server=@local` 初始化本机内嵌身份。包内已带两平台程序，无需安装 FRP 或完整 EasyTier。
2. `configure` 使用 inspect 返回的 revision 和公钥，只授权所需目标、反向监听、发现对等点或短期文件根。P2P 配置支持 UDP/QUIC、TCP 及 `auto`。公网协助者只发布候选地址，除非另有明确转发授权，不承担业务数据转发。
3. `start` 启用该节点，`pair` 配对已有节点；双方 connectivity 必须启用。若用协助者，其 coordinate 能力、互相发现权限和可达地址均需具备。`pair` 的 forwards 可省略，文件同步仅需要身份和原生链路。
4. `inspect` 检查 loaded revision、实际 transport/peer、错误和时间戳，再通过普通 SSH 或文件操作验收。`revoke` 撤回某 peer 并持久化拒绝记录；后续路由重编译不会擅自恢复权限。用户显式配对/配置才可重新授权。
5. `upgrade` 切换已安装节点版本，保持身份；停止的节点保持停止。Windows 服务升级会确认旧服务已停止，再启动替代程序；超时失败时检查回执中的恢复状态。`uninstall` 退役该实例的服务和目录登记，保留身份/不可变发布文件以供恢复；仍被共享资源引用时拒绝。本机启动项见 [INSTALL.md](INSTALL.md)。伴随器版本交接会重建内嵌节点传输；进行中的原生流可能中断，命令不自动重放，已验证文件块可继续复用。

NAT 规则会影响穿透；TCP/QUIC 都不能保证所有网络可达。自动协议选择和允许 SSH 回退是不同选项，结果会标出实际采用的路径。

节点安装、配置、升级、撤权和资源探测沿目录中管理 profile 的原 SSH/ProxyJump 路径执行，使用独立连接池；普通命令和文件继续沿所选数据路由执行。管理 profile 必须具备可用的独立 SSH 通路，数据路由退役不会撤掉该管理访问。`ssh_connection_status` 分别列出普通连接和 management_connections。

## 路由与共享资源

`ssh_node_manage discover` 先登记已有机器身份；它可用于没有原生节点的历史 SSH 隧道设备。`ssh_route_inspect` 返回目录中的设备、期望路由、资源及时间戳观测；`probe=true` 增加当前设备的 SSH 认证和所选资源的服务/监听检查。大量资源用 `resourceIds` 分批选择。监听可达不是目标 SSH 身份验收。

`ssh_route_plan` 支持 create/update/adopt/repair/disable/remove/rollback。完整 SSH 跳板链使用 `via`；原生入口额外指定 `sourceDevice`、`sourceInstance` 与 `ingress`。`resourceIds` 把路由与资源关联，`resources` 提交节点、反向通道、转发或历史 SSH 服务的结构化变更。资源有独立 owner、revision、dependencies、enabled 和 persistent。精确对象字段见 [tools.json](../schemas/tools.json)。

plan 只保存五分钟方案，不改变网络；检查方案后用其 `planId` 和 `expectedRevision` 调用 `ssh_route_apply`。apply 记录运行意图，应用节点权限、验证原始目标 SSH 身份后提交。旧命令不重放；共享节点/权限只在最后一项相关资源退役时清除。外部服务接管需匹配现有可执行文件、启动配置与真实 SSH `-R` 映射，不按备注猜测。共享映射、孤立进程或会切断管理通路的停止操作会被拒绝。手动启动且没有监督者的 SSH 反向进程使用 external 的 backend=process、persistent=false 登记；service 此时是观测标签。它只记录进程和地址关系，不允许 start/stop/repair，移除记录不会结束原进程。

发生失败时看 apply 收据。只有仍持有有效执行租约、且节点没有独立修改时才能自动恢复；超时或部分恢复会标为 recovery_required。依据实时 inspect 建立新 repair 方案，不能盲目重放旧方案。

## 单源多端分发

```json
{
  "source": {"id":"origin","server":"@local","root":"/home/me/data"},
  "targets": [
    {"id":"b","server":"device-b","root":"/srv/data"},
    {"id":"c","server":"device-c","root":"C:\\Data"}
  ],
  "options": {"dryRun":true,"delete":false,"compression":"auto","timeout":300000}
}
```

复制根目录中的内容，不额外套一层根目录名。先只读预览，再使用相同参数并把 dryRun 改为 false。远端需先安装 v6 原生节点/文件助手。`delete=true` 才删除目标多余文件；排除项保护匹配的子树。普通上传下载沿当前 SSH 路由执行；此工具为同步提供清单、内容哈希、块复用、断点续传、压缩和原子提交。

原生文件控制使用当前连接器的主 `@local` 节点（安装、启动时省略 instance），它需与每个远端配对；远端之间还需彼此配对。一项原生操作每台设备使用一个根目录。远端到远端优先由接收端拉取，A 端只协调。原生链路不可用时，默认不通过 A 端中继文件；仅在用户允许时传 `allowRelay=true`。中心始终不接收文件字节。`transport=p2p` 要求原生链路，`ssh` 使用各端 SSH 路由，`auto` 优先原生。回执记录来源/目标、实际数据路径、字节统计、失败项和清理结果。文件载荷统计不含协议头；失败传输的总字节数无法确认时，byteCountsComplete=false、filePayloadBytes=null，committedFilePayloadBytes 只计已确认提交的文件。

原生同步的主控和文件端点应使用配套候选程序；从早期 6.0 候选更新时，先用 `ssh_node_manage upgrade` 更新文件端点。同步会合并节点控制请求，并行扫描、授权和连接不同端点，逐文件提交与冲突顺序保持有序。

`timingsMs` 分别记录端点扫描、规划/租约、授权、建连、执行、收尾和撤权的墙钟耗时；它们与各端文件数据阶段的 `transferMs` 不同。`scanMs` 仅汇总端点本机扫描时间。工具返回的 `elapsedMs` 包含本次执行及授权清理；完整调用还可能包含入口的目录加载和 IPC 等待。持久收据的耗时截至最后一次收据写入之前。

## 双向同步集

先创建持久同步集：

```json
{
  "action":"create",
  "name":"共享资料",
  "participants":[
    {"id":"a","server":"@local","root":"/home/me/shared"},
    {"id":"b","server":"device-b","root":"/srv/shared"}
  ],
  "propagateDeletes":true,
  "exclude":[".git","**/*.tmp"]
}
```

后续执行 `{"syncSet":"返回的 UUID","options":{"dryRun":true}}` 或去掉 dryRun 正式运行。`list` 查同步集，`inspect` 查策略、版本、墓碑和最近收据。参与设备、排除项与删除策略由同步集固定，不可在 run 中临时改写。

各端修改用因果版本比较。并发编辑保留确定命名的 `.sshmcp-conflict-<内容哈希>` 副本；删除与编辑并发时保留编辑。已观察到的删除保留版本墓碑，离线旧副本再次上线不会被误认为新修改。未能读取的端和缺少的内容会延期并报告；先保留冲突内容，再替换原路径。

同步集最多 8 个参与端、4096 个跟踪文件，组合清单最多 100000 项；大目录按独立职责拆分。双向集跟踪普通文件及其必要父目录，空目录不作为因果版本对象。同步按工具调用执行，没有默认持续监控。各文件原子提交，整批不是跨设备事务。

## 文件授权

原生文件能力绑定已认证身份、绝对根目录、操作权限和最长 24 小时的任务有效期。根目录身份固定；拒绝符号链接、硬链接、Windows 保留名与目录穿越。接收端只能向任务中声明的来源拉取。取消、超时和撤权阻止后续提交；已验证的临时块可用于恢复，正式文件只有完整哈希通过才替换。

所有工具 `timeout` 是毫秒；文件任务 `expiresAt` 是 Unix 秒时间戳。只读预览不会创建目标目录、临时文件、文件授权或同步基线。

失败回执中的 `filePayloadBytes=null`、`byteCountsComplete=false` 表示实际传输字节数不完整。`dataPaths`、`remoteDataPath` 和 `committedFilePayloadBytes` 只汇总已经完成的文件操作；发生中断时不能据此推断网络从未发送数据。
