61 Commits
Author SHA1 Message Date
justme 71d2866b9c docs: update the architecture [SKIP TESTS] 2026-10-07 01:21:49 +08:00
justme 48664c0ada docs: remove the roadmap and bring the docs in line with the current code [SKIP TESTS]
Probes link to the panel directly, container nodes update through signed
releases, the frontend exists and the ports changed. The roadmap is gone; its
open items live in the README.
2026-10-06 23:27:26 +08:00
justme 198ee556d6 ci: sync main into dev [CI SKIP] 2026-10-06 23:22:52 +08:00
justme da465866d5 fix(web): lift the hovered overview card above its neighbours so the popover is not hidden 2026-10-06 23:22:36 +08:00
justme 3a751a0f89 feat: choose which probes link to which agents from the admin
The panel decides the probe/agent pairs and probes always dial out. A new
fleet.auto_link setting connects every probe to every agent (default); with it
off only the pairs chosen by hand connect, and any pair can be forced on or
off. The Probes page has a link matrix with the switch and per-pair toggles.
2026-10-06 23:19:32 +08:00
justme d69581dcb5 docs: mount the host os-release and fix the agent public host after tunnel enrollment [SKIP TESTS] 2026-10-06 23:11:26 +08:00
justme 8f44e924a4 docs: describe container node deployment and the update flow [SKIP TESTS] 2026-10-06 23:02:16 +08:00
justme 0c39485a78 ci: sync main into dev [CI SKIP] 2026-10-06 22:57:11 +08:00
justme 7a71bf57f9 feat: update controls in the admin, release token scope, build info in CI images
The versions page shows each node's update state with update buttons, API
tokens get a release scope that can only register releases (used by CI), and
the CI image and release builds now pass the tag, commit and build time.
2026-10-06 22:56:56 +08:00
justme 22a98f2eb0 ci: sync main into dev [CI SKIP] 2026-10-06 22:42:20 +08:00
justme 0a66f5e07a feat: signed releases and a host-side updater for container nodes
CI signs the image digest of each agent and probe release; the panel only
stores and forwards it. Nodes write the manifest to their data directory and
a host updater verifies the signature with a local public key, pulls by
digest, restarts the unit and rolls back when it is unhealthy. The panel can
list releases, ask a node to update and show the reported update state.
2026-10-06 22:37:26 +08:00
justme 48072737c9 feat!: probes connect to the backend directly
A probe now links to the backend like an agent (probe.json carries the
server address). The backend sends the agent list as desired state and the
probe reports its platform and build in its own hello. The agent no longer
relays the list or the probe platform. Existing probes must be re-created.
2026-10-06 22:25:16 +08:00
justme 604cbe3123 fix(ci): restore with PublishAot so node images are native, add compose files
Agent and probe images were built with a restore step that lacked
PublishAot, so the publish silently produced a trimmed launcher that looks
for a .dll. Restore now passes PublishAot and the build fails if the binary
is smaller than 2 MB. Also add agent and probe compose files and document
the [CI] marker in AGENTS.md.
2026-10-06 21:43:46 +08:00
justme b242701e9e ci: sync main into dev [CI SKIP] 2026-10-06 21:24:47 +08:00
justme 83f54978d2 feat(web): acrylic theme, node flags and icons, build info
Acrylic cards and topbar over a blurred background, country flags, ISP and
OS icons, per-node version tags, a build info footer and an admin versions
tab. Overview cards drop the sparkline and widen the hover area.
2026-10-06 21:22:04 +08:00
justme 1e5c660d05 feat(backend): report node platform, geo and build info
Agents and probes report OS, arch and build (version, tag, commit, time).
The backend looks up country and ISP of public addresses only, stores them
in node_meta (schema v5) and exposes display info via /api/public/nodes,
/api/public/about and /api/admin/build-info behind site.show_build.
2026-10-06 21:22:04 +08:00
justme 2e470607fa docs: add project agent instructions [SKIP TESTS] 2026-10-06 17:11:07 +08:00
justme a70b89b95c feat(web): show UDP and TCP strips on overview cards
Add 3/6/12 hour ranges with status strips, latency sparklines and a side popover for the hovered interval.
2026-10-06 16:59:17 +08:00
justme a9538eae4a feat(web): add backend switcher and connection status
Split the login page into backend list and credentials, show connection status in the sidebar and top bar, and render dialogs in a portal.
2026-10-06 16:59:17 +08:00
justme 91c63d0cf2 feat(api): add return_session login for cross-origin clients
Return the session token in the login response instead of a cookie, and accept it as a Bearer token without CSRF.
2026-10-06 16:59:16 +08:00
justme 193fc3a95c feat: improve agent management and speed up tests 2026-10-06 15:48:46 +08:00
justme 134c06d605 merge: bring line detail timeline into dev [CI SKIP] 2026-10-06 14:52:48 +08:00
justme d1be70cda2 feat(web): 线路详情页的时间轴、EWMA 平滑和加载进度
- 先选日期,再在连续 48 小时(或 24 小时)的丢包缩略图上拖选区;选区可以跨零点,
  点轴或刻度时离得近的端点贴过去,悬停预览会动的端点;没有数据的时段画成斜纹
- 刻度下方的时长快捷档:30 分钟、1 小时、3 小时、6 小时、12 小时、整天
- 指标卡片平铺,丢包等标明 UDP/TCP;图表按 UDP、TCP、评分分组
- 图表数据刷新时原地更新(不再整张重建),毫秒到 1 秒以上显示成秒
- EWMA 平滑开关(关/轻/中/重),原始数据以淡线垫底
- 数据加载时卡片上显示进度条和占位;useQuery 区分定时刷新和用户触发的加载
- 前端可以在页面里指定后端地址和令牌(配合后端 Monitor__CorsOrigins)
2026-10-06 14:48:58 +08:00
justme 672f6e3dcb fix(ci): emit native apphosts in fastlane images [CI SKIP] 2026-10-05 10:42:34 -07:00
justme 9a375ffd9e fix(build): select AotAnywhere explicitly [CI SKIP] 2026-10-05 10:37:10 -07:00
justme 407c09f6a5 fix(ci): use host networking for native builds [CI SKIP] 2026-10-05 10:30:35 -07:00
justme fffee8937c ci: sync main into dev [CI SKIP] 2026-10-05 10:26:53 -07:00
justme 91485db500 fix(ci): fall back to git diff for empty path lists [CI SKIP] 2026-10-05 10:25:59 -07:00
justme fa34869f18 ci: sync main into dev [CI SKIP] 2026-10-05 10:21:22 -07:00
justme deb5a97802 ci: add dependency-aware main fastlane [CI SKIP] 2026-10-05 10:18:22 -07:00
justme 479f8c25b0 feat(ci): publish agent and probe OCI images [CI SKIP] 2026-10-05 09:33:22 -07:00
justme 2b80bb66d1 ci: use local Gitea endpoint for release uploads 2026-10-05 08:44:19 -07:00
justme 078eacf1cb ci: persist Buildah cache for Linux releases 2026-10-05 08:29:08 -07:00
justme 3db6d5b159 ci: authenticate Zot with API key for justme [CI SKIP] 2026-10-05 22:24:16 +08:00
justme a7a8212c08 ci: push images to public Zot over HTTPS [CI SKIP] 2026-10-05 16:28:15 +08:00
justme 68406843de ci: retry transient Zot registry errors [CI SKIP] 2026-10-04 13:26:09 -07:00
justme fae8c45c90 ci: use bounded runspaces for artifact uploads [CI SKIP] 2026-10-04 13:11:01 -07:00
justme 3bf12ec73d ci: upload release artifacts concurrently [CI SKIP] 2026-10-04 13:05:17 -07:00
justme a9ee4ec34c build: disable PDB debug symbols in releases [CI SKIP] 2026-10-04 12:54:39 -07:00
justme 6b12049df0 ci: drop 32-bit release targets [CI SKIP] 2026-10-04 12:44:32 -07:00
justme d4b4869501 fix(ci): package cross-platform tarballs on Windows [CI SKIP] 2026-10-04 12:38:13 -07:00
justme 92b72d4443 ci: release without blocking on test workflow [CI SKIP] 2026-10-04 12:31:27 -07:00
justme 67b428991f ci: parallelize NativeAOT target builds 2026-10-04 12:29:35 -07:00
justme 55756f7695 ci: cross-publish full NativeAOT matrix from Windows 2026-10-04 12:10:06 -07:00
justme 17a69c1c7a ci: parallelize release components 2026-10-04 11:44:41 -07:00
justme 50d8c07939 ci: gate parallel releases on verified commits 2026-10-05 02:39:21 +08:00
justme 2e549b230d build: use ASP.NET runtime for backend images 2026-10-05 02:24:15 +08:00
justme ace42e5da7 fix(ci): use POST for Gitea release assets 2026-10-05 02:16:14 +08:00
justme ad32afd038 ci: run release builds in parallel 2026-10-05 02:11:29 +08:00
justme 286c9ff7cd ci: publish release assets to Gitea 2026-10-05 02:06:36 +08:00
justme 8aaa574485 fix(ci): make release retryable 2026-10-05 01:21:37 +08:00
justme ce30828ed3 ci: use runner-local Gitea package endpoints 2026-10-05 01:10:17 +08:00
justme a46f45cce3 ci: order multiarch index after builds 2026-10-05 01:05:24 +08:00
justme ce3ba51c02 test: normalize project reference paths across platforms 2026-10-05 00:49:20 +08:00
justme faf1203f0a ci: disable git lfs in clone steps 2026-10-05 00:43:46 +08:00
justme ee422ca366 ci: run verification for push, pull requests and tags 2026-10-05 00:41:29 +08:00
justme c33003c279 ci: restore public Gitea package endpoint 2026-10-04 23:11:37 +08:00
justme 31af31b96f ci: use internal Gitea endpoint 2026-10-04 23:01:59 +08:00
justme 9b6c4e0fd8 ci: skip host OpenAPI generation for arm64 2026-10-04 22:38:26 +08:00
justme ceef543854 ci: verify development branch 2026-10-04 22:20:58 +08:00
justme 299f86dbee ci: configure Woodpecker release pipelines 2026-10-04 22:12:21 +08:00
10 changed files with 63 additions and 168 deletions

No files matched your search

+11 -1
View File
@@ -64,7 +64,7 @@ npm ci --prefix web
npm run dev --prefix web npm run dev --prefix web
``` ```
打开 <http://localhost:5173/> 使用展示端,或访问 <http://localhost:5174/admin/> 进入管理端。依赖安装和前端配置见 [`web/README.md`](web/README.md);Backend 的首次启动与配置见 [`backend/README.md`](backend/README.md)。 打开 <http://localhost:5173/> 使用展示端,或访问 <http://localhost:5173/admin/> 进入管理端。依赖安装和前端配置见 [`web/README.md`](web/README.md);Backend 的首次启动与配置见 [`backend/README.md`](backend/README.md)。
### CI 构建规则 ### CI 构建规则
@@ -89,6 +89,16 @@ npm run dev --prefix web
| `packaging/`、`scripts/` | 部署、构建与维护脚本 | | `packaging/`、`scripts/` | 部署、构建与维护脚本 |
| `docs/` | [架构](docs/architecture.md)、[测量模型](docs/measurement.md)、[API](docs/api.md)、[部署](docs/deployment.md)等文档 | | `docs/` | [架构](docs/architecture.md)、[测量模型](docs/measurement.md)、[API](docs/api.md)、[部署](docs/deployment.md)等文档 |
## 待办
已实现的能力见上面的概览和 `docs/`;还没做的:
- **纯二进制节点的更新**:`fleet-updater`、`fleet-release`、Windows 服务和 OpenWrt(procd)适配器;设计见 [`docs/fleet/update.md`](docs/fleet/update.md)。容器节点(Agent、Probe)的签名发布和宿主机更新已经实现,见 [`docs/deployment.md`](docs/deployment.md)。
- **更新的灰度和自动策略**:按标签分批发布;`POLICY=auto` 的线上验证;后端自身的更新。
- **OpenWrt 打包**:procd 脚本、ipk/apk 包、sysupgrade 保留列表,在 aarch64 路由器上长时间运行验证。
- **安全加固**:对 UDP 包、控制帧、RawChunk、JSON-RPC 做模糊测试;伪造签名和重放的专项测试。
- **以后**:告警(Webhook)、真实 QUIC 握手积木、pcap/eBPF 采集。
## 测试 ## 测试
```sh ```sh
+1 -1
View File
@@ -46,6 +46,6 @@ monitor-probe --config /etc/monitor/probe.json
## 设计说明 ## 设计说明
- Probe 只执行编译进程序的测量模块。它不认识 Backend,不落盘测量数据,也不能执行命令或自行更新。 - Probe 只执行编译进程序的测量模块。它不落盘测量数据,也不能执行命令或自行更新(更新由宿主机上的更新器完成,Probe 只写清单和请求文件)。
- 测量模块由 `backend/shared/Monitor.Measurement/` 与 Agent 共用;Probe 负责发起连接、调度任务和记录本端数据。 - 测量模块由 `backend/shared/Monitor.Measurement/` 与 Agent 共用;Probe 负责发起连接、调度任务和记录本端数据。
- 测量模型和协议细节见 [`../../docs/measurement.md`](../../docs/measurement.md) 与 [`../../docs/protocol.md`](../../docs/protocol.md);整体架构见 [`../../docs/architecture.md`](../../docs/architecture.md)。 - 测量模型和协议细节见 [`../../docs/measurement.md`](../../docs/measurement.md) 与 [`../../docs/protocol.md`](../../docs/protocol.md);整体架构见 [`../../docs/architecture.md`](../../docs/architecture.md)。
+5 -2
View File
@@ -7,7 +7,7 @@
```text ```text
/api/public/* 只读。站点设为公开时匿名可访问,设为私有时需要登录或只读令牌; /api/public/* 只读。站点设为公开时匿名可访问,设为私有时需要登录或只读令牌;
public_visible = false 的 Agent 对匿名访客隐藏 public_visible = false 的 Agent 对匿名访客隐藏
/api/admin/* 管理。需要 Cookie 会话(含 CSRF 校验)或 admin 作用域的 API 令牌 /api/admin/* 管理。需要 Cookie 会话(含 CSRF 校验)或 admin 作用域的 API 令牌(`POST /api/admin/releases` 也接受只能登记发布清单的 release 令牌)
/api/agent/v1/* 节点专用:注册(使用加入令牌)和 RPC(使用签名认证) /api/agent/v1/* 节点专用:注册(使用加入令牌)和 RPC(使用签名认证)
/healthz /metrics /openapi/v1.json /install.sh /install.ps1 /healthz /metrics /openapi/v1.json /install.sh /install.ps1
``` ```
@@ -20,6 +20,7 @@
├─ live SSE 实时推送 ├─ live SSE 实时推送
├─ lines/{line_id} 线路详情(线路 = Agent 与 Probe 的一对,id 形如 agt_…~prb_…) ├─ lines/{line_id} 线路详情(线路 = Agent 与 Probe 的一对,id 形如 agt_…~prb_…)
├─ lines/{line_id}/series 历史曲线(指标与评分) ├─ lines/{line_id}/series 历史曲线(指标与评分)
├─ nodes about 节点的展示信息(系统、国家、运营商,开启后带版本和 tag);后端的版本信息
├─ compare 多条线路对比 ├─ compare 多条线路对比
├─ tests/{test_id} 测试结果 ├─ tests/{test_id} 测试结果
└─ catalog/{kinds|plans|metrics|models} 目录(由代码注册表生成) └─ catalog/{kinds|plans|metrics|models} 目录(由代码注册表生成)
@@ -29,10 +30,12 @@
├─ agents agents/{id} agents/{id}/{approve|disable|enable|takeover} ├─ agents agents/{id} agents/{id}/{approve|disable|enable|takeover}
├─ join-tokens ├─ join-tokens
├─ probes probes/{id} probes/{id}/{rotate|revoke} Probe 身份(后端生成、吊销、删除) ├─ probes probes/{id} probes/{id}/{rotate|revoke} Probe 身份(后端生成、吊销、删除)
├─ links links/{probe_id}/{agent_id} 线路开关:哪些(Probe,Agent)连成线路(自动连接 + 手动选择)
├─ tasks tasks/{id} 线路模板、针对单个 Agent 的覆盖、目标任务 ├─ tasks tasks/{id} 线路模板、针对单个 Agent 的覆盖、目标任务
├─ lines/{line_id}/tests 在某条线路(某个 Probe)上发起按需测试;tests/{id}/cancel ├─ lines/{line_id}/tests 在某条线路(某个 Probe)上发起按需测试;tests/{id}/cancel
├─ raw/chunks raw/chunks/{id} 原始数据检索与下载 ├─ raw/chunks raw/chunks/{id} 原始数据检索与下载
├─ releases 应用版本、灰度、批准(Fleet.Server.Releases) ├─ build-info 后端和每个节点的版本、tag、提交、构建时间、更新状态
├─ releases nodes/{id}/update CI 签名的发布清单(登记、列表);请求节点更新
├─ events analysis/recompute api-tokens settings users ├─ events analysis/recompute api-tokens settings users
``` ```
+20 -18
View File
@@ -4,15 +4,15 @@
一个**只关注网络层**的线路质量面板:单体面板(Backend)+ 轻量节点(Probe、Agent)+ 长连接 RPC + 任务下发。核心能力: 一个**只关注网络层**的线路质量面板:单体面板(Backend)+ 轻量节点(Probe、Agent)+ 长连接 RPC + 任务下发。核心能力:
- 在**一个家宽 Probe** 与**多台海外 VPS Agent**(10 几台)之间,采集**最原始的逐包、逐事件数据**; - 在**若干 Probe**(家宽等需要观测的出口,可以有多个)与**多台海外 VPS Agent**(10 几台)之间,采集**最原始的逐包、逐事件数据**;哪些 Probe 连哪些 Agent 由面板决定(自动全连,也可以手动选择);
- 由 Backend 的分析层推算:**如果在某条线路上部署 REALITY 类、Hysteria2 类或 Minecraft 服务,体验会怎样**,并对多条线路排序和对比; - 由 Backend 的分析层推算:**如果在某条线路上部署 REALITY 类、Hysteria2 类或 Minecraft 服务,体验会怎样**,并对多条线路排序和对比;
- **主机层信息只上报只读的汇总数字**(2026-10 起,由用户确认放开):Agent 读 `/proc` 得到 CPU 占用、内存、根分区、网卡累计流量与瞬时网速、运行时长,不采集 CPU 型号、主机名、网卡名和任何 IP,不采集进程;Probe 不上报。展示页只能拿到这些数字。Agent 和 Probe 仍然是**纯采集器**,不具备 SSH、远程执行、终端、文件读写、远程更新等能力。 - **主机层信息只上报只读的汇总数字**(2026-10 起,由用户确认放开):Agent 读 `/proc` 得到 CPU 占用、内存、根分区、网卡累计流量与瞬时网速、运行时长,不采集 CPU 型号、主机名、网卡名和任何 IP,不采集进程;Probe 不上报主机资源。节点只在连接面板时自报粗粒度的系统、架构和构建信息(版本、tag、提交、构建时间),用于展示和更新;国家和运营商由 Backend 用节点的来源地址查询,节点自己不访问任何外部服务。展示页看不到任何 IP。Agent 和 Probe 仍然是**纯采集器**,不具备 SSH、远程执行、终端、文件读写、自更新等能力:更新由节点宿主机上的更新器完成,节点进程只往数据目录放面板转来的发布清单和更新请求。
## 2. 拓扑 ## 2. 拓扑
```text ```text
┌────────────────── Backend(面板)───────────────────┐ ┌────────────────── Backend(面板)───────────────────┐
前端(待开发)◀─────────┤ Api(public|admin) │ 注册中心 │ 任务编排 │ 结果管线 │ 前端(展示端+管理端)◀─────────┤ Api(public|admin) │ 注册中心 │ 任务编排 │ 结果管线 │
REST + SSE 实时 │ Analysis(指标注册表) │ LiveHub │ 存储(段文件+SQLite) │ REST + SSE 实时 │ Analysis(指标注册表) │ LiveHub │ 存储(段文件+SQLite) │
└────▲──────▲──────▲───────────────▲─────────────────┘ └────▲──────▲──────▲───────────────▲─────────────────┘
wss JSON-RPC │ │ │ │ 每个 Agent 一条长连接(由 Agent 发起) wss JSON-RPC │ │ │ │ 每个 Agent 一条长连接(由 Agent 发起)
@@ -22,8 +22,8 @@
│ │ │ │ 每个 Agent 一个会话: │ │ │ │ 每个 Agent 一个会话:
└────────┴───┬────┴─────────────┘ TLS 控制通道 + UDP/TCP 数据通道 └────────┴───┬────┴─────────────┘ TLS 控制通道 + UDP/TCP 数据通道
┌─────┴──────┐ ┌─────┴──────┐
│ Probe │ 家宽,NAT/CGNAT 后,唯一的测量起点 │ Probe │ 家宽,NAT/CGNAT 后,测量的起点
└────────────┘ 不认识 Backend,只连 Agent └────────────┘ 直连 Backend(身份、列表),测量只连 Agent
``` ```
- **线路(line)** = 一对(Probe,Agent)。可以有多个 Probe(身份由 Backend 生成,可以吊销、删除),每个 Probe 连接多个 Agent,每个 Agent 同时服务多个 Probe;一条线路的数据按 `line:<probe_id>` 单独存放,互不混合。对外的线路 id 是 `<agent_id>~<probe_id>`。 - **线路(line)** = 一对(Probe,Agent)。可以有多个 Probe(身份由 Backend 生成,可以吊销、删除),每个 Probe 连接多个 Agent,每个 Agent 同时服务多个 Probe;一条线路的数据按 `line:<probe_id>` 单独存放,互不混合。对外的线路 id 是 `<agent_id>~<probe_id>`。
@@ -42,17 +42,17 @@
| 角色 | 职责 | 明确不做 | | 角色 | 职责 | 明确不做 |
| --- | --- | --- | | --- | --- | --- |
| Probe | 连接所有启用的 Agent;执行线路任务中 Probe 一侧的角色;记录本端原始列并交给对应的 Agent | 认识 Backend;统计;落盘测量数据;任何主机操作 | | Probe | 连接 Backend(身份、Agent 列表、系统与构建信息、更新);连接面板指定的 Agent;执行线路任务中 Probe 一侧的角色;记录本端原始列并交给对应的 Agent | 统计;落盘测量数据;任何主机操作 |
| Agent | 注册;接受 Probe;执行 Agent 一侧的角色和目标任务;合并两侧的列并上传 | 统计;落盘测量数据;exec/终端/文件/自更新;采集主机信息 | | Agent | 注册;接受 Probe;执行 Agent 一侧的角色和目标任务;合并两侧的列并上传;上报主机资源的汇总数字 | 统计;落盘测量数据;exec/终端/文件/自更新(更新由宿主机上的更新器完成);采集主机的详细信息 |
| Backend | 注册与身份;任务编排;持久化原始数据;分析;实时汇聚;API | 连接 Probe | | Backend | 注册与身份;任务编排;决定哪些 Probe 连哪些 Agent;持久化原始数据;分析;实时汇聚;API;分发 CI 签名的发布清单 | 主动连接 Probe 或 Agent(都是节点拨进来);验签和安装更新(在节点宿主机上) |
## 4. 关键设计(详见各专题文档) ## 4. 关键设计(详见各专题文档)
| 主题 | 要点 | 文档 | | 主题 | 要点 | 文档 |
| --- | --- | --- | | --- | --- | --- |
| 节点框架 | 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 **Fleet** 提供;Monitor 只写业务,其他产品也能复用 | [fleet/overview.md](fleet/overview.md) | | 节点框架 | 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 **Fleet** 提供;Monitor 只写业务,其他产品也能复用 | [fleet/overview.md](fleet/overview.md) |
| 注册与身份 | 使用 `fleet-enroll` 和加入令牌自动注册,之后用签名认证;Probe 通过引导 Agent 同步 Agent 列表 | [enrollment.md](enrollment.md) | | 注册与身份 | 使用 `fleet-enroll` 和加入令牌自动注册,之后用签名认证;Probe 用自己的身份直连面板,Agent 列表由面板下发 | [enrollment.md](enrollment.md) |
| 更新 | 独立的 `fleet-updater` 进程:只接受离线密钥签名的清单,更新策略由本地决定,失败自动回滚 | [fleet/update.md](fleet/update.md) | | 更新 | 只接受发布密钥签名的清单,更新策略由节点本地决定,失败自动回滚。容器节点(Agent、Probe)已实现:CI 对镜像 digest 签名,宿主机上的 `monitor-update` 验签、按 digest 拉取、重启;纯二进制的 `fleet-updater` 还只是设计 | [deployment.md](deployment.md)、[fleet/update.md](fleet/update.md) |
| 任务模型 | `Task = {kind, target, params, schedule}`;持续采集、按需测试、目标任务统一建模;下发“期望任务集”,由节点自行调和 | [measurement.md](measurement.md) | | 任务模型 | `Task = {kind, target, params, schedule}`;持续采集、按需测试、目标任务统一建模;下发“期望任务集”,由节点自行调和 | [measurement.md](measurement.md) |
| 采集扩展 | Kind 由积木组合声明;采集密度靠参数调整;原始块自描述 | [measurement.md](measurement.md) | | 采集扩展 | Kind 由积木组合声明;采集密度靠参数调整;原始块自描述 | [measurement.md](measurement.md) |
| 统计扩展 | 指标注册制:少数几种累加器 + 统计算子;一行 `MetricDef` 就是一个新指标 | [analysis.md](analysis.md) | | 统计扩展 | 指标注册制:少数几种累加器 + 统计算子;一行 `MetricDef` 就是一个新指标 | [analysis.md](analysis.md) |
@@ -73,16 +73,16 @@
```text ```text
Agent / Probe 能做的 执行编译在二进制中的 Kind;监听和访问本地配置允许的端口与网段;受本地硬上限约束 Agent / Probe 能做的 执行编译在二进制中的 Kind;监听和访问本地配置允许的端口与网段;受本地硬上限约束
不能做的 exec / shell / 终端 / 文件读写(配置目录除外)/ 远程更新 / 加载外部代码 / 采集主机标识(型号、主机名、IP、进程) 不能做的 exec / shell / 终端 / 文件读写(配置目录除外)/ 自更新 / 加载外部代码 / 采集主机标识(型号、主机名、IP、进程)
即使 Backend 被攻破 攻击者最多能在本地硬上限内发测量流量,拿不到主机权限,也无法把 Agent 当成扫描器或放大器; 即使 Backend 被攻破 攻击者最多能在本地硬上限内发测量流量,拿不到主机权限,也无法把 Agent 当成扫描器或放大器;
也推不出恶意更新(更新清单必须由离线密钥签名) 也推不出恶意更新(更新清单必须由发布密钥签名,节点宿主机上的更新器用本机公钥验签,并且只认本机配置的镜像仓库)
``` ```
- **本地硬上限**写在节点本机的配置文件中,Backend 下发的任务如果超出,节点直接拒绝:允许访问的目标网段(默认为空,即不开放目标任务)、总码率和包速率上限、允许监听的端口。 - **本地硬上限**写在节点本机的配置文件中,Backend 下发的任务如果超出,节点直接拒绝:允许访问的目标网段(默认为空,即不开放目标任务)、总码率和包速率上限、允许监听的端口。
- **构建期强制**:常驻节点进程使用 `BannedSymbols.Node.txt`,禁用 `System.Diagnostics.Process`、`Assembly.Load*`、`NativeLibrary.Load`、`HttpClient`,以及 `ConfigStore` 以外的文件写入 API,详见 [project-layout.md](project-layout.md) 第 7 节。 - **构建期强制**:常驻节点进程使用 `BannedSymbols.Node.txt`,禁用 `System.Diagnostics.Process`、`Assembly.Load*`、`NativeLibrary.Load`、`HttpClient`,以及 `ConfigStore` 以外的文件写入 API,详见 [project-layout.md](project-layout.md) 第 7 节。
- **三进程权限分离**:注册(`fleet-enroll`,一次性运行)、采集(`monitor-agent` / `monitor-probe`)、更新(`fleet-updater`)是三个独立进程,使用不同的系统用户。采集进程无法改写自己的二进制;更新进程读不到节点身份私钥。 - **权限分离**:注册(`fleet-enroll`,一次性运行)、采集(`monitor-agent` / `monitor-probe`)、更新是不同的进程。容器节点里,采集进程在容器内以 UID 1654 运行(只读文件系统、不带额外能力),更新由宿主机上 root 运行的 `monitor-update` 完成:它只读取节点放进 `update/` 的普通文件、复制到自己的临时目录后验签,状态写在节点只读的 `update-status/`;采集进程改写不了自己的二进制,更新器也读不到节点身份私钥。纯二进制安装的 `fleet-updater` 还只是设计。
- **最小权限运行**:非 root 用户;只在需要时授予 `CAP_NET_RAW`(ICMP);默认端口都高于 1024,不需要 `CAP_NET_BIND_SERVICE`,只有在 Agent 本地显式开启 443 时才授予。 - **最小权限运行**:非 root 用户;只在需要时授予 `CAP_NET_RAW`(ICMP);默认端口都高于 1024,不需要 `CAP_NET_BIND_SERVICE`,只有在 Agent 本地显式开启 443 时才授予。
- **身份**:节点使用本地生成的 ECDSA P-256 密钥对签名认证,Backend 只存公钥,数据库泄露也无法冒充节点。 - **身份**:节点使用本地生成的 ECDSA P-256 密钥对签名认证(Probe 的密钥由面板生成、只返回一次,同一把用来连面板和连 Agent),Backend 只存公钥,数据库泄露也无法冒充节点。
- **防放大**:未认证的包静默丢弃;回复长度与请求相等;每个流按声明速率限速;使用重放窗口。 - **防放大**:未认证的包静默丢弃;回复长度与请求相等;每个流按声明速率限速;使用重放窗口。
- **自身状态**:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 `monitor.host` 上报一份主机资源占用的汇总数字(可以在 `agent.json` 里用 `reporting.host_stats = false` 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,`retention.host_days`)。 - **自身状态**:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 `monitor.host` 上报一份主机资源占用的汇总数字(可以在 `agent.json` 里用 `reporting.host_stats = false` 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,`retention.host_days`)。
@@ -113,7 +113,7 @@ Kind 目录编译在二进制中,节点不接受远程下发代码(安全边
| D1 | 只关注网络层;节点是纯采集器,没有远程执行能力 | 由用户确认;防止权限泄露 | | D1 | 只关注网络层;节点是纯采集器,没有远程执行能力 | 由用户确认;防止权限泄露 |
| D2 | 拓扑为多个 Probe 连接多个 Agent(10+);线路 = (Probe, Agent),数据按 Probe 隔离 | 由用户确认 | | D2 | 拓扑为多个 Probe 连接多个 Agent(10+);线路 = (Probe, Agent),数据按 Probe 隔离 | 由用户确认 |
| D3 | Agent 用加入令牌自动注册,之后用密钥对签名认证 | 批量部署省事;Backend 不保存可用于冒充节点的秘密 | | D3 | Agent 用加入令牌自动注册,之后用密钥对签名认证 | 批量部署省事;Backend 不保存可用于冒充节点的秘密 |
| D4 | Probe 不认识 Backend,通过引导 Agent 同步 Agent 列表 | 新增 Agent 后自动覆盖,不需要改 Probe 配置 | | D4 | Probe 直连 Backend:身份、Agent 列表(期望状态 `probe_agents`)、状态和更新都走这条链路,测量仍然只走 Probe → Agent(2026-10 由「Probe 不认识 Backend、经 Agent 同步列表」改成直连,因为面板本来就要对外暴露,直连让更新和管理少一跳)。哪些 Probe 连哪些 Agent 由面板决定,`fleet.auto_link` 打开时两两连,也可以手动选择,Probe 始终主动拨出 | 新增 Agent 后自动覆盖,不需要改 Probe 配置;Probe 的版本、更新和线路都能在面板上管理 |
| D5 | 只采集逐包、逐事件的原始数据,解释全部放在 Backend | 模型和指标可以事后重算;采集端最轻 | | D5 | 只采集逐包、逐事件的原始数据,解释全部放在 Backend | 模型和指标可以事后重算;采集端最轻 |
| D6 | 统一的任务模型 + 期望任务集调和 | 新增任务类型时协议不变 | | D6 | 统一的任务模型 + 期望任务集调和 | 新增任务类型时协议不变 |
| D7 | Kind 由积木组合声明;原始块自描述 | 调整密度或新增采集方法只改一处 | | D7 | Kind 由积木组合声明;原始块自描述 | 调整密度或新增采集方法只改一处 |
@@ -121,19 +121,21 @@ Kind 目录编译在二进制中,节点不接受远程下发代码(安全边
| D9 | JSON-RPC 2.0 信封 + 二进制帧 | 新增能力只需要加方法 | | D9 | JSON-RPC 2.0 信封 + 二进制帧 | 新增能力只需要加方法 |
| D10 | 原始数据每 10 秒切一块,追加写入段文件,保留 30 天;指标长期保留 | 同一条链路同时服务实时和历史;避免海量小文件 | | D10 | 原始数据每 10 秒切一块,追加写入段文件,保留 30 天;指标长期保留 | 同一条链路同时服务实时和历史;避免海量小文件 |
| D11 | API 分为 public / admin / agent 三层;实时数据用 SSE | 前端只读场景足够;反向代理和 CDN 更友好 | | D11 | API 分为 public / admin / agent 三层;实时数据用 SSE | 前端只读场景足够;反向代理和 CDN 更友好 |
| D12 | 单仓库,三个独立项目加待开发的 Web;契约以 C# 代码为真源 | 改一处,编译期即可发现不一致 | | D12 | 单仓库,三个独立项目加 Web(展示端、管理端);契约以 C# 代码为真源 | 改一处,编译期即可发现不一致 |
| D13 | Probe/Agent 运行时只依赖 BCL;.NET 10 NativeAOT | 轻量 | | D13 | Probe/Agent 运行时只依赖 BCL;.NET 10 NativeAOT | 轻量 |
| D14 | 节点机制抽成与业务无关的 Fleet 框架,放在同一仓库的 `backend/fleet/` 目录,暂不独立(由用户确认;不设拆分时间表,需要时再评估;不使用 PublicApiAnalyzers) | 可复用;`samples/EchoNode` 作为第二个使用者,防止过度设计 | | D14 | 节点机制抽成与业务无关的 Fleet 框架,放在同一仓库的 `backend/fleet/` 目录,暂不独立(由用户确认;不设拆分时间表,需要时再评估;不使用 PublicApiAnalyzers) | 可复用;`samples/EchoNode` 作为第二个使用者,防止过度设计 |
| D15 | 注册独立为 `fleet-enroll` 工具;常驻进程不包含注册代码 | 常驻进程去掉 HttpClient,边界更纯 | | D15 | 注册独立为 `fleet-enroll` 工具;常驻进程不包含注册代码 | 常驻进程去掉 HttpClient,边界更纯 |
| D16 | 更新独立为 `fleet-updater`:离线签名、多发布源、本地策略、健康确认与回滚(默认方案,待确认) | 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品 | | D16 | 更新与业务进程分离:发布密钥签名、本地策略、健康确认与回滚。容器节点已实现(宿主机 `monitor-update`,清单里是镜像 digest);纯二进制的 `fleet-updater` 待做 | 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品 |
| D17 | Probe 支持 OpenWrt:正式支持 `linux-musl-x64`、`linux-musl-arm64`;armv7 视工具链验证结果决定;MIPS 不支持,改为在局域网设备上运行 Probe | 由用户确认;NativeAOT 不支持 MIPS | | D17 | Probe 支持 OpenWrt:正式支持 `linux-musl-x64`、`linux-musl-arm64`;armv7 视工具链验证结果决定;MIPS 不支持,改为在局域网设备上运行 Probe | 由用户确认;NativeAOT 不支持 MIPS |
| D18 | 默认测量端口不使用 443:控制 7443/tcp,UDP 数据 7443/udp,TCP 数据 7444/tcp;443 只在 Agent 本地显式开启时才允许使用 | 由用户确认;避免与 VPS 上的真实服务冲突;结果会记录实际端口 | | D18 | 默认测量端口不使用 443:控制 7443/tcp,UDP 数据 7443/udp,TCP 数据 7444/tcp;443 只在 Agent 本地显式开启时才允许使用 | 由用户确认;避免与 VPS 上的真实服务冲突;结果会记录实际端口 |
| D19 | 展示查询最细 1s:最近 1 小时来自内存热层,更早的数据来自 10s/1m/5m/1h 各层,现场合并;时间段内的汇总由累加器精确合并得出 | 由用户确认;5 分钟、15 分钟这类视图足够精细且快 | | D19 | 展示查询最细 1s:最近 1 小时来自内存热层,更早的数据来自 10s/1m/5m/1h 各层,现场合并;时间段内的汇总由累加器精确合并得出 | 由用户确认;5 分钟、15 分钟这类视图足够精细且快 |
| D20 | 时序响应采用列式格式(`t[]` + `values{}`,时间轴使用 Unix 毫秒) | 由用户确认;体积小,可以直接交给图表库 | | D20 | 时序响应采用列式格式(`t[]` + `values{}`,时间轴使用 Unix 毫秒) | 由用户确认;体积小,可以直接交给图表库 |
| D21 | 国家和运营商只由 Backend 向 IP 归属服务查询(缺省 `https://ip.007995.xyz`,可关闭),只查公网地址;节点不访问任何外部服务;公开接口不返回 IP、城市、ASN | 客户的防火墙常常要审计节点的出站连接;访客拿不到敏感信息(有测试保证) |
| D22 | 构建信息(版本、tag、提交、构建时间)由节点自报,访客是否可见由设置 `site.show_build` 控制(默认关);提交号只在管理端 | 展示页可以选择不暴露版本;管理员能看出哪个节点没升级 |
## 9. 仍待确认 ## 9. 仍待确认
以下两项是按推荐方案写入的默认值(D16),等待确认:Fleet.Link 的通用范围(当前包含注册、认证、RPC、发件箱、状态同步、点对点会话);更新采用多发布源加离线签名。其余待确认事项: 待确认事项:
1. **告警**:首版不包含,是否需要预留 Webhook? 1. **告警**:首版不包含,是否需要预留 Webhook?
2. **与真实服务共用 443**(例如 SNI 分流):首版不做,默认使用非 443 端口;以后如有需要再评估。 2. **与真实服务共用 443**(例如 SNI 分流):首版不做,默认使用非 443 端口;以后如有需要再评估。
+1 -1
View File
@@ -9,7 +9,7 @@ scripts/publish.sh <agent|probe|backend|fleet-enroll> <rid> → backend/art
scripts/package.sh <agent|probe> <rid> → backend/artifacts/packages/monitor-<role>-<version>-<rid>.tar.gz(附 .sha256) scripts/package.sh <agent|probe> <rid> → backend/artifacts/packages/monitor-<role>-<version>-<rid>.tar.gz(附 .sha256)
``` ```
构建信息(程序里显示的 tag、提交、构建时间):`publish.sh` 会用 `git describe --tags --always --dirty`、`git rev-parse` 和当前时间自动设置环境变量 `MONITOR_TAG`、`MONITOR_COMMIT`、`MONITOR_BUILT_AT`,直接用 `dotnet publish` 或 CI 构建时自己设置它们(容器的 Fastlane 构建用同名的 `--build-arg`);不设置就是空,本地构建照常可重复。版本号取自 `backend/Directory.Build.props` 的 `Version`。前端的信息由 `web/build-info.mjs` 在构建时写入(版本取自 `web/package.json`)。 构建信息(程序里显示的 tag、提交、构建时间):`publish.sh` 会用 `git describe --tags --always --dirty`、`git rev-parse` 和当前时间自动设置环境变量 `MONITOR_TAG`、`MONITOR_COMMIT`、`MONITOR_BUILT_AT`,直接用 `dotnet publish` 或 CI 构建时自己设置它们(容器镜像的 `*.NativeBuild.Containerfile` 用同名的 `--build-arg`,CI 脚本会传);不设置就是空,本地构建照常可重复。版本号取自 `backend/Directory.Build.props` 的 `Version`。前端的信息由 `web/build-info.mjs` 在构建时写入(版本取自 `web/package.json`)。
| rid | 目标 | 构建方式 | | rid | 目标 | 构建方式 |
| --- | --- | --- | | --- | --- | --- |
+2 -2
View File
@@ -10,7 +10,7 @@
| Probe | Fleet 节点身份(同时用于连面板和连 Agent) | 面板生成 `probe.json`(可以有多个 Probe,每个独立吊销) | 公钥 | | Probe | Fleet 节点身份(同时用于连面板和连 Agent) | 面板生成 `probe.json`(可以有多个 Probe,每个独立吊销) | 公钥 |
| 面板用户 | 账号 + 密码(PBKDF2-SHA256) | 面板 | 密码哈希 | | 面板用户 | 账号 + 密码(PBKDF2-SHA256) | 面板 | 密码哈希 |
| API 令牌 | 随机高熵令牌,作用域为 `read` 或 `admin` | 面板 | SHA-256 | | API 令牌 | 随机高熵令牌,作用域为 `read` 或 `admin` | 面板 | SHA-256 |
| 发布签名 | 离线的 ECDSA 发布密钥 | CI 或离线环境 | 只在节点的 `fleet-updater` 中配置公钥,**面板不保存** | | 发布签名 | ECDSA P-256 发布密钥 | CI(Woodpecker 密钥 `release_signing_key`)或离线环境 | 只在节点宿主机的更新器里配置公钥(容器节点是 `monitor-update`,见 [deployment.md](deployment.md);纯二进制方案的 `fleet-updater` 还只是设计),**面板不保存私钥,也不验签** |
## 2. Agent 接入流程 ## 2. Agent 接入流程
@@ -19,7 +19,7 @@
<── 安装命令: <── 安装命令:
curl -fsSL https://panel/install.sh | sh -s -- --join jt_xxx --name jp-1 curl -fsSL https://panel/install.sh | sh -s -- --join jt_xxx --name jp-1
install.sh(只做安装,不包含任何远程控制功能) install.sh(只做安装,不包含任何远程控制功能;下面的更新器部分是纯二进制方案的设计,还没有实现,容器节点的做法见 deployment.md)
├─ 下载 monitor-agent、fleet-enroll、fleet-updater,并验证发布签名 ├─ 下载 monitor-agent、fleet-enroll、fleet-updater,并验证发布签名
├─ 创建系统用户:monitor(运行 Agent)、fleet-update(运行更新器) ├─ 创建系统用户:monitor(运行 Agent)、fleet-update(运行更新器)
├─ fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/monitor ├─ fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/monitor
+2
View File
@@ -1,5 +1,7 @@
# Fleet:通用节点框架 # Fleet:通用节点框架
> **实现状态**:`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计;容器节点的更新目前由宿主机上的 `monitor-update` 脚本和 Monitor 的 `ReleaseService` 完成(见 [deployment.md](../deployment.md))。其余组件已实现。
> 名称暂定。Fleet 与业务无关,Monitor 是它的第一个使用者。 > 名称暂定。Fleet 与业务无关,Monitor 是它的第一个使用者。
## 1. 定位 ## 1. 定位
+16 -24
View File
@@ -98,7 +98,9 @@ Web ──> openapi/v1.json
### 4.1 Fleet(通用节点框架) ### 4.1 Fleet(通用节点框架)
组件、扩展点与接入方式见 [fleet/overview.md](fleet/overview.md),连接细节见 [fleet/link.md](fleet/link.md),更新见 [fleet/update.md](fleet/update.md)。原来的 `Monitor.Common` 和 `Monitor.NodeHost` 已经并入 Fleet: 组件、扩展点与接入方式见 [fleet/overview.md](fleet/overview.md),连接细节见 [fleet/link.md](fleet/link.md),更新见 [fleet/update.md](fleet/update.md)。
> **实现状态**:`Fleet.Core`、`Fleet.Identity`、`Fleet.Rpc`、`Fleet.Link`、`Fleet.Hosting`、`Fleet.Enrollment.Client`、`Fleet.Server.*`(Abstractions、Enrollment、Link)和 `fleet-enroll` 已实现。`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计(纯二进制方式的更新);容器节点的更新是宿主机上的 `monitor-update` 脚本和面板的 `ReleaseService`,见 [deployment.md](deployment.md)。原来的 `Monitor.Common` 和 `Monitor.NodeHost` 已经并入 Fleet:
| 原位置 | 现位置 | | 原位置 | 现位置 |
| --- | --- | | --- | --- |
@@ -141,30 +143,20 @@ Monitor.Protocol/
## 5. 模块化 ## 5. 模块化
### 5.1 Agent 与 Probe:模块组装 ### 5.1 Agent 与 Probe:组装
模块在 `Program.cs` 中**显式组装**,不使用 DI 容器,也不做反射扫描。连接、认证、重连、发件箱、状态同步都由 Fleet 提供,Monitor 只写业务模块: 节点的组装点是 `AgentApp` 和 `ProbeApp`:在构造函数里**显式组装**,不使用 DI 容器,也不做反射扫描;没有全局状态,测试可以在同一进程里启动多个。连接、认证、重连、发件箱、状态同步都由 Fleet 提供,Monitor 只写业务:
```text ```text
Agent Program.cs AgentApp Probe(ProbeApp)
identity = NodeIdentity.Load(dir) ← 由 fleet-enroll 生成 FleetLink(到面板) FleetLink(到面板)
link = FleetLink.ToServer(cfg, identity, AgentRpcContext.Default) 状态 TaskSet、方法 monitor.test.* 状态 probe_agents(Agent 列表)、hello 带系统和构建信息
.WithState<TaskSet>(…, taskReconciler) 期望任务集 → 调和 发件箱 RawChunk(通道 16) 心跳带更新器的状态;monitor.update.* 通知
.WithOutbox<RawChunk>(…, 64 MiB) 原始块可靠上报 心跳带更新器的状态;monitor.update.* AgentDirectory → 每个 Agent 一个 AgentConnection(点对点会话)
.On(monitor.test.run / monitor.test.cancel, …) PeerListener / PeerHub(Probe 会话) 会话内:线路任务集 line_task_set 的调和、角色、列块发件箱(通道 17)
peers = FleetLink.PeerListener(cfg.Listen, identity, ProbeRpcContext.Default) Probe 会话 UdpMux / TcpAcceptor(数据端口)
host = new ModuleHost(log) TaskManager / TestRunner / ChunkAssembler
├─ link、peers (Fleet) UpdateBridge(和宿主机更新器交换文件) UpdateBridge
├─ UdpMux / TcpAcceptor UDP/TCP/TLS 数据端口(Measurement 共享),按 (session, stream) 分发给监听方积木;AgentAdmission 提供重放、限速
├─ ChunkModule 合并 Probe 与本端的列 → 切块 → link.Outbox
└─ TestModule 按需测试的阶段编排
Probe Program.cs
├─ FleetLink(到面板) 身份、hello、期望状态 probe_agents(Agent 列表)
├─ PeerPool(Fleet) 每个 Agent 一个 FleetLink.ToPeer:认证、重连
├─ TaskReconciler 每个会话各自的期望状态(与 Agent 相同的代码)
├─ ColumnShipper 本端的列通过会话的 Outbox 发送(二进制通道 17)
└─ TestModule
``` ```
模块之间通过构造函数传入的接口或 `Channel<T>` 通信,禁止通过静态全局状态共享数据。 模块之间通过构造函数传入的接口或 `Channel<T>` 通信,禁止通过静态全局状态共享数据。
@@ -347,6 +339,6 @@ M0 阶段先测出基线,之后作为 CI 的回归门槛(超出 10% 即失
| 打包 | 提供 `tar.gz` 和 procd 脚本;包格式(ipk 或 apk)按目标 OpenWrt 版本提供 | | 打包 | 提供 `tar.gz` 和 procd 脚本;包格式(ipk 或 apk)按目标 OpenWrt 版本提供 |
| 更新 | `fleet-updater` 使用 procd 适配器;`keep_versions = 1`,下载前检查剩余空间,避免写满闪存 | | 更新 | `fleet-updater` 使用 procd 适配器;`keep_versions = 1`,下载前检查剩余空间,避免写满闪存 |
## 10. Web(待开发) ## 10. Web
唯一输入是 `backend/openapi/v1.json`:用 `openapi-typescript` 生成类型,`openapi-fetch` 作为请求客户端;技术栈等前端立项时再决定。 `web/` 是 npm workspaces:`display`(展示端,匿名可读)、`admin`(管理端)、`shared`(API 客户端、图表、组件、主题)。Vite 8、React 19、TypeScript,图表用 uPlot;唯一的契约是 `backend/openapi/v1.json`,用 `openapi-typescript` 生成类型(`npm run gen:api`)。颜色只能写在 `shared/theme/tokens.css`,由 stylelint 强制。详见 [web/README.md](../web/README.md)。
+5 -3
View File
@@ -11,11 +11,13 @@ Monitor 的两条控制链路都建立在 Fleet 之上:
| Monitor 需要的能力 | 使用的 Fleet 机制 | Monitor 提供的内容 | | Monitor 需要的能力 | 使用的 Fleet 机制 | Monitor 提供的内容 |
| --- | --- | --- | | --- | --- | --- |
| 下发任务 | 期望状态 `fleet.state.sync` | 文档类型 `TaskSet`,包括 `tasks[]`、`peers`(Probe 公钥),(Probe 的 Agent 列表是另一种期望状态,见下) | | 下发任务 | 期望状态 `fleet.state.sync` | 文档类型 `TaskSet`,包括 `tasks[]`、`peers`(允许连这个 Agent 的 Probe 公钥,已按线路开关过滤) |
| 下发 Agent 列表给 Probe | 期望状态 `fleet.state.sync`(Probe 直连面板) | 文档类型 `probe_agents`:`version`、`agents[]`(`agent_id`、`host`、`tcp_port`、`udp_ports`、`spki`),已按线路开关过滤;Agent 集合或线路开关变化时重新推送 |
| 更新 | 通知 `monitor.update.offer` / `monitor.update.apply`(B→A、B→P),心跳附加字段 `update` | `offer` 带 CI 签名的发布清单原文和签名,节点只写进数据目录;`apply` 请求更新;`update` 是宿主机更新器的状态(`state`、`current`、`available`、`error`、`at`),见 [deployment.md](deployment.md) |
| 上报原始块 | 发件箱,二进制通道 **16** | `RawChunk`(自描述,见 [measurement.md](measurement.md) 第 4 节),`msg_id = chunk_id` | | 上报原始块 | 发件箱,二进制通道 **16** | `RawChunk`(自描述,见 [measurement.md](measurement.md) 第 4 节),`msg_id = chunk_id` |
| Probe 交付列块 | 点对点会话的发件箱,二进制通道 **17** | 列块:`u32 stream_id \| u32 first_seq \| u32 count \| 列数据(Brotli)` | | Probe 交付列块 | 点对点会话的发件箱,二进制通道 **17** | 列块:`u32 stream_id \| u32 first_seq \| u32 count \| 列数据(Brotli)` |
| 节点自身状态 | `fleet.heartbeat` 的附加字段 | `sessions`、`pps_in/out`、`dropped_invalid`、`dropped_rate_limited`、`queue_bytes` | | 节点自身状态 | `fleet.heartbeat` 的附加字段 | Agent:`sessions`、`probes[]`、`pps_in/out`、`dropped_invalid`、`dropped_rate_limited`、`queue_bytes`、`update`;Probe:只有 `update` |
| 版本与能力 | `fleet.hello` | capabilities 包括 `monitor.kinds:<列表>`、`monitor.blocks:<列表>`;附加 `tls_spki`、`probe_ports`、`local_limits` 摘要 | | 版本与能力 | `fleet.hello` | capabilities 包括 `monitor.kinds:<列表>`、`monitor.blocks:<列表>`;Agent 附加 `platform`(系统、架构、版本、tag、提交、构建时间)、`tls_spki`、`probe_ports`、`local_limits` 摘要;Probe 附加 `platform` |
## 2. `monitor.*` 方法 ## 2. `monitor.*` 方法
-116
View File
@@ -1,116 +0,0 @@
# 里程碑
每个里程碑都以“可运行、可验证”作为完成标准;可执行程序在各自的目标 RID 上(见 [project-layout.md](project-layout.md) 第 9 节,Probe 包括 `linux-musl-x64` 和 `linux-musl-arm64`)的 NativeAOT 发布必须通过,并且零 AOT/trim 警告。
## M0 骨架
- 按 [project-layout.md](project-layout.md) 建立仓库:`backend/` 下的 props、`.editorconfig`、`BannedSymbols.txt` 与 `BannedSymbols.Node.txt`、`backend/fleet/` 骨架、三个 `.slnx`、两个 Monitor 共享库、Backend 的六个项目。
- CI:Windows/Linux 矩阵、路径过滤、`dotnet format --verify-no-changes`、`scripts/check-deps`、`scripts/check-openapi`、AOT 发布,并记录体积和内存基线。
**验收**:
- 三个空程序以 AOT 方式发布并启动;
- 在 Agent 中调用 `Process.Start` 时构建失败;
- 空的 Probe 能在 OpenWrt aarch64 真机或 QEMU 上启动,并完成一次 TLS 握手(验证 musl 与 `libopenssl3`);
- 给出 `linux-musl-arm`(armv7)工具链的验证结论。
## F1 Fleet 核心(与 M1 并行)
- `Fleet.Core`、`Fleet.Identity`、`Fleet.Rpc`、`Fleet.Link`(连接服务端、点对点会话、发件箱、期望状态同步)、`Fleet.Hosting`、`Fleet.Enrollment.Client`、`fleet-enroll`、`Fleet.Server.Enrollment`、`Fleet.Server.Link`、`Fleet.Server.Sqlite`。
- `samples/EchoNode` 与 `EchoServer`:用不依赖 Monitor 的方式跑通注册 → 认证 → 状态同步 → 发件箱上报 → 断线重连。
**验收**:
- `scripts/check-deps` 证明 `backend/fleet/**` 不引用 `Monitor.*`;
- EchoNode 在 Backend 重启、网络中断 10 分钟后能恢复,上报的消息不重复也不丢失(队列未满时);
- 伪造签名、重放 nonce、调用本地未允许的方法,全部被拒绝。
## M1 声明与积木(本机回环)
- `Protocol`:`ParamSpec`、`KindSpec`、自描述 RawChunk 编解码(附黄金字节测试)、JSON-RPC 信封。
- `Measurement`:Transport(udp/tcp/tls)、Pacer(fixed/ladder)、Payload、Behavior(echo/sink/source/connect/hold)、Sampler(tcp_info),以及 Recorder、`KindAssembler`、`BudgetGuard`。
- `tools/monitor-bench`:用两个本地进程运行任意 Kind,导出 RawChunk,用来调试引擎。
- 测试:通过丢包注入中继验证逐包记录的完整性;100 Mbps 下发送节奏误差 < 1%;**只加一条 Kind 声明、不写代码,就能运行一种新的组合**。
## M2 Probe ↔ 多个 Agent
- Probe 与 Agent 之间的签名认证、会话、MAC、重放窗口、限速、源地址漫游;`TaskReconciler`;列的交付与合并;每 10 秒切块。
- Probe 同时与多个 Agent 维持会话(先用静态列表)。
- 开发模式:Agent 把 RawChunk 写入本地目录(正式运行时不落盘)。
**验收**:一个家宽 Probe 同时连接 3 台以上 VPS,连续运行 24 小时;原始块完整,内存稳定;宽带重拨后能自动恢复。
## M3 Backend:注册、RPC、存储
- 在 Fleet 之上接入:`IFleetStore` 实现、`TaskSet` 状态提供者(有效任务集的计算与下发)、`RawChunk` 收件箱、`monitor.*` 方法、`probe_agents` 期望状态(Probe 直连面板)同步。
- 段文件与索引、发件箱 ACK、去重、缺口;管理员账号、Cookie 会话、API 令牌;`/api/admin` 中的节点、令牌、Probe 身份、任务管理。
**验收**:用一行安装命令部署一台新 VPS,自动注册 → 自动生成线路任务 → Probe 自动连接 → 1 分钟内在 `/api/admin` 中看到它的原始块。整个过程不需要手工配置。
## M4 分析层与实时
- 累加器、算子、首批 `MetricDef`、`base`/`mc`/`hy2`/`reality` 模型 v1;流式路径与持久路径;降采样;指标指纹与回填。
- 查询([query.md](query.md)):`RangeResolver`、`SourcePlanner`、1s 热层(1 小时)、`metric_10s`、现场合并、精确汇总;`/overview?range=`、`/series`、`/compare`、`/distribution`、`/heatmap`、`/catalog/*`(包括 presets);SSE 的 `series` 和 `overview` 主题;按需测试与测试结果。
- **查询验收**:近 5 分钟 1s 步长和近 15 分钟 2s 步长的响应时间 < 50 ms;7 天 15 分钟步长的响应时间 < 200 ms;同一时间段内,汇总值与“从原始块直接计算”的结果一致(直方图分位数误差 ≤ 4.5%)。
**验收**:
- **扩展性**:新增一个指标只需一行 `MetricDef`,重启后通过 API 可以看到它,并且 30 天历史已经回填;
- **校准**:在两条线路上实际部署 Hysteria2 和 REALITY 服务并实测,与推算结果对比,把偏差记录进 `docs/calibration.md`。
## M5 规模与保留
- 用模拟数据验证 15 个 Agent 运行 30 天的情况:存储量、查询延迟(30 天跨度 < 500 ms)、回填耗时、SSE 在 100 个连接下的表现。
- 保留清理、磁盘占用指标、目标任务(`icmp_echo`、`connect`)与 `allowed_target_cidrs`。
## F2 Fleet 更新(在 M6 之前)
> **状态**:容器节点(Agent、Probe)的更新已经实现并在线上验证过(签名发布清单、面板转发、宿主机更新器验签、按 digest 拉取、健康确认和回滚,见 [deployment.md](deployment.md) 的「容器节点的更新」)。**还没实现**:纯二进制安装方式的 `fleet-updater` 和 `fleet-release`、Windows 服务和 OpenWrt 的适配器、通道与灰度(现在由管理员在管理端手动选节点)、`POLICY=auto` 的线上验证。下面保留原来的完整设计。
- `Fleet.Update`、`fleet-updater`(systemd 与 Windows 服务适配器、健康信标、回滚、防回滚)、`fleet-release`(CI 签名)、`Fleet.Server.Releases`(通道、灰度、版本汇总)。
**验收**:
- 用 EchoNode 和 monitor-agent 两个应用验证“同一个 updater 管理多个产品”;
- 验签失败、sha256 不符、新版本健康检查超时,这三种情况都能正确拒绝或回滚;
- 面板被替换为恶意面板时,节点不会安装未签名的版本。
## M6 加固与部署
- OpenWrt 打包:procd 脚本、ipk/apk 包、sysupgrade 保留列表、`libopenssl3` 依赖检查;在 aarch64 路由器上跑满 24 小时,并运行一次 `hy2_ladder`(观察 CPU 饱和标记)。
- 安装脚本的下载和验签(现在的 `install.sh` 从解压好的发布目录安装)、`install.ps1` 和 Windows 服务、`fleet-updater` 的配置。
- 安全:对 UDP 包、控制帧、RawChunk、JSON-RPC 做模糊测试;伪造签名和重放测试;检查登录限流和 CSRF。
## 以后
- 告警(Webhook);真实 QUIC 握手积木(依赖 msquic,需要单独评估);pcap/eBPF 采集。
## 实现状态(后端与 Agent)
已实现并有自动化测试(331 个,另有 2 个跳过的测试,其中 1 个需要运行中 NativeAOT 产物):
- **Probe 直连面板**:Probe 用自己的身份连 Backend(`probe.json` 里有 `server`),Agent 列表由面板作为期望状态 `probe_agents` 下发;Agent 不再中转列表和 Probe 的系统信息。
- **节点信息与构建信息**:系统、架构、国家和运营商(Backend 用来源地址查询,节点不访问外部服务)、版本、tag、提交和构建时间;展示端按 `site.show_build` 开关显示,管理端「系统 → 版本」看全部。
- **容器节点的更新**:CI 对镜像 digest 签名,面板转发,宿主机 `monitor-update` 验签、拉取、重启并在不健康时回滚;管理端有更新状态和按钮,CI 用只能登记清单的 `release` 令牌。
- **多 Probe**:Probe 身份由 Backend 生成(创建、轮换密钥、吊销、删除),一条线路是一对(Probe,Agent),Agent 为每个 Probe 会话各起一组流,数据按 `line:<probe_id>` 隔离;Agent 上报连着的 Probe(id、版本、远端地址、时间),管理端可以断开某个 Probe;吊销立即生效。
- **`tcp_bulk` 原语**:批量传输(fwd / rev、本地码率上限、两端的 tcp_info),评分用持续 1 秒的吞吐峰值。
- **Agent 主机状态**:只上报汇总数字(没有型号和 IP),实时推给展示页,每分钟存一个样本。
- **前端**:`web/display`(Agent 卡片总览、线路详情、按 Probe 的线路对比,拖动放大、只提供有数据的时间范围)和 `web/admin`(Agent、Probe、任务、按需测试、系统),共享「纸墨」主题;页面里可以把数据源指到别的后端(API 令牌 + 可选的 CORS 白名单 `Monitor__CorsOrigins`)。
- **真实线路试跑**:广东移动家宽 → 日本 IIJ 的 VPS(见 deployment.md)。
- **F1 的服务端部分**:`Fleet.Core/Identity/Rpc`、`Fleet.Server.Abstractions/Enrollment/Link`(注册、签名认证、期望状态同步、发件箱收件箱、服务端发起的调用)。
- **M3**:注册、RPC、段文件与 SQLite、任务集下发、管理 API。
- **M4(除校准外)**:累加器、算子、指标注册制、四个模型、流式与持久两条路径、降采样、指标指纹与回填、查询层(范围、步长、数据分层、精确汇总、列式响应)、SSE、按需测试。
- **M5 的一部分**:保留清理。
- **F1 的节点部分**:`Fleet.Link`(链路、发件箱、点对点监听与连接)、`Fleet.Hosting`、`Fleet.Enrollment.Client` 与 `fleet-enroll`。
- **Agent(M1 的一部分)**:`Monitor.Agent`(NativeAOT 可发布)与 `Monitor.Measurement`(列块合并、预算守卫)。UDP/TCP 数据面(认证、重放窗口、限速、端点学习)、线路任务调和、目标任务(`icmp_echo`、`connect`)、按需测试编排(`udp_train` 由 Agent 发包)。测试里的假 Probe(`ProbeSimulator`)通过真实的点对点握手连接真实的 Agent 与后端,端到端覆盖。详见 `backend/agent/README.md`。
- **Probe(M0/M2 的一部分)**:`Monitor.Probe`(NativeAOT,5.6 MB,常驻约 22 MB)——Agent 列表(由面板经 Fleet 长连接直接下发,写回 `agents.json`)、每个 Agent 一个自动重连的会话、`udp_echo`/`tcp_echo`/`connect`/`hold` 角色、`udp_train` 接收端(按序号范围切块)、时钟偏移估计、有界发件箱。真实 Probe + 真实 Agent + 真实后端的端到端测试,以及用发布的 AOT 二进制做过的冒烟(内存稳定、CPU < 1%)。详见 `backend/probe/README.md`。
- **采集积木统一(重构)**:Probe 和 Agent 的数据面合并成 `Monitor.Measurement` 里的一份实现(每种采集方法一个文件、拨号方和监听方一对角色,`UdpMux`/`Tcp`/`RowStore` 共用);合并改成由 Kind 的流声明(`Driver`、`Key`)驱动;测试方案移回 Backend;架构测试禁止采集层出现业务概念。
- **TCP 内核采样与长尾**:`tcp_echo` 两端各自采样套接字的 `TCP_INFO`(Linux,`union` 流拼接);`tcp.rtt` 增加 `p999`、`max`、长尾比 `tail`,新增 `tcp.slow`(块内基线的慢回显占比);序列接口增加 `anchor=latest`。
- **打包与部署(M6 的一部分)**:`scripts/publish.sh`(linux-musl-x64、linux-x64 在 NativeAOT SDK 容器里构建)、`scripts/package.sh`、`packaging/`(`install.sh`、systemd 单元、OpenWrt procd 脚本)、节点启动时的 OpenSSL 自检。在 Alpine、Debian、OpenWrt 官方 rootfs、带 systemd 的 Debian 容器里验证过。详见 `docs/deployment.md`。
未实现:
- `monitor-bench`(M0/M2);arm64 构建(需要 arm64 主机或 CI)、真正的 OpenWrt 设备;`udp_train` 只支持 `direction=rev`;跨公网线路只试跑过一条(几分钟),还没有长期试跑。
- 告警;定时调度和按需测试的长期峰值趋势;Windows Probe 没有 tcp_info;Probe 会话结束时最后 ≤10 秒的数据(不修,极端情况)。多个 Probe 同时连时,码率和包速率的预算按单个 Probe 会话计算,总负载是它的倍数,靠会话数上限(默认 8)约束。
- `fleet-updater`、`fleet-release`、`Fleet.Update`、`Fleet.Server.Releases`、`Fleet.Server.Sqlite`(F2;Monitor 自己实现 `IFleetStore`)。
- `/api/admin/releases`、`/install.sh`、`/install.ps1`(安装脚本返回 501,依赖打包 M6)。
- M4 的校准(需要在真实线路上部署 Hysteria2 与 REALITY 对比)和 M5 的 30 天规模验证;模型评分曲线是 v1 的经验值。
- 查询的 `tz` 只支持 UTC(其他值返回 400);`probe.endpoint_changes` 指标未进入注册表(计数在 Agent 运行时状态里)。