docs: describe the unified Agent topology and link controls
Align architecture, protocol, API, storage, security and deployment documentation with default mutual measurements and the Agent link board.
This commit is contained in:
1 parent
15f0023614
commit
c9beeee8e3
16 files changed
+363
-383
No files matched your search
+25
-29
@@ -8,7 +8,7 @@
|
||||
|
||||
- `PATCH /api/admin/agents/{id}` 新增可选 `ipv6_host`:IPv6 地址或域名;空字符串关闭 IPv6,缺省或 null 不修改。原 `public_host` 用于 IPv4。
|
||||
- `GET /api/public/overview` 新增可选 `ip_version=ipv4|ipv6`,缺省 IPv4。SSE `/api/public/live` 的同名参数选择 overview 主题的地址族;series 主题仍按线路 id 定位。
|
||||
- IPv4 线路 id 保持 `agent~probe`,IPv6 为 `agent~probe~ipv6`。series、distribution、heatmap、compare 和管理端测试端点共用这个标识。测试记录与评分叠加独立。
|
||||
- IPv4 线路 id 保持 `listener~dialer`,IPv6 为 `agent~dialer~ipv6`。series、distribution、heatmap、compare 和管理端测试端点共用这个标识。测试记录与评分叠加独立。
|
||||
- 线路、时序、测试和连接 DTO 新增 `ip_version`;管理端 Agent DTO 新增 `ipv6_host`、`ipv6_supported`;线路详情新增 `ipv6_available`(是否配置了 IPv6 地址)。公开 DTO 不包含任一种 IP 地址。
|
||||
- API v1 采用新增字段和可选参数,现有 IPv4 请求及历史链接继续有效。前端类型从 OpenAPI 重新生成。
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
/api/public
|
||||
├─ overview 首屏快照:所有线路的在线状态、最新指标、评分
|
||||
├─ live SSE 实时推送
|
||||
├─ lines/{line_id} 线路详情(线路 = Agent 与 Probe 的一对,id 形如 agt_…~prb_…)
|
||||
├─ lines/{line_id} 线路详情(线路 = Agent 的一对,id 形如 agt_…~agt_…)
|
||||
├─ lines/{line_id}/series 历史曲线(指标与评分)
|
||||
├─ nodes about ipv6 节点的展示信息(系统、国家、运营商,开启后带版本和 tag);后端的版本信息;IPv6 配置与采集诊断(不含地址)
|
||||
├─ compare 多条线路对比
|
||||
@@ -40,10 +40,9 @@
|
||||
├─ audit 管理操作审计日志
|
||||
├─ agents agents/{id} agents/{id}/{approve|disable|enable|takeover}
|
||||
├─ join-tokens
|
||||
├─ probes probes/{id} probes/{id}/revoke Probe(用 probe 角色的加入令牌注册、吊销、删除)
|
||||
├─ links links/{probe_id}/{agent_id}[/retry] 线路开关:哪些(Probe,Agent)连成线路(自动连接 + 手动选择),立即重试
|
||||
├─ links links/{dialer_id}/{agent_id}[/retry] 线路开关:哪些(拨号方,Agent)连成线路(自动连接 + 手动选择),立即重试
|
||||
├─ tasks tasks/{id} 线路模板、针对单个 Agent 的覆盖、目标任务
|
||||
├─ lines/{line_id}/tests 在某条线路(某个 Probe)上发起按需测试、列出近期测试;tests/{id}/cancel
|
||||
├─ lines/{line_id}/tests 在某条线路(某个 拨号方)上发起按需测试、列出近期测试;tests/{id}/cancel
|
||||
├─ raw/chunks raw/chunks/{id} 原始数据检索与下载
|
||||
├─ build-info 后端和每个节点的版本、tag、提交、构建时间、更新状态
|
||||
├─ releases release-key nodes/{id}/update CI 签名的发布清单、可信发布密钥;请求节点更新
|
||||
@@ -90,25 +89,25 @@
|
||||
|
||||
参数、响应示例和限额见 [query.md](../design/query.md)。
|
||||
|
||||
线路详情 `GET /api/public/lines/{line_id}` 带 `data_since`:这条线路最早的数据是什么时候(没有数据是 null),前端据此只提供「有数据」的时间范围。展示端的响应里没有 Probe 的版本和来源地址(那些只在管理端)。
|
||||
线路详情 `GET /api/public/lines/{line_id}` 带 `data_since`:这条线路最早的数据是什么时候(没有数据是 null),前端据此只提供「有数据」的时间范围。展示端的响应里没有 拨号方 的版本和来源地址(那些只在管理端)。
|
||||
|
||||
### 2.3.1 Agent 的主机资源占用
|
||||
|
||||
| 端点 | 内容 |
|
||||
| --- | --- |
|
||||
| `GET /api/public/hosts` | 每个对外可见的 Agent 和没被吊销的 Probe 的最新快照:`{id, kind, name, online, stale, last_seen_at, host}`(`kind` 是 `agent` 或 `probe`);超过 15 s 没有更新 `stale` 为真(离线,或者节点关了上报) |
|
||||
| `GET /api/public/hosts/{node_id}?range=1h` | Agent 或 Probe 的历史曲线(列式,按步长取平均;`range` 从 1 小时到 30 天):`cpu`、`mem`、`disk`(百分比)、`rx_bps`、`tx_bps` |
|
||||
| `GET /api/public/hosts` | 每个对外可见的 Agent 和没被吊销的 拨号方 的最新快照:`{id, name, online, stale, last_seen_at, host}`;超过 15 s 没有更新 `stale` 为真(离线,或者节点关了上报) |
|
||||
| `GET /api/public/hosts/{node_id}?range=1h` | Agent 或 拨号方 的历史曲线(列式,按步长取平均;`range` 从 1 小时到 30 天):`cpu`、`mem`、`disk`(百分比)、`rx_bps`、`tx_bps` |
|
||||
|
||||
`host` 只有汇总的数字:CPU 占用、内存与根分区的已用和总量、网卡累计收发字节与瞬时网速、系统运行时长。没有 CPU 型号、主机名、网卡名,也没有任何 IP——Agent 不采集这些,公开接口当然也不会返回;有测试断言响应里不出现这类字段。实时更新走 SSE 的 `hosts` 主题,历史每个 Agent 每分钟存一个样本(`retention.host_days`,默认 7 天)。
|
||||
|
||||
### 2.3.2 节点的展示信息
|
||||
|
||||
`GET /api/public/nodes`:看得见的 Agent 和没被吊销的 Probe 的展示信息,`{id, kind, os, os_name, arch, country, isp_key, isp_name, isp_manual}`——系统(`linux` / `windows` / `macos`)、「Debian 12」这样的系统名、架构(`x64` / `arm64`…)、国家代码(`CN`)、运营商类别(`mobile` / `telecom` / `unicom` / `broadnet` / `other`)和名字。没有 IP 地址、城市、ASN、经纬度,有测试保证:公开接口的整个响应里不出现 IP 形状的字符串,也没有 `ip`、`city`、`asn` 这些字段。管理员在设置里打开 `site.show_build` 之后,每项还有 `version` 和 `tag`(节点自己上报的程序版本和 tag);关着时这两个字段是 `null`。
|
||||
`GET /api/public/nodes`:看得见的 Agent 和没被吊销的 拨号方 的展示信息,`{id, kind, os, os_name, arch, country, isp_key, isp_name, isp_manual}`——系统(`linux` / `windows` / `macos`)、「Debian 12」这样的系统名、架构(`x64` / `arm64`…)、国家代码(`CN`)、运营商类别(`mobile` / `telecom` / `unicom` / `broadnet` / `other`)和名字。没有 IP 地址、城市、ASN、经纬度,有测试保证:公开接口的整个响应里不出现 IP 形状的字符串,也没有 `ip`、`city`、`asn` 这些字段。管理员在设置里打开 `site.show_build` 之后,每项还有 `version` 和 `tag`(节点自己上报的程序版本和 tag);关着时这两个字段是 `null`。
|
||||
|
||||
`GET /api/public/about`:`{show_build, backend}`。`show_build` 关着时 `backend` 是 `null`;打开后 `backend` 是 `{version, tag, built_at}`(后端自己的版本、tag、构建时间,不含提交号)。
|
||||
|
||||
- 系统和架构是节点自报的:Agent 和 Probe 都在各自连面板时的 `fleet.hello` 里自报。只有粗粒度的名字,没有主机名、内核版本、CPU 型号。
|
||||
- 国家和运营商由 Backend 用节点的来源地址向 IP 归属服务查一次(`MoShiro__GeoLookupUrl`,缺省 `https://ip.007995.xyz`,留空就不查):Probe 用 Agent 看到的来源 IP,Agent 用它的公开地址。Probe 和 Agent 不访问任何外部服务。只查公网地址(回环、内网、CGNAT、链路本地一律不发出去);同一个地址一周内只查一次,失败 10 分钟后再试;查到的只留国家代码和运营商,地址只存在 Backend 内部的 `node_meta.geo_host`,不对外。
|
||||
- 系统和架构是节点自报的:Agent 都在各自连面板时的 `fleet.hello` 里自报。只有粗粒度的名字,没有主机名、内核版本、CPU 型号。
|
||||
- 国家和运营商由 Backend 用节点的来源地址向 IP 归属服务查一次(`MoShiro__GeoLookupUrl`,缺省 `https://ip.007995.xyz`,留空就不查):拨号方 用 Agent 看到的来源 IP,Agent 用它的公开地址。Agent 不访问任何外部服务。只查公网地址(回环、内网、CGNAT、链路本地一律不发出去);同一个地址一周内只查一次,失败 10 分钟后再试;查到的只留国家代码和运营商,地址只存在 Backend 内部的 `node_meta.geo_host`,不对外。
|
||||
|
||||
### 2.4 测试结果与目录
|
||||
|
||||
@@ -131,15 +130,12 @@
|
||||
| PATCH | `/api/admin/agents/{id}` | `name`、`labels`、`public_host`、`ipv6_host`、`public_visible`;`public_host_auto` / `ipv6_host_auto` 为 true 时撤销手填、回到自动识别。手填就是手动,Agent 上报的地址不再覆盖;DTO 的 `public_host_auto` / `ipv6_host_auto` 表示当前是不是自动 |
|
||||
| POST | `/api/admin/agents/{id}/approve` `/disable` `/enable` `/takeover` | 见 [enrollment.md](../security/enrollment.md) |
|
||||
| DELETE | `/api/admin/agents/{id}` | 删除;历史数据按保留策略自然过期 |
|
||||
| GET/POST | `/api/admin/join-tokens` | 创建时返回令牌明文和安装命令(仅此一次);`role` 是 `agent`(默认)或 `probe`,决定用它注册出来的是哪种节点 |
|
||||
| GET/POST | `/api/admin/join-tokens` | 创建时返回令牌明文和安装命令(仅此一次);`role` 统一为 `agent`(默认) |
|
||||
| DELETE | `/api/admin/join-tokens/{id}` | 吊销 |
|
||||
| GET | `/api/admin/probes` | 列出所有 Probe(含它现在连着哪些 Agent:版本、远端 IP、起始时间);Probe 由 `role=probe` 的加入令牌注册,密钥由它自己生成 |
|
||||
| GET/PATCH/DELETE | `/api/admin/probes/{id}` | 详情、改名、删除(历史数据按保留策略自然过期) |
|
||||
| POST | `/api/admin/probes/{id}/revoke` | 吊销:公钥从所有 Agent 的名单里移除,Agent 立即断开它;记录和历史保留 |
|
||||
| POST | `/api/admin/agents/{id}/probes/{probe_id}/disconnect` | 让这个 Agent 断开这个 Probe 的会话(Probe 会重连,除非已吊销);没有会话时返回 409 `probe_offline` |
|
||||
| POST | `/api/admin/agents/{id}/dialers/{dialer_id}/disconnect` | 让这个 Agent 断开这个 拨号方 的会话(拨号方 会重连,除非已吊销);没有会话时返回 409 `dialer_offline` |
|
||||
| GET/POST | `/api/admin/tasks` | 列表和创建:line 模板(不带 `agent_id`)、单个 Agent 的覆盖(带 `agent_id` + `base_task_id`)、target 任务 |
|
||||
| GET/PATCH/DELETE | `/api/admin/tasks/{id}` | 参数按 Kind 的 `ParamSpec` 校验,并检查带宽预算 |
|
||||
| POST | `/api/admin/lines/{line_id}/tests` | `{plan, params?}` → 202 + `Location: /api/public/tests/{id}`;Probe 离线时返回 409 `probe_offline`,节点繁忙时返回 409 `test_busy` |
|
||||
| POST | `/api/admin/lines/{line_id}/tests` | `{plan, params?}` → 202 + `Location: /api/public/tests/{id}`;拨号方 离线时返回 409 `dialer_offline`,节点繁忙时返回 409 `test_busy` |
|
||||
| POST | `/api/admin/tests/{id}/cancel` | |
|
||||
| GET | `/api/admin/raw/chunks?agent_id=&kind=&from=&to=&test_id=` | 原始块元数据 |
|
||||
| GET | `/api/admin/raw/chunks/{chunk_id}` | 下载原始块(`application/vnd.moshiro.rawchunk`);过期返回 410 `raw_expired` |
|
||||
@@ -150,19 +146,19 @@
|
||||
| GET/POST/DELETE | `/api/admin/auth/passkeys`、`/auth/passkeys/options`、`/auth/passkeys/{id}` | 已登录用户管理自己的 passkey:列出、取注册挑战、注册、删除(API key 不能用) |
|
||||
| GET | `/api/admin/capabilities` | 能力目录:分组、哪些改变信任、哪些是预留、依赖关系和预设(只读、运维、接入、CI 发布、全部) |
|
||||
| GET/PATCH | `/api/admin/settings` | 站点公开或私有、是否对访客显示构建信息(`site.show_build`)、保留期、节点定时重启(`fleet.restart_enabled`、`fleet.restart_hour`、`fleet.restart_minute`、`fleet.restart_utc_offset_minutes`,默认开、04:30、UTC+8;改完立即通知在线节点)等 |
|
||||
| GET | `/api/admin/build-info` | 后端和每个 Agent、Probe 的版本、tag、提交、构建时间、系统,以及节点宿主机更新器报告的更新状态(管理端「系统 → 版本」) |
|
||||
| GET | `/api/admin/build-info` | 后端和每个 Agent 的版本、tag、提交、构建时间、系统,以及节点宿主机更新器报告的更新状态(管理端「系统 → 版本」) |
|
||||
| GET | `/api/admin/releases` | 每个组件最新的一份签名发布清单 |
|
||||
| GET/PUT/DELETE | `/api/admin/release-key` | 可信资产来源:GET 看官方发布密钥和(如果有)自有发布公钥的指纹;PUT `{public_key}` 另外信任自己的发布公钥(P-256 的 PEM,拒绝私钥);DELETE 停用它,只有它签过的已登记清单会被丢掉。官方密钥始终信任。PUT/DELETE 需要 `update.confirm`,写审计 |
|
||||
| POST | `/api/release/offer` | CI 登记发布清单的入口,不需要令牌:`{manifest, signature}` 的签名必须能用 Backend 内嵌的官方发布公钥或已开启的自有发布公钥验过(ECDSA P-256 / SHA-256,DER),否则 403;`seq` 必须比已登记的新,否则 409。登记不依赖面板里的任何凭证,清库、重装、还没有 passkey 时照常工作 |
|
||||
| GET | `/api/admin/links` | 每一对(Probe,Agent)的线路开关:`linked`(最终结果)、`override`(手动设置,`null` 跟随自动连接)、`connected`,以及 `auto_link` |
|
||||
| PUT | `/api/admin/links/{probe_id}/{agent_id}` | `{enabled: true|false|null}`:手动强制连或不连,`null` 取消手动设置;改完立即重新下发 |
|
||||
| POST | `/api/admin/nodes/{id}/update` | 请求一个在线的 Agent 或 Probe 更新到它收到的清单(节点不在线返回 409) |
|
||||
| GET | `/api/admin/links` | 每一对(拨号方,Agent)的线路开关:`linked`(最终结果)、`override`(手动设置,`null` 跟随自动连接)、`connected`、`connection_state`(`disabled`、`dialer_unavailable`、`listener_unavailable`、`waiting_report`、`control_connecting`、`control_down`、`task_connecting`、`task_retrying`、`connected`,含义见 [architecture.md](../design/architecture.md) 第 4.3 节),以及 `auto_link` |
|
||||
| PUT | `/api/admin/links/{dialer_id}/{agent_id}` | `{enabled: true|false|null}`:手动强制连或不连,`null` 取消手动设置;改完立即重新下发 |
|
||||
| POST | `/api/admin/nodes/{id}/update` | 请求一个在线的 Agent 或 拨号方 更新到它收到的清单(节点不在线返回 409) |
|
||||
| GET/POST/PATCH | `/api/admin/users`、`/api/admin/users/{id}` | 用户列表、创建、修改密码 |
|
||||
| GET/POST | `/api/admin/auth/setup` | 首次认领:GET 返回面板是否尚无用户;POST 凭后端日志中的认领码创建第一个管理员,响应同密码登录,已有用户后关闭 |
|
||||
| GET/POST | `/api/admin/trust` | 操作员集合(信任根):GET 返回创世指纹、版本、与已注册 passkey 是否一致;POST 提交 passkey 签名的下一版集合 |
|
||||
| GET | `/api/admin/audit` | 管理操作审计日志,按时间倒序 |
|
||||
| GET | `/api/admin/lines/{line_id}/tests` | 该线路的近期测试 |
|
||||
| POST | `/api/admin/links/{probe_id}/{agent_id}/retry` | 启用该线路并立即让 Probe 重试连接 |
|
||||
| POST | `/api/admin/links/{dialer_id}/{agent_id}/retry` | 启用该线路并立即让 拨号方 重试连接 |
|
||||
| GET | `/api/admin/nodes/{id}/logs` | 节点近期运行日志(经心跳上传) |
|
||||
| GET/POST | `/api/admin/backend/update`、GET `/backend/update/live` | Backend 自身的更新状态、管理员确认更新,以及 SSE 通知 |
|
||||
|
||||
@@ -189,7 +185,7 @@
|
||||
| `GET /metrics` | admin 令牌 | Prometheus 格式:在线数、块入库速率、分析队列长度、重算进度、磁盘占用、SSE 连接数 |
|
||||
| `GET /openapi/v1.json` | 无 | |
|
||||
| `GET /install`(也接受 `/install.sh`)、`GET /install.ps1`、`GET /install/openwrt.sh` | 无 | 静态安装脚本,原样返回、不替换任何值,所以哈希在一个版本内固定;角色、面板地址、代码和属主走环境变量 |
|
||||
| `GET /install/compose/{file}`、`GET /install/quadlet/{file}` | 无 | 静态的 compose 文件与 Quadlet 单元(`agent`、`probe`、`backend`),所有人拿到的字节相同 |
|
||||
| `GET /install/compose/{file}`、`GET /install/quadlet/{file}` | 无 | 静态的 compose 文件与 Quadlet 单元(`agent`、`backend`),所有人拿到的字节相同 |
|
||||
| `GET /install/manifest?code=…`、`GET /install/manifest.sig?code=…` | 无(凭一次性代码) | 该角色已签名的发布清单(原样)和签名,安装脚本据此验签、按 digest 拉镜像;代码不能用就是 404 |
|
||||
|
||||
## 6. 错误码(节选,完整列表见共享的 `ErrorCode`)
|
||||
@@ -200,7 +196,7 @@
|
||||
| 401 | `unauthorized`、`csrf_failed` |
|
||||
| 403 | `forbidden`(凭证缺少端点需要的能力)、`site_private` |
|
||||
| 404 | `agent_not_found`、`task_not_found`、`test_not_found`、`chunk_not_found`、`unknown_kind`、`unknown_plan` |
|
||||
| 409 | `name_conflict`、`probe_offline`、`test_busy`、`agent_not_active` |
|
||||
| 409 | `name_conflict`、`dialer_offline`、`test_busy`、`agent_not_active` |
|
||||
| 410 | `raw_expired`、`join_token_expired` |
|
||||
| 422 | `range_too_large`、`invalid_step`、`unknown_metric`、`unknown_preset`、`too_many_metrics` |
|
||||
| 429 | `too_many_requests`、`too_many_streams` |
|
||||
@@ -208,7 +204,7 @@
|
||||
|
||||
## 7. 设计约束
|
||||
|
||||
本文是所有 Backend REST 端点都必须遵守的规则;具体端点见上面第 1–6 节。规则参考了 [Microsoft Azure REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md) 和 RFC 9457,并根据本项目做了取舍。内部协议(Probe↔Agent、Agent↔Backend)的兼容规则见 [protocol.md](../design/protocol.md) 第 4 节。
|
||||
本文是所有 Backend REST 端点都必须遵守的规则;具体端点见上面第 1–6 节。规则参考了 [Microsoft Azure REST API Guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md) 和 RFC 9457,并根据本项目做了取舍。内部协议(拨号方↔Agent、Agent↔Backend)的兼容规则见 [protocol.md](../design/protocol.md) 第 4 节。
|
||||
|
||||
### 7.1 契约生成链路
|
||||
|
||||
@@ -247,7 +243,7 @@ web:openapi-typescript 生成类型,调用方的类型错误会直接暴露
|
||||
|
||||
- 按调用方分层:`/api/public`(只读展示)、`/api/admin`(管理)、`/api/agent`(节点)。同一个资源在不同层中可以有不同的 DTO,例如 public 层不暴露 `pubkey`、`local_limits`。
|
||||
- 路径全部小写,资源名用复数名词,多个单词用 `-` 连接:`/api/admin/join-tokens`。
|
||||
- 嵌套最多一层;子资源一旦有全局唯一 ID,就提供顶层路径(`/probes/{probe_id}`)。
|
||||
- 嵌套最多一层;子资源一旦有全局唯一 ID,就提供顶层路径(`/agents/{dialer_id}`)。
|
||||
- 查询参数和 JSON 字段使用 snake_case。
|
||||
|
||||
| 操作 | 方法 | 成功状态 | 说明 |
|
||||
@@ -270,7 +266,7 @@ web:openapi-typescript 生成类型,调用方的类型错误会直接暴露
|
||||
| 延迟 | 带小数的毫秒值(指标字典中的单位见目录) | `rtt_p95_ms: 58.93` |
|
||||
| 百分比 | 0–100 的数字 + `_pct` | `loss_pct: 0.5` |
|
||||
| 计数 | 整数,不使用浮点数 | `attempts: 600` |
|
||||
| ID | 带类型前缀的 ULID 字符串 | `prb_01J9Z...` |
|
||||
| ID | 带类型前缀的 ULID 字符串 | `agt_01J9Z...` |
|
||||
| 枚举 | snake_case 字符串 | `"udp_echo"` |
|
||||
| 缺失值 | 字段始终存在;未知值写 `null`,不省略字段 | `"last_seen_at": null` |
|
||||
| 布尔值 | 使用 `is_`/`has_` 前缀,或者用意义明确的形容词 | `online`、`enabled` |
|
||||
@@ -301,7 +297,7 @@ web:openapi-typescript 生成类型,调用方的类型错误会直接暴露
|
||||
| 401 | 未认证 |
|
||||
| 404 | 资源不存在 |
|
||||
| 403 | 已认证但作用域不足 |
|
||||
| 409 | 名称冲突、状态冲突(例如 `probe_offline`) |
|
||||
| 409 | 名称冲突、状态冲突(例如 `dialer_offline`) |
|
||||
| 410 | 资源曾经存在但已过保留期(例如 `raw_expired`) |
|
||||
| 412 | `If-Match` 不匹配 |
|
||||
| 413 | 请求体超过上限 |
|
||||
@@ -317,7 +313,7 @@ web:openapi-typescript 生成类型,调用方的类型错误会直接暴露
|
||||
|
||||
### 7.7 并发控制
|
||||
|
||||
- 配置类资源(Agent、Probe、任务)在响应中返回 `ETag`,PATCH/DELETE 支持 `If-Match`,不匹配时返回 412。没有带 `If-Match` 的请求按“最后写入者获胜”处理。
|
||||
- 配置类资源(Agent、任务)在响应中返回 `ETag`,PATCH/DELETE 支持 `If-Match`,不匹配时返回 412。没有带 `If-Match` 的请求按“最后写入者获胜”处理。
|
||||
|
||||
### 7.8 安全
|
||||
|
||||
|
||||
Reference in new issue
Block a user