diff --git a/.woodpecker/verify.yml b/.woodpecker/verify.yml index b8b2958..c9b527e 100644 --- a/.woodpecker/verify.yml +++ b/.woodpecker/verify.yml @@ -23,4 +23,4 @@ steps: environment: NUGET_PACKAGES: /home/justme/.nuget/packages commands: - - cd backend && dotnet test --solution Monitor.slnx -c Release + - cd backend && dotnet test --solution MoShiro.slnx -c Release diff --git a/AGENTS.md b/AGENTS.md index 5925e03..4b59fea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Project -Monitor is a self-hosted network path quality monitor. Backend coordinates Agents and Probes, stores measurements, and serves the REST/SSE API. `web/display` is the read-only overview; `web/admin` manages nodes, tests, and settings. +MoShiro is a self-hosted network path quality monitor. Backend coordinates Agents and Probes, stores measurements, and serves the REST/SSE API. `web/display` is the read-only overview; `web/admin` manages nodes, tests, and settings. ## Branch and workspace safety @@ -10,12 +10,12 @@ Monitor is a self-hosted network path quality monitor. Backend coordinates Agent - A push to `main` builds images only if its commit message contains `[CI]` (for a merged PR, put it in the PR or merge commit title). Without `[CI]` nothing is built; use `[CI SKIP]` to skip the pipeline entirely. When squashing or pushing to `main`, add `[CI]` unless a build is explicitly not wanted. - Before editing, inspect `git status`. Preserve pre-existing user changes: do not reset, clean, stash, or include unrelated files in a commit. - Do not commit credentials, API keys, registry passwords, or generated runtime data. CI credentials belong in Woodpecker secrets (`zot_secret`, `gitea_token`, `release_signing_key` for signing release manifests, `release_api_token` with the `release` scope). The release signing private key never goes into the repository or the panel; only the public key in `packaging/update/` does. -- Keep development OCI images in the `monitor-dev/` repository path and release images in `monitor/`. Do not let a development build overwrite release tags such as `latest`. +- Keep development OCI images in the `moshiro-dev/` repository path and release images in `moshiro/`. Do not let a development build overwrite release tags such as `latest`. ## Project layout -- `backend/src/Monitor.Backend/`: service entry point and composition. -- `backend/src/Monitor.Backend.Api/`: REST endpoints, contracts, and OpenAPI. +- `backend/src/MoShiro.Backend/`: service entry point and composition. +- `backend/src/MoShiro.Backend.Api/`: REST endpoints, contracts, and OpenAPI. - `backend/agent/`, `backend/probe/`: node applications and their READMEs. - `backend/fleet/`, `backend/shared/`: reusable node framework, measurement engine, protocols, and data formats. - `web/display/`, `web/admin/`: Vite applications; `web/shared/`: API client, generated types, charts, and shared UI. @@ -25,12 +25,12 @@ Monitor is a self-hosted network path quality monitor. Backend coordinates Agent Requirements: .NET SDK 10 and Node.js/npm. The SDK selection is defined by the root `global.json`; NuGet package versions are centralized in the root `Directory.Packages.props`. -- All .NET projects: `dotnet build Monitor.slnx -c Release` from the repository root. -- Backend build: `cd backend && dotnet build Monitor.slnx -c Release` -- Backend tests: `cd backend && dotnet test --solution Monitor.slnx -c Release` +- All .NET projects: `dotnet build MoShiro.slnx -c Release` from the repository root. +- Backend build: `cd backend && dotnet build MoShiro.slnx -c Release` +- Backend tests: `cd backend && dotnet test --solution MoShiro.slnx -c Release` - Frontend dependencies/build: `npm ci --prefix web && npm run build --prefix web` - Frontend style check: `npm run lint:css --prefix web` -- Local backend: from `backend/`, run `dotnet run --project src/Monitor.Backend -- --urls http://localhost:5000`. +- Local backend: from `backend/`, run `dotnet run --project src/MoShiro.Backend -- --urls http://localhost:5000`. - Local web apps: from the repository root, run `npm ci --prefix web` then `npm run dev --prefix web`. The current dev config uses display port 5173 and admin port 5175; `/admin/` is proxied through the display server. Check the Vite configs when working on another branch because ports can differ. - After changing API contracts, regenerate/check `backend/openapi/v1.json` and run `npm run gen:api --prefix web` so frontend API types stay aligned. diff --git a/Directory.Packages.props b/Directory.Packages.props index 55fb462..b6b6d54 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -2,9 +2,9 @@ true - 3530400 - 2026 - 1e71ddf93849c6a6ecf58b827c0692073d2dd7ee40196158068f7b29f422e87d + 3530400 + 2026 + 1e71ddf93849c6a6ecf58b827c0692073d2dd7ee40196158068f7b29f422e87d diff --git a/Monitor.slnx b/MoShiro.slnx similarity index 55% rename from Monitor.slnx rename to MoShiro.slnx index 8a5c58c..f15c43c 100644 --- a/Monitor.slnx +++ b/MoShiro.slnx @@ -17,36 +17,36 @@ - - + + - - + + - + - + - + - - - - - - + + + + + + - - - + + + - + diff --git a/README.md b/README.md index 96b9d39..da70858 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,36 @@ -# Monitor +# MoShiro -Monitor 是一个自托管的网络线路质量观测工具。它在 Probe 与多个 Agent 之间执行主动网络测量,记录逐包和逐事件结果;Backend 保存原始数据并生成可查询的线路指标,Web 界面用于查看、比较和管理节点。 +MoShiro 是一个自托管的**网络线路质量观测工具**。它在若干 Probe(例如家宽出口)与多台 Agent(例如海外 VPS)之间做主动网络测量,只记录逐包、逐事件的原始数据;Backend 保存这些原始数据,推算延迟、丢包、抖动、吞吐等指标,并回答「如果在这条线路上部署某类服务,体验会怎样」;Web 界面用来查看、比较和管理。 -它面向需要比较不同网络出口、评估线路稳定性,或观察特定网络服务体验的场景。Agent 与 Probe 专注于测量和上报;它们不提供远程终端、命令执行或文件管理能力。 +## 项目边界 -## 能力概览 +**它做什么** -- **主动测量**:组合 TCP、UDP 等测量模块,采集延迟、丢包、抖动、吞吐和连接事件。 -- **线路分析**:从原始测量数据生成时间序列指标,并比较线路在不同服务模型下的体验。 -- **节点管理**:通过 Fleet 节点框架处理身份、注册、连接、任务同步和数据上报。 -- **实时面板**:展示端查看线路状态和历史趋势;管理端负责节点、任务和系统配置。 -- **线路管理**:Backend 决定哪些 Probe 连哪些 Agent(自动连接开关加手动选择),在管理端的线路矩阵里一键开关。 -- **节点信息与更新**:节点自报系统、架构和构建信息(版本、tag、提交、构建时间),国家和运营商由 Backend 查询;容器节点的更新由 CI 签名、Backend 转发、节点宿主机上的更新器验签并执行,失败自动回滚,管理端可以看状态和点「更新」。 -- **自托管**:Backend 使用段文件和 SQLite 保存数据;提供 Linux、systemd 和 OpenWrt 相关部署文件。 +- 主动测量:组合 TCP、UDP、TLS、ICMP 等采集积木,只记录时间、序号、大小这类原始事实,**不转发也不记录业务内容**。 +- 线路分析:Backend 从原始数据生成时间序列指标,按服务模型(REALITY、Hysteria2、Minecraft 等)评分并对多条线路排序、对比;模型和指标可以事后重算。 +- 管理:节点注册与审批、Probe 和 Agent 之间连哪些线路、任务与按需测试、节点版本与更新,都在管理端完成。 + +**它不做什么** + +- **节点是纯采集器**:Agent 和 Probe 没有终端、命令执行、文件读写和自更新能力,也不加载外部代码。更新由节点宿主机上的更新器完成,节点进程只放面板转来的发布清单。 +- **不是主机监控**:Agent 只上报 CPU、内存、磁盘、网卡流量和运行时长这类只读的汇总数字,不采集型号、主机名、进程和任何 IP;Probe 不上报主机资源。 +- **不暴露地址**:公开接口里没有 IP、城市、ASN;国家和运营商只由 Backend 查询(节点自己不访问任何外部服务)。 +- 暂不包含告警(见文末待办)。 + +## 信任关系 + +```text + 管理员 ── 账号密码(Cookie 会话 + CSRF)或 API 令牌(read · admin · release)──▶ Backend + 访客 ── 匿名只读,站点设为公开时才可用(没有 IP、城市、ASN)──────────────▶ Backend 公开接口 + + 节点 ── 加入令牌一次性注册,之后用自己的 ECDSA P-256 密钥签名认证 ───────▶ Backend(只存公钥) + Probe ── 用同一把密钥签名建立测量会话 ───────────────────────────────────▶ Agent(只放行面板名单里的 Probe 公钥) + CI ── 发布私钥对「镜像 digest 清单」签名 ──▶ Backend 原样转发 ──▶ 节点宿主机的更新器用本机公钥验签 +``` + +- **Backend 不信任节点上报的内容**:数据按线路隔离存放;节点的身份靠签名,数据库泄露也无法冒充节点。 +- **节点不信任 Backend 的下发**:任务超出节点本机的硬上限(目标网段、码率、包速率、监听端口)就被拒绝;节点不执行任何「执行类」指令。 +- **更新不依赖面板的诚实**:清单必须由发布私钥签名;节点宿主机的更新器用本机公钥验签、只认本机配置的镜像仓库、按 digest 拉取、拒绝旧的 `seq`。面板被攻破也推不出恶意镜像。 ## 架构 @@ -35,99 +53,68 @@ Monitor 是一个自托管的网络线路质量观测工具。它在 Probe 与 测量会话:TLS 控制通道 + UDP/TCP 数据通道,由 Probe 主动拨出 ``` -两种连接:**Probe 和 Agent 都用长连接连 Backend**(身份、任务、状态、更新通知),**测量只走 Probe → Agent**(Probe 主动拨出,不需要入站端口)。哪些 Probe 连哪些 Agent 由 Backend 决定:默认自动全连,也可以在管理端手动选择。数据存储:段文件保存原始数据,SQLite 保存索引、指标与配置。 +两种连接:**Probe 和 Agent 都用长连接连 Backend**(身份、任务、状态、更新通知),**测量只走 Probe → Agent**(Probe 主动拨出,Agent 不需要连 Probe,家宽也不需要入站端口)。哪些 Probe 连哪些 Agent 由 Backend 决定:默认自动全连,也可以在管理端手动选择。 -## 快速开始 - -### 环境要求 -- .NET SDK 10;项目使用 `global.json` 指定 SDK 版本和滚动策略。 -- Node.js 与 npm,用于构建 Web 前端。 - -### 构建 - -从仓库根目录构建全部 .NET 项目(Backend、Agent、Probe、Fleet、测试和 CI 工具): - -```sh -dotnet build Monitor.slnx -c Release +```text + 采集(只记录) 传输(有界、至少一次) 分析(注册制、可重算) 展示 + Probe / Agent 积木 ──▶ 原始列块(10 秒一块)──▶ Backend 段文件 ──▶ 指标与服务模型 ──▶ REST / SSE ──▶ Web ``` -NuGet 包版本统一在根目录 `Directory.Packages.props` 管理,项目中的 `PackageReference` 不写版本号。AotAnywhere 的 MSBuild SDK 版本在根目录 `global.json` 的 `msbuild-sdks` 中管理。`backend/Monitor.slnx` 保留用于只构建后端目录内的项目。 +Backend 用段文件保存原始数据,用 SQLite 保存索引、指标与配置。**完整的架构、数据链路、安全边界和设计决策见 [`docs/architecture.md`](docs/architecture.md)。** -安装前端依赖并构建展示端和管理端(在仓库根目录运行): +## 协议 + +- **控制链路(Fleet)**:节点到 Backend 的 `wss` 长连接(`/api/agent/v1/rpc`),JSON-RPC 2.0 信封加二进制通道;挑战加签名认证,`hello` 协商版本和能力,心跳;**期望状态同步**(Backend 下发完整快照,节点调和并回报已应用的版本和被拒绝的条目);**可靠发件箱**(原始块至少一次投递,按 id 去重)。Fleet 与业务无关,放在 `backend/fleet/`。 +- **业务方法**:`moshiro.*`(按需测试、Probe 断开、事件、主机汇总、更新通知等),状态文档有 `task_set`(Agent 的任务集)和 `probe_agents`(Probe 的 Agent 列表)。 +- **测量数据面**:Probe 到 Agent 的 TLS 控制通道、UDP 和 TCP 数据通道(默认 `7443/tcp`、`7443/udp`、`7444/tcp`);会话用 `session_id` 和 MAC 认证、不绑定五元组,NAT 映射变化后自动跟随。 +- **REST + SSE**:`/api/public`(展示,可匿名)、`/api/admin`(管理);OpenAPI 在 `backend/openapi/v1.json`,前端据此生成类型。 + +细节见 [`docs/protocol.md`](docs/protocol.md)、[`docs/fleet/link.md`](docs/fleet/link.md)、[`docs/api.md`](docs/api.md)、[`docs/enrollment.md`](docs/enrollment.md)。 + +## 如何构建 + +环境:.NET SDK 10(`global.json` 指定版本)、Node.js 与 npm。在仓库根目录: ```sh -npm ci --prefix web -npm run build --prefix web -npm run gen:api --prefix web # API schema changed after Backend edits -npm run lint:css --prefix web +dotnet build MoShiro.slnx -c Release # Backend、Agent、Probe、Fleet、测试和 CI 工具 +npm ci --prefix web && npm run build --prefix web # 展示端和管理端,产物在 web/display/dist、web/admin/dist ``` -前端产物位于 `web/display/dist/` 和 `web/admin/dist/`。NativeAOT 目标平台和 Windows 上使用 AotAnywhere 交叉构建的说明见 [`docs/deployment.md`](docs/deployment.md)。 +本地运行:先在 `backend/` 里 `dotnet run --project src/MoShiro.Backend -- --urls http://localhost:5000`,再 `npm run dev --prefix web`,展示端是 ,管理端是 。 -Backend、Agent 和 Probe 均以 OCI 镜像发布,支持 Linux/amd64 与 Linux/arm64;Agent 和 Probe 另外提供 Linux glibc/musl、macOS 与 Windows 二进制包。容器部署配置见 [`compose.yaml`](compose.yaml),发布说明见 [`docs/deployment.md`](docs/deployment.md)。 - -### 本地运行 - -1. **启动 Backend。** 在仓库根目录运行: +测试和检查: ```sh -cd backend -dotnet run --project src/Monitor.Backend -- --urls http://localhost:5000 +dotnet test --solution MoShiro.slnx # 后端和节点的测试 +npm run lint:css --prefix web # 前端样式(颜色只能写在主题令牌里) +npm run gen:api --prefix web # 后端改了接口之后重新生成前端类型 ``` -2. **启动前端。** 保持 Backend 运行,在另一个终端的仓库根目录运行: +Backend、Agent、Probe 以 OCI 镜像发布(Linux amd64、arm64),Agent 和 Probe 另有 Linux glibc/musl、macOS、Windows 的二进制包。NativeAOT 的构建细节、CI 触发规则(`[CI]`、`[SKIP TESTS]`、版本 tag)、镜像路径、容器节点的运行和更新见 [`docs/deployment.md`](docs/deployment.md);前端见 [`web/README.md`](web/README.md),后端见 [`backend/README.md`](backend/README.md)。 -```sh -npm ci --prefix web -npm run dev --prefix web -``` +## 文档 -打开 使用展示端,或访问 进入管理端。依赖安装和前端配置见 [`web/README.md`](web/README.md);Backend 的首次启动与配置见 [`backend/README.md`](backend/README.md)。 - -### CI 构建规则 - -- Pull request 和 `dev` 分支 push:先跑 Backend tests,通过后再构建前端。 -- 在需要跳过 Backend tests 的提交信息后加 `[SKIP TESTS]`;Gitea pull request 需要把标记写进 PR 标题。前端构建仍会运行。 -- `main` 上的提交信息包含 `[CI]` 时,并行构建 Backend、Agent、Probe 的 Linux/amd64 镜像,推送到 Zot 的 `monitor-dev/` 仓库;普通 main push 不构建。 -- 推送 `vMAJOR.MINOR.PATCH` tag 会触发全平台正式构建和 Release 发布。 - -完整触发条件、开发镜像标签和发布路径见 [`docs/deployment.md`](docs/deployment.md)。 - -## 项目布局 - -| 路径 | 说明 | +| 文档 | 内容 | | --- | --- | -| [`backend/`](backend/README.md) | Backend 服务、Fleet 框架、共享测量代码与 .NET 测试 | -| [`backend/agent/`](backend/agent/README.md) | Agent 节点:注册到 Backend,接受 Probe 测量并上报数据 | -| [`backend/probe/`](backend/probe/README.md) | Probe 节点:发起线路测量并将采集数据交给 Agent | -| `backend/fleet/` | 可复用的节点身份、注册和连接框架 | -| `backend/shared/` | 测量引擎、协议和原始数据格式 | -| `web/display/`、`web/admin/` | 展示端和管理端 | -| `web/shared/` | 前端 API、图表、组件与主题 | -| `packaging/`、`scripts/` | 部署、构建与维护脚本 | -| `docs/` | [架构](docs/architecture.md)、[测量模型](docs/measurement.md)、[API](docs/api.md)、[部署](docs/deployment.md)等文档 | +| [`docs/architecture.md`](docs/architecture.md) | 架构、数据链路、安全边界、设计决策 | +| [`docs/protocol.md`](docs/protocol.md)、[`docs/fleet/`](docs/fleet/overview.md) | 线协议、Fleet 节点框架(身份、连接、更新) | +| [`docs/enrollment.md`](docs/enrollment.md) | 注册、身份、Probe 与 Agent 的接入 | +| [`docs/measurement.md`](docs/measurement.md)、[`docs/analysis.md`](docs/analysis.md) | 采集引擎与任务模型、分析层与指标 | +| [`docs/storage.md`](docs/storage.md)、[`docs/query.md`](docs/query.md) | 存储、展示数据查询 | +| [`docs/api.md`](docs/api.md)、[`docs/api-guidelines.md`](docs/api-guidelines.md) | API 规范与约束 | +| [`docs/deployment.md`](docs/deployment.md) | 构建、发布、CI、部署与更新 | +| [`docs/project-layout.md`](docs/project-layout.md)、[`docs/coding-style.md`](docs/coding-style.md) | 仓库结构与依赖规则、代码风格 | + +代码目录:`backend/`(Backend、`fleet/` 节点框架、`shared/` 测量引擎和协议、`agent/`、`probe/`、`tests/`)、`web/`(`display` 展示端、`admin` 管理端、`shared`)、`packaging/` 和 `scripts/`(部署与维护)、`ci/` 和 `.woodpecker/`(流水线)。 ## 待办 -已实现的能力见上面的概览和 `docs/`;还没做的: - -- **纯二进制节点的更新**:`fleet-updater`、`fleet-release`、Windows 服务和 OpenWrt(procd)适配器;设计见 [`docs/fleet/update.md`](docs/fleet/update.md)。容器节点(Agent、Probe)的签名发布和宿主机更新已经实现,见 [`docs/deployment.md`](docs/deployment.md)。 +- **纯二进制节点的更新**:`fleet-updater`、`fleet-release`、Windows 服务和 OpenWrt(procd)适配器;设计见 [`docs/fleet/update.md`](docs/fleet/update.md)。容器节点的签名发布和宿主机更新已经实现。 - **更新的灰度和自动策略**:按标签分批发布;`POLICY=auto` 的线上验证;后端自身的更新。 - **OpenWrt 打包**:procd 脚本、ipk/apk 包、sysupgrade 保留列表,在 aarch64 路由器上长时间运行验证。 - **安全加固**:对 UDP 包、控制帧、RawChunk、JSON-RPC 做模糊测试;伪造签名和重放的专项测试。 - **以后**:告警(Webhook)、真实 QUIC 握手积木、pcap/eBPF 采集。 -## 测试 - -```sh -dotnet test --solution Monitor.slnx -``` - -```sh -# 前端样式检查(仓库根目录) -npm run lint:css --prefix web -``` - ## 许可证 本项目采用 [Apache License 2.0](LICENSE)。 diff --git a/backend/Monitor.slnx b/backend/MoShiro.slnx similarity index 58% rename from backend/Monitor.slnx rename to backend/MoShiro.slnx index c69c642..e453cc3 100644 --- a/backend/Monitor.slnx +++ b/backend/MoShiro.slnx @@ -17,33 +17,33 @@ - - + + - - + + - + - + - + - - - - - - + + + + + + - - - + + + diff --git a/backend/README.md b/backend/README.md index aae510d..7e0021f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,17 +1,17 @@ # Backend -Backend 是 Monitor 的控制面和数据服务,负责 Agent 注册与连接、任务编排、原始测量数据持久化、指标分析,以及供前端使用的 REST API 和 SSE 实时数据流。 +Backend 是 MoShiro 的控制面和数据服务,负责 Agent 注册与连接、任务编排、原始测量数据持久化、指标分析,以及供前端使用的 REST API 和 SSE 实时数据流。 ## 组件 | 路径 | 职责 | | --- | --- | -| `src/Monitor.Backend/` | 应用入口与依赖组合 | -| `src/Monitor.Backend.Api/` | Public、Admin API 和 OpenAPI | -| `src/Monitor.Backend.Gateway/` | Fleet RPC、节点状态与测量数据接收 | -| `src/Monitor.Backend.Application/` | 业务用例、查询、任务和后台服务 | -| `src/Monitor.Backend.Analysis/` | 指标计算、累加器和服务模型 | -| `src/Monitor.Backend.Storage.Sqlite/` | 段文件与 SQLite 存储 | +| `src/MoShiro.Backend/` | 应用入口与依赖组合 | +| `src/MoShiro.Backend.Api/` | Public、Admin API 和 OpenAPI | +| `src/MoShiro.Backend.Gateway/` | Fleet RPC、节点状态与测量数据接收 | +| `src/MoShiro.Backend.Application/` | 业务用例、查询、任务和后台服务 | +| `src/MoShiro.Backend.Analysis/` | 指标计算、累加器和服务模型 | +| `src/MoShiro.Backend.Storage.Sqlite/` | 段文件与 SQLite 存储 | | `fleet/` | 通用节点身份、注册和连接框架 | | `shared/` | 测量引擎、协议和数据格式 | | `tests/` | 集成测试和测试支持代码 | @@ -26,14 +26,14 @@ Backend 是 Monitor 的控制面和数据服务,负责 Agent 注册与连接 ```sh cd backend -dotnet build Monitor.slnx -c Release -dotnet test --solution Monitor.slnx +dotnet build MoShiro.slnx -c Release +dotnet test --solution MoShiro.slnx ``` 发布当前平台的 NativeAOT Backend: ```sh -dotnet publish src/Monitor.Backend -c Release -r -p:PublishAot=true +dotnet publish src/MoShiro.Backend -c Release -r -p:PublishAot=true ``` 例如 `` 可为 `win-x64`、`linux-x64` 或 `linux-arm64`。NativeAOT 发布需要对应平台的 C/C++ 工具链。仓库根目录的 `scripts/publish.sh backend ` 可构建单个平台的 Backend 可执行文件;容器发布由 Woodpecker tag 流水线完成,详见 [`../docs/deployment.md`](../docs/deployment.md)。 @@ -46,9 +46,9 @@ dotnet publish src/Monitor.Backend -c Release -r -p:PublishAot=true ```sh cd backend -dotnet run --project src/Monitor.Backend -- --urls http://localhost:5000 +dotnet run --project src/MoShiro.Backend -- --urls http://localhost:5000 ``` -首次启动时可用 `Monitor__InitialAdminUser` 和 `Monitor__InitialAdminPassword` 设置初始管理员。数据目录、公开地址、Cookie 和 CORS 等设置通过 `Monitor` 配置节传入;完整配置说明见 [`../docs/deployment.md`](../docs/deployment.md)。站点默认需要登录,可在管理端修改公开设置。 +首次启动时可用 `MoShiro__InitialAdminUser` 和 `MoShiro__InitialAdminPassword` 设置初始管理员。数据目录、公开地址、Cookie 和 CORS 等设置通过 `MoShiro` 配置节传入;完整配置说明见 [`../docs/deployment.md`](../docs/deployment.md)。站点默认需要登录,可在管理端修改公开设置。 OpenAPI 文档由构建生成到 `openapi/v1.json`。API 设计和端点说明见 [`../docs/api.md`](../docs/api.md);项目总览见仓库根目录的 [`README.md`](../README.md)。 diff --git a/backend/agent/README.md b/backend/agent/README.md index c52e71a..b70a50b 100644 --- a/backend/agent/README.md +++ b/backend/agent/README.md @@ -1,4 +1,4 @@ -# Monitor Agent +# MoShiro Agent Agent 是部署在 VPS 或其他可达网络节点上的采集进程。它向 Backend 注册并保持连接,接受 Probe 的测量会话,执行线路任务中的 Agent 侧工作,再将两端数据合并后上报。 @@ -18,14 +18,14 @@ Probe ──TLS 控制 / UDP、TCP 测量──> Agent ──长连接与数据 ```sh cd backend -dotnet build agent/src/Monitor.Agent -c Release +dotnet build agent/src/MoShiro.Agent -c Release dotnet build fleet/tools/fleet-enroll -c Release ``` 发布 NativeAOT 程序需要对应平台的 C/C++ 工具链;`` 应与构建环境和目标平台匹配: ```sh -dotnet publish agent/src/Monitor.Agent -c Release -r -p:PublishAot=true +dotnet publish agent/src/MoShiro.Agent -c Release -r -p:PublishAot=true dotnet publish fleet/tools/fleet-enroll -c Release -r -p:PublishAot=true ``` @@ -36,7 +36,7 @@ dotnet publish fleet/tools/fleet-enroll -c Release -r -p:PublishAot=true 先在管理端创建加入令牌,然后在 Agent 主机上运行一次性注册工具: ```sh -fleet-enroll --server https:// --join --name --identity-dir /etc/monitor +fleet-enroll --server https:// --join --name --identity-dir /etc/moshiro ``` 注册工具会在身份目录写入节点身份和证书。准备本地 `agent.json`,其中 `fleet.server` 指向 Backend,`fleet.identity_dir` 指向上述目录;`listen`、`limits` 和 `reporting` 可按节点环境调整。配置文件不会由 Backend 修改。限制目标网段和测量预算时,应按实际网络与机器能力配置;`allowed_target_cidrs` 默认为空,不允许 Agent 执行目标探测任务。 @@ -44,12 +44,12 @@ fleet-enroll --server https:// --join --name --ide 启动 Agent: ```sh -monitor-agent --config /etc/monitor/agent.json +moshiro-agent --config /etc/moshiro/agent.json ``` -容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md)。也可使用 OCI 镜像 `registry.oeo.one/monitor/monitor-agent:`,自动选择 Linux amd64 或 arm64 镜像。镜像同时包含 `fleet-enroll`;可通过 `--entrypoint /usr/local/bin/fleet-enroll` 执行首次注册。容器默认读取命名 volume 中的 `/etc/monitor/agent.json` 与身份文件;建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。镜像以 UID/GID 1654 运行,新命名 volume 由引擎按镜像目录初始化权限。 +容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md)。也可使用 OCI 镜像 `registry.oeo.one/moshiro/moshiro-agent:`,自动选择 Linux amd64 或 arm64 镜像。镜像同时包含 `fleet-enroll`;可通过 `--entrypoint /usr/local/bin/fleet-enroll` 执行首次注册。容器默认读取命名 volume 中的 `/etc/moshiro/agent.json` 与身份文件;建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。镜像以 UID/GID 1654 运行,新命名 volume 由引擎按镜像目录初始化权限。 -不传 `--config` 时默认读取 `/etc/monitor/agent.json`。Agent 默认监听控制端口 `7443/tcp`,数据端口 `7443/udp` 和 `7444/tcp`;防火墙和面板中的节点端口配置需与本地配置一致。`reporting.host_stats` 可关闭主机状态上报;启用时只上报资源使用等汇总数据,不包含主机名、进程或 IP。 +不传 `--config` 时默认读取 `/etc/moshiro/agent.json`。Agent 默认监听控制端口 `7443/tcp`,数据端口 `7443/udp` 和 `7444/tcp`;防火墙和面板中的节点端口配置需与本地配置一致。`reporting.host_stats` 可关闭主机状态上报;启用时只上报资源使用等汇总数据,不包含主机名、进程或 IP。 ## 设计说明 diff --git a/backend/agent/src/Monitor.Agent/AgentApp.cs b/backend/agent/src/MoShiro.Agent/AgentApp.cs similarity index 89% rename from backend/agent/src/Monitor.Agent/AgentApp.cs rename to backend/agent/src/MoShiro.Agent/AgentApp.cs index a23f2f8..70d72b6 100644 --- a/backend/agent/src/Monitor.Agent/AgentApp.cs +++ b/backend/agent/src/MoShiro.Agent/AgentApp.cs @@ -4,15 +4,15 @@ using Fleet.Core; using Fleet.Hosting; using Fleet.Link; using Fleet.Rpc; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// Agent 的组装点:数据面、点对点监听、与 Backend 的链路都在这里接起来。没有全局状态,测试可以在同一进程里启动多个。 public sealed class AgentApp : IDisposable { - public const string Version = Monitor.Protocol.BuildInfo.Version; + public const string Version = MoShiro.Protocol.BuildInfo.Version; private readonly AgentConfig _config; private readonly LoadedNodeIdentity _identity; @@ -35,7 +35,7 @@ public sealed class AgentApp : IDisposable private readonly TestRunner _tests; private readonly FleetLink _link; - /// 配置目录(容器里的 /etc/monitor):宿主机更新器和节点在这里交换清单、请求和状态。 + /// 配置目录(容器里的 /etc/moshiro):宿主机更新器和节点在这里交换清单、请求和状态。 public AgentApp(AgentConfig config, LoadedNodeIdentity identity, string dataDirectory, TimeProvider time, INodeLog log) { _update = new UpdateBridge(dataDirectory, new ConfigStore(dataDirectory).Write, log); @@ -185,48 +185,48 @@ public sealed class AgentApp : IDisposable HashSet? allowed = _config.Policy.AllowMethods is { } list ? [.. list] : null; bool Allow(string method) => allowed is null || allowed.Contains(method); - if (Allow(MonitorMethods.TestRun)) + if (Allow(MoShiroMethods.TestRun)) { router.On( - MonitorMethods.TestRun, - MonitorJsonContext.Default.TestRunParams, - MonitorJsonContext.Default.TestRunResult, + MoShiroMethods.TestRun, + MoShiroJsonContext.Default.TestRunParams, + MoShiroJsonContext.Default.TestRunResult, (_, run, _) => ValueTask.FromResult(_tests.Start(run))); } - if (Allow(MonitorMethods.TestCancel)) + if (Allow(MoShiroMethods.TestCancel)) { router.On( - MonitorMethods.TestCancel, - MonitorJsonContext.Default.TestCancelParams, - MonitorJsonContext.Default.TestRunResult, + MoShiroMethods.TestCancel, + MoShiroJsonContext.Default.TestCancelParams, + MoShiroJsonContext.Default.TestRunResult, (_, cancel, _) => ValueTask.FromResult(Result.Ok(new TestRunResult { Accepted = _tests.Cancel(cancel.TestId) }))); } - if (Allow(MonitorMethods.UpdateOffer)) + if (Allow(MoShiroMethods.UpdateOffer)) { - router.OnNotify(MonitorMethods.UpdateOffer, MonitorJsonContext.Default.UpdateOfferParams, (_, offer, _) => + router.OnNotify(MoShiroMethods.UpdateOffer, MoShiroJsonContext.Default.UpdateOfferParams, (_, offer, _) => { _update.WriteOffer(offer); return ValueTask.CompletedTask; }); } - if (Allow(MonitorMethods.UpdateApply)) + if (Allow(MoShiroMethods.UpdateApply)) { - router.OnNotify(MonitorMethods.UpdateApply, MonitorJsonContext.Default.UpdateOfferParams, (_, _, _) => + router.OnNotify(MoShiroMethods.UpdateApply, MoShiroJsonContext.Default.UpdateOfferParams, (_, _, _) => { _update.RequestApply(); return ValueTask.CompletedTask; }); } - if (Allow(MonitorMethods.ProbeDisconnect)) + if (Allow(MoShiroMethods.ProbeDisconnect)) { router.On( - MonitorMethods.ProbeDisconnect, - MonitorJsonContext.Default.ProbeDisconnectParams, - MonitorJsonContext.Default.TestRunResult, + MoShiroMethods.ProbeDisconnect, + MoShiroJsonContext.Default.ProbeDisconnectParams, + MoShiroJsonContext.Default.TestRunResult, async (_, request, _) => Result.Ok(new TestRunResult { Accepted = await _hub.DisconnectAsync(request.ProbeId.Value) })); } @@ -236,11 +236,11 @@ public sealed class AgentApp : IDisposable Identity = _identity.Identity, NodeId = _identity.NodeId, NodeVersion = Version, - Capabilities = [MonitorCapabilities.Base, MonitorCapabilities.KindsPrefix + string.Join(',', BlockCatalog.AgentKinds)], + Capabilities = [MoShiroCapabilities.Base, MoShiroCapabilities.KindsPrefix + string.Join(',', BlockCatalog.AgentKinds)], HelloMeta = HelloMeta, HeartbeatExtra = HeartbeatExtra, }; - var link = new FleetLink(options, router, [new StateHandler(TaskSet.StateType, MonitorJsonContext.Default.TaskSet, _tasks.ApplyAsync)], _outbox, _time, _log); + var link = new FleetLink(options, router, [new StateHandler(TaskSet.StateType, MoShiroJsonContext.Default.TaskSet, _tasks.ApplyAsync)], _outbox, _time, _log); // 链路重新连上之后补报:在线的 Probe 都告诉 Backend(Backend 重启或链路抖动期间错过了 probe_connected) link.OnConnected = async (_, _) => @@ -257,7 +257,7 @@ public sealed class AgentApp : IDisposable { var meta = new AgentHelloMeta { - Platform = Monitor.Measurement.PlatformDetector.Detect(), + Platform = MoShiro.Measurement.PlatformDetector.Detect(), TlsSpki = _identity.File.TlsSpki, ProbeTcpPort = ControlPort, ProbeUdpPorts = [.. UdpPorts], @@ -269,7 +269,7 @@ public sealed class AgentApp : IDisposable MaxConcurrentTests = _config.Limits.MaxConcurrentTests, }, }; - return JsonSerializer.SerializeToElement(meta, MonitorJsonContext.Default.AgentHelloMeta); + return JsonSerializer.SerializeToElement(meta, MoShiroJsonContext.Default.AgentHelloMeta); } private long _lastIn; @@ -296,6 +296,6 @@ public sealed class AgentApp : IDisposable _lastIn = packetsIn; _lastOut = packetsOut; _lastAtMs = nowMs; - return JsonSerializer.SerializeToElement(extra, MonitorJsonContext.Default.AgentHeartbeatExtra); + return JsonSerializer.SerializeToElement(extra, MoShiroJsonContext.Default.AgentHeartbeatExtra); } } diff --git a/backend/agent/src/Monitor.Agent/AgentConfig.cs b/backend/agent/src/MoShiro.Agent/AgentConfig.cs similarity index 96% rename from backend/agent/src/Monitor.Agent/AgentConfig.cs rename to backend/agent/src/MoShiro.Agent/AgentConfig.cs index 16ed3e9..cc89cb3 100644 --- a/backend/agent/src/Monitor.Agent/AgentConfig.cs +++ b/backend/agent/src/MoShiro.Agent/AgentConfig.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// /// agent.json:节点本地配置,面板无法修改。其中 limits 是本地硬上限:期望任务集超出这些范围时, @@ -40,7 +40,7 @@ public sealed class FleetSection public string Server { get; set; } = ""; [JsonPropertyName("identity_dir")] - public string IdentityDir { get; set; } = "/etc/monitor"; + public string IdentityDir { get; set; } = "/etc/moshiro"; } public sealed class ListenSection diff --git a/backend/agent/src/Monitor.Agent/Chunks/ChunkAssembler.cs b/backend/agent/src/MoShiro.Agent/Chunks/ChunkAssembler.cs similarity index 95% rename from backend/agent/src/Monitor.Agent/Chunks/ChunkAssembler.cs rename to backend/agent/src/MoShiro.Agent/Chunks/ChunkAssembler.cs index fc0098d..21f050e 100644 --- a/backend/agent/src/Monitor.Agent/Chunks/ChunkAssembler.cs +++ b/backend/agent/src/MoShiro.Agent/Chunks/ChunkAssembler.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Fleet.Link; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// /// Probe 的列块到达之后:找到对应的流 → 与 Agent 在数据面记录的列合并 → 盖章(块头、时钟锚点)→ 编码 → 放进发件箱(通道 16)。 @@ -70,6 +70,6 @@ public sealed class ChunkAssembler /// Agent 自己采集的块(目标任务)直接发出。 public void Emit(RawChunk chunk) { - _outbox.Enqueue(MonitorChannels.RawChunk, chunk.Header.ChunkId.Value, RawChunkCodec.Encode(chunk)); + _outbox.Enqueue(MoShiroChannels.RawChunk, chunk.Header.ChunkId.Value, RawChunkCodec.Encode(chunk)); } } diff --git a/backend/agent/src/Monitor.Agent/DataPlane/Registries.cs b/backend/agent/src/MoShiro.Agent/DataPlane/Registries.cs similarity index 99% rename from backend/agent/src/Monitor.Agent/DataPlane/Registries.cs rename to backend/agent/src/MoShiro.Agent/DataPlane/Registries.cs index fff6753..5703c4d 100644 --- a/backend/agent/src/Monitor.Agent/DataPlane/Registries.cs +++ b/backend/agent/src/MoShiro.Agent/DataPlane/Registries.cs @@ -4,10 +4,10 @@ using System.Net.Sockets; using Fleet.Core; using Fleet.Link; using Fleet.Rpc; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// 进程内计数(只有计数,没有主机信息):用于心跳和面板上的节点状态。数据面的包计数由共享的 提供。 public sealed class AgentCounters diff --git a/backend/agent/src/Monitor.Agent/Host/HostSampler.cs b/backend/agent/src/MoShiro.Agent/Host/HostSampler.cs similarity index 99% rename from backend/agent/src/Monitor.Agent/Host/HostSampler.cs rename to backend/agent/src/MoShiro.Agent/Host/HostSampler.cs index b24d775..05e1791 100644 --- a/backend/agent/src/Monitor.Agent/Host/HostSampler.cs +++ b/backend/agent/src/MoShiro.Agent/Host/HostSampler.cs @@ -1,7 +1,7 @@ using System.Globalization; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// /// 主机资源占用的只读快照:CPU 占用、内存、根分区、网卡累计流量与瞬时网速、系统运行时长。 diff --git a/backend/probe/src/Monitor.Probe/Monitor.Probe.csproj b/backend/agent/src/MoShiro.Agent/MoShiro.Agent.csproj similarity index 78% rename from backend/probe/src/Monitor.Probe/Monitor.Probe.csproj rename to backend/agent/src/MoShiro.Agent/MoShiro.Agent.csproj index 6a2dca7..7896c09 100644 --- a/backend/probe/src/Monitor.Probe/Monitor.Probe.csproj +++ b/backend/agent/src/MoShiro.Agent/MoShiro.Agent.csproj @@ -4,8 +4,8 @@ Condition="'$(UseAotAnywhere)' == 'true'" /> Exe - monitor-probe - Monitor.Probe + moshiro-agent + MoShiro.Agent true true true @@ -15,10 +15,10 @@ false - + - + Agent → Backend 的上行:事件和测试状态是尽力而为的通知(链路断开时丢弃;列块走发件箱,不会丢)。 public sealed class BackendUplink @@ -31,7 +31,7 @@ public sealed class BackendUplink } var parameters = new AgentEventParams { Kind = kind, AtUtcMs = _time.GetUtcNow().ToUnixTimeMilliseconds(), Detail = detail, Probe = probe }; - await SafeAsync(() => session.NotifyAsync(MonitorMethods.Event, parameters, MonitorJsonContext.Default.AgentEventParams, CancellationToken.None)); + await SafeAsync(() => session.NotifyAsync(MoShiroMethods.Event, parameters, MoShiroJsonContext.Default.AgentEventParams, CancellationToken.None)); } public async ValueTask SendHostAsync(HostSnapshot snapshot) @@ -42,7 +42,7 @@ public sealed class BackendUplink return; } - await SafeAsync(() => session.NotifyAsync(MonitorMethods.Host, snapshot, MonitorJsonContext.Default.HostSnapshot, CancellationToken.None)); + await SafeAsync(() => session.NotifyAsync(MoShiroMethods.Host, snapshot, MoShiroJsonContext.Default.HostSnapshot, CancellationToken.None)); } public async ValueTask SendTestStatusAsync(TestId test, string status, int? phase = null, string? errorCode = null) @@ -54,7 +54,7 @@ public sealed class BackendUplink } var parameters = new TestStatusParams { TestId = test, Status = status, Phase = phase, ErrorCode = errorCode }; - await SafeAsync(() => session.NotifyAsync(MonitorMethods.TestStatus, parameters, MonitorJsonContext.Default.TestStatusParams, CancellationToken.None)); + await SafeAsync(() => session.NotifyAsync(MoShiroMethods.TestStatus, parameters, MoShiroJsonContext.Default.TestStatusParams, CancellationToken.None)); } private static async ValueTask SafeAsync(Func send) @@ -115,7 +115,7 @@ public sealed class PeerHub var order = new Queue(); rpc.OnBinary = async (frame, token) => { - if (!OutboxFrame.TryParse(frame, out byte channel, out Guid id, out ReadOnlyMemory payload) || channel != MonitorChannels.ColumnBlock) + if (!OutboxFrame.TryParse(frame, out byte channel, out Guid id, out ReadOnlyMemory payload) || channel != MoShiroChannels.ColumnBlock) { return; } @@ -251,7 +251,7 @@ public sealed class PeerHub { Type = LineTaskSet.StateType, Version = set.Version, - Doc = JsonSerializer.SerializeToElement(set, MonitorJsonContext.Default.LineTaskSet), + Doc = JsonSerializer.SerializeToElement(set, MoShiroJsonContext.Default.LineTaskSet), }; try { @@ -270,8 +270,8 @@ public sealed class PeerHub private static RpcRouter BuildRouter() => new RpcRouter() .On( - MonitorMethods.Clock, - MonitorJsonContext.Default.ClockParams, - MonitorJsonContext.Default.ClockResult, + MoShiroMethods.Clock, + MoShiroJsonContext.Default.ClockParams, + MoShiroJsonContext.Default.ClockResult, (context, _, _) => ValueTask.FromResult(Result.Ok(new ClockResult { AgentMonoUs = context.Clock.NowUs() }))); } diff --git a/backend/agent/src/Monitor.Agent/Program.cs b/backend/agent/src/MoShiro.Agent/Program.cs similarity index 93% rename from backend/agent/src/Monitor.Agent/Program.cs rename to backend/agent/src/MoShiro.Agent/Program.cs index fd273c2..096bf85 100644 --- a/backend/agent/src/Monitor.Agent/Program.cs +++ b/backend/agent/src/MoShiro.Agent/Program.cs @@ -1,6 +1,6 @@ using Fleet.Core; using Fleet.Hosting; -using Monitor.Agent; +using MoShiro.Agent; TimeProvider time = TimeProvider.System; INodeLog log = new ConsoleNodeLog(time); @@ -11,7 +11,7 @@ if (PlatformCheck.VerifyCrypto() is { } problem) return 1; } -string configPath = "/etc/monitor/agent.json"; +string configPath = "/etc/moshiro/agent.json"; for (int i = 0; i < args.Length - 1; i++) { if (args[i] == "--config") diff --git a/backend/agent/src/Monitor.Agent/Tasks/TargetRunners.cs b/backend/agent/src/MoShiro.Agent/Tasks/TargetRunners.cs similarity index 98% rename from backend/agent/src/Monitor.Agent/Tasks/TargetRunners.cs rename to backend/agent/src/MoShiro.Agent/Tasks/TargetRunners.cs index 667cab7..a5dbf29 100644 --- a/backend/agent/src/Monitor.Agent/Tasks/TargetRunners.cs +++ b/backend/agent/src/MoShiro.Agent/Tasks/TargetRunners.cs @@ -1,10 +1,10 @@ using System.Net; using System.Text.Json; using Fleet.Core; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// /// 目标任务(Agent 自己探测一个地址,没有对端):用和线路任务同一套积木( 里的拨号方,例如 icmp_echo、connect), diff --git a/backend/agent/src/Monitor.Agent/Tasks/TaskManager.cs b/backend/agent/src/MoShiro.Agent/Tasks/TaskManager.cs similarity index 99% rename from backend/agent/src/Monitor.Agent/Tasks/TaskManager.cs rename to backend/agent/src/MoShiro.Agent/Tasks/TaskManager.cs index 070ed17..a8254a0 100644 --- a/backend/agent/src/Monitor.Agent/Tasks/TaskManager.cs +++ b/backend/agent/src/MoShiro.Agent/Tasks/TaskManager.cs @@ -1,10 +1,10 @@ using System.Text.Json; using Fleet.Core; using Fleet.Rpc; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// /// 期望任务集(task_set)的调和器:每个任务先经过 (参数校验 + 本地硬上限)。 diff --git a/backend/agent/src/Monitor.Agent/Tests/TestRunner.cs b/backend/agent/src/MoShiro.Agent/Tests/TestRunner.cs similarity index 90% rename from backend/agent/src/Monitor.Agent/Tests/TestRunner.cs rename to backend/agent/src/MoShiro.Agent/Tests/TestRunner.cs index cfb7017..23c2ddc 100644 --- a/backend/agent/src/Monitor.Agent/Tests/TestRunner.cs +++ b/backend/agent/src/MoShiro.Agent/Tests/TestRunner.cs @@ -1,15 +1,15 @@ using System.Collections.Concurrent; using Fleet.Core; using Fleet.Rpc; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Agent; +namespace MoShiro.Agent; /// -/// 按需测试的编排(monitor.test.run):Agent 是时间的主人。每个阶段: -/// 登记数据面的流 → monitor.test.phase(Probe 准备好自己一侧的角色)→ 运行(单向数据流由 Agent 发包,回显类等待 Probe 发包) -/// → monitor.test.phase_end(Probe 收尾,回复它交出的列块个数)→ 等所有列块合并上传 → phase_done。 +/// 按需测试的编排(moshiro.test.run):Agent 是时间的主人。每个阶段: +/// 登记数据面的流 → moshiro.test.phase(Probe 准备好自己一侧的角色)→ 运行(单向数据流由 Agent 发包,回显类等待 Probe 发包) +/// → moshiro.test.phase_end(Probe 收尾,回复它交出的列块个数)→ 等所有列块合并上传 → phase_done。 /// 同一时间只能运行 max_concurrent_tests 个测试;Probe 掉线或被取消时测试中止,已经上传的阶段数据保留。 /// public sealed class TestRunner @@ -43,17 +43,17 @@ public sealed class TestRunner public int Running => _running.Count; - /// 校验并接受一个测试(立即返回),实际运行在后台进行,状态通过 monitor.test.status 回报。 + /// 校验并接受一个测试(立即返回),实际运行在后台进行,状态通过 moshiro.test.status 回报。 public Result Start(TestRunParams run) { if (_sessions.Get(run.ProbeId.Value) is not { } probe) { - return Result.Fail(MonitorErrors.ProbeOffline, $"probe {run.ProbeId.Value} is not connected to this agent"); + return Result.Fail(MoShiroErrors.ProbeOffline, $"probe {run.ProbeId.Value} is not connected to this agent"); } if (run.Phases.Count == 0) { - return Result.Fail(MonitorErrors.ValidationFailed, "a test needs at least one phase"); + return Result.Fail(MoShiroErrors.ValidationFailed, "a test needs at least one phase"); } var resolved = new List<(TestPhase Phase, TaskDecision Decision)>(); @@ -64,7 +64,7 @@ public sealed class TestRunner if (!decision.Accepted) { return Result.Fail( - decision.Reason == RejectReasons.Unsupported ? FleetErrors.Unsupported : MonitorErrors.BudgetExceeded, + decision.Reason == RejectReasons.Unsupported ? FleetErrors.Unsupported : MoShiroErrors.BudgetExceeded, $"phase {phase.Kind} refused: {decision.Reason}"); } @@ -84,7 +84,7 @@ public sealed class TestRunner if (_running.Count >= _maxConcurrent || !_running.TryAdd(run.TestId.Value, running)) { running.Dispose(); - return Result.Fail(MonitorErrors.TestBusy, "this agent is already running a test"); + return Result.Fail(MoShiroErrors.TestBusy, "this agent is already running a test"); } _ = Task.Run(() => RunAsync(run.TestId, resolved, running), CancellationToken.None); @@ -154,7 +154,7 @@ public sealed class TestRunner catch (OperationCanceledException) { finalStatus = running.Lost ? TestStatuses.Failed : TestStatuses.Aborted; - errorCode = running.Lost ? MonitorErrors.ProbeOffline.Value : null; + errorCode = running.Lost ? MoShiroErrors.ProbeOffline.Value : null; await TryAbortProbeAsync(running.Probe, test); } catch (TestFailure failure) @@ -183,7 +183,7 @@ public sealed class TestRunner _streams.Add(stream); try { - RpcSession rpc = probe.Rpc ?? throw new TestFailure(MonitorErrors.ProbeOffline.Value); + RpcSession rpc = probe.Rpc ?? throw new TestFailure(MoShiroErrors.ProbeOffline.Value); var start = new TestPhaseStartParams { TestId = test, @@ -194,7 +194,7 @@ public sealed class TestRunner DurationSeconds = durationSeconds, Port = stream.Port, }; - Result accepted = await rpc.RequestAsync(MonitorMethods.TestPhase, start, MonitorJsonContext.Default.TestPhaseStartParams, MonitorJsonContext.Default.TestPhaseStartResult, ct); + Result accepted = await rpc.RequestAsync(MoShiroMethods.TestPhase, start, MoShiroJsonContext.Default.TestPhaseStartParams, MoShiroJsonContext.Default.TestPhaseStartResult, ct); if (!accepted.TryGet(out TestPhaseStartResult? started, out FleetError? error) || !started.Accepted) { throw new TestFailure(error?.Code.Value ?? FleetErrors.Unsupported.Value); @@ -209,7 +209,7 @@ public sealed class TestRunner } catch (BlockFaultException) { - throw new TestFailure(MonitorErrors.ProbeOffline.Value); + throw new TestFailure(MoShiroErrors.ProbeOffline.Value); } } else @@ -219,7 +219,7 @@ public sealed class TestRunner long sent = stream.Running.Block.Produced; var end = new TestPhaseEndParams { TestId = test, Phase = index, StreamId = stream.Id, SentCount = sent }; - Result ended = await rpc.RequestAsync(MonitorMethods.TestPhaseEnd, end, MonitorJsonContext.Default.TestPhaseEndParams, MonitorJsonContext.Default.TestPhaseEndResult, ct, s_phaseSlack); + Result ended = await rpc.RequestAsync(MoShiroMethods.TestPhaseEnd, end, MoShiroJsonContext.Default.TestPhaseEndParams, MoShiroJsonContext.Default.TestPhaseEndResult, ct, s_phaseSlack); if (!ended.TryGet(out TestPhaseEndResult? finished, out FleetError? endError)) { throw new TestFailure(endError.Code.Value); @@ -254,7 +254,7 @@ public sealed class TestRunner try { - await rpc.NotifyAsync(MonitorMethods.TestAbort, new TestAbortParams { TestId = test }, MonitorJsonContext.Default.TestAbortParams, CancellationToken.None); + await rpc.NotifyAsync(MoShiroMethods.TestAbort, new TestAbortParams { TestId = test }, MoShiroJsonContext.Default.TestAbortParams, CancellationToken.None); } catch (Exception ex) when (ex is ObjectDisposedException or InvalidOperationException or IOException) { diff --git a/backend/fleet/src/Fleet.Core/StringId.cs b/backend/fleet/src/Fleet.Core/StringId.cs index 87f4058..71371bb 100644 --- a/backend/fleet/src/Fleet.Core/StringId.cs +++ b/backend/fleet/src/Fleet.Core/StringId.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; namespace Fleet.Core; -/// 节点 ID(带类型前缀的 ULID)。Monitor 中的 AgentId 就是 NodeId。 +/// 节点 ID(带类型前缀的 ULID)。MoShiro 中的 AgentId 就是 NodeId。 [JsonConverter(typeof(NodeIdJsonConverter))] public readonly record struct NodeId(string Value) { diff --git a/backend/fleet/src/Fleet.Link/FleetLink.cs b/backend/fleet/src/Fleet.Link/FleetLink.cs index 0fdcc5f..5d86ded 100644 --- a/backend/fleet/src/Fleet.Link/FleetLink.cs +++ b/backend/fleet/src/Fleet.Link/FleetLink.cs @@ -49,7 +49,7 @@ public sealed class FleetLinkOptions public string NodeVersion { get; init; } = "0.0.0"; - /// 框架之外的能力,例如 monitor.v1 与 monitor.kinds:udp_echo,tcp_echo。 + /// 框架之外的能力,例如 moshiro.v1 与 moshiro.kinds:udp_echo,tcp_echo。 public IReadOnlyList Capabilities { get; init; } = []; /// 附加到 fleet.hello 的产品元数据。 diff --git a/backend/fleet/src/Fleet.Rpc/FleetMethods.cs b/backend/fleet/src/Fleet.Rpc/FleetMethods.cs index 9053db9..6062ead 100644 --- a/backend/fleet/src/Fleet.Rpc/FleetMethods.cs +++ b/backend/fleet/src/Fleet.Rpc/FleetMethods.cs @@ -58,7 +58,7 @@ public sealed class HelloParams [JsonPropertyName("now_utc_ms")] public long NowUtcMs { get; set; } - /// 产品自定义的元数据(例如 Monitor 的 tls_spki、probe_ports、local_limits 摘要)。 + /// 产品自定义的元数据(例如 MoShiro 的 tls_spki、probe_ports、local_limits 摘要)。 [JsonPropertyName("meta")] public JsonElement? Meta { get; set; } } diff --git a/backend/fleet/src/Fleet.Server.Abstractions/FleetServerOptions.cs b/backend/fleet/src/Fleet.Server.Abstractions/FleetServerOptions.cs index fd99ac7..0a581bf 100644 --- a/backend/fleet/src/Fleet.Server.Abstractions/FleetServerOptions.cs +++ b/backend/fleet/src/Fleet.Server.Abstractions/FleetServerOptions.cs @@ -2,7 +2,7 @@ namespace Fleet.Server; public sealed class FleetServerOptions { - /// 节点 ID 的类型前缀,Monitor 使用 agt。 + /// 节点 ID 的类型前缀,MoShiro 使用 agt。 public string NodeIdPrefix { get; set; } = "node"; public int MaxFrameBytes { get; set; } = 16 << 20; diff --git a/backend/fleet/src/Fleet.Server.Abstractions/IFleetStore.cs b/backend/fleet/src/Fleet.Server.Abstractions/IFleetStore.cs index ed00273..e4d7af0 100644 --- a/backend/fleet/src/Fleet.Server.Abstractions/IFleetStore.cs +++ b/backend/fleet/src/Fleet.Server.Abstractions/IFleetStore.cs @@ -2,7 +2,7 @@ using Fleet.Core; namespace Fleet.Server; -/// Fleet 服务端需要的存储。产品可以自己实现(Monitor 把它放进 monitor.db)。 +/// Fleet 服务端需要的存储。产品可以自己实现(MoShiro 把它放进 moshiro.db)。 public interface IFleetStore { ValueTask FindJoinTokenAsync(ReadOnlyMemory tokenSha256, CancellationToken ct); diff --git a/backend/fleet/src/Fleet.Server.Link/FleetServerBuilder.cs b/backend/fleet/src/Fleet.Server.Link/FleetServerBuilder.cs index 7141df9..755deb2 100644 --- a/backend/fleet/src/Fleet.Server.Link/FleetServerBuilder.cs +++ b/backend/fleet/src/Fleet.Server.Link/FleetServerBuilder.cs @@ -50,7 +50,7 @@ public sealed class FleetServerBuilder return this; } - /// 注册产品的 RPC 方法(monitor.* 等)。处理函数通过 在调用时解析服务,避免构造期的循环依赖。 + /// 注册产品的 RPC 方法(moshiro.* 等)。处理函数通过 在调用时解析服务,避免构造期的循环依赖。 public FleetServerBuilder MapMethods(Action, IServiceProvider> configure) { _routeConfigurators.Add(configure); diff --git a/backend/fleet/tools/fleet-enroll/Program.cs b/backend/fleet/tools/fleet-enroll/Program.cs index 2ede74b..3c31989 100644 --- a/backend/fleet/tools/fleet-enroll/Program.cs +++ b/backend/fleet/tools/fleet-enroll/Program.cs @@ -2,7 +2,7 @@ using System.Text.Json.Nodes; using Fleet.Core; using Fleet.Enrollment.Client; -// fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/monitor [--meta '{"probe_tcp_port":7443,...}'] [--version 1.0] +// fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/moshiro [--meta '{"probe_tcp_port":7443,...}'] [--version 1.0] // 一次性运行:生成身份、注册、写入身份目录,然后退出。 string? Arg(string name) => args.SkipWhile(a => a != name).Skip(1).FirstOrDefault(); diff --git a/backend/openapi/v1.json b/backend/openapi/v1.json index bc9085e..fb33894 100644 --- a/backend/openapi/v1.json +++ b/backend/openapi/v1.json @@ -1,7 +1,7 @@ { "openapi": "3.1.1", "info": { - "title": "monitor-backend | v1", + "title": "moshiro-backend | v1", "version": "1.0.0" }, "paths": { @@ -3168,7 +3168,7 @@ "tags": [ "admin-data" ], - "summary": "Download a raw chunk (application/vnd.monitor.rawchunk)", + "summary": "Download a raw chunk (application/vnd.moshiro.rawchunk)", "operationId": "raw_chunks_get", "parameters": [ { diff --git a/backend/probe/README.md b/backend/probe/README.md index 6e764cc..76db344 100644 --- a/backend/probe/README.md +++ b/backend/probe/README.md @@ -1,4 +1,4 @@ -# Monitor Probe +# MoShiro Probe Probe 是网络测量的发起端,通常运行在家宽或其他需要观测的网络出口。它连接 Backend(身份、Agent 列表、状态)和 Agent(测量),执行线路任务,并把本端采集的原始数据交给 Agent 汇总上报。Probe 不提供远程命令或文件管理功能。 @@ -13,7 +13,7 @@ Backend ──任务──> Agent <──测量会话──> Probe Probe 使用面板生成的 `probe.json` 读取自身身份、面板地址(`server`)和本地测试限制。连上面板之后,面板下发要连接的 Agent 列表;Probe 将列表缓存到同目录的 `agents.json`,面板暂时连不上时靠它继续测量。`probe.json` 含私钥,请限制文件访问权限,不要提交到版本控制。 -容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md);Probe 也可以用 OCI 镜像 `registry.oeo.one/monitor/monitor-probe:`,容器默认读取 `/etc/monitor/probe.json`,命名 volume 保存身份配置和缓存,镜像以 UID/GID 1654 运行。 +容器节点的运行方式(podman + systemd)和宿主机更新器见 [`../../docs/deployment.md`](../../docs/deployment.md);Probe 也可以用 OCI 镜像 `registry.oeo.one/moshiro/moshiro-probe:`,容器默认读取 `/etc/moshiro/probe.json`,命名 volume 保存身份配置和缓存,镜像以 UID/GID 1654 运行。 ## 构建 @@ -21,33 +21,33 @@ Probe 使用面板生成的 `probe.json` 读取自身身份、面板地址(`se ```sh cd backend -dotnet build probe/src/Monitor.Probe -c Release +dotnet build probe/src/MoShiro.Probe -c Release ``` 发布 NativeAOT 程序需要对应平台的 C/C++ 工具链;`` 应与构建环境和目标平台匹配: ```sh -dotnet publish probe/src/Monitor.Probe -c Release -r -p:PublishAot=true +dotnet publish probe/src/MoShiro.Probe -c Release -r -p:PublishAot=true ``` 也可在仓库根目录运行 `bash scripts/publish.sh probe `,再使用 `scripts/package.sh probe ` 生成部署包。支持平台和安装流程见 [`../../docs/deployment.md`](../../docs/deployment.md)。 ## 配置与运行 -在管理端创建 Probe 并下载一次性身份配置,将文件保存为目标机上的 `/etc/monitor/probe.json`。随后运行: +在管理端创建 Probe 并下载一次性身份配置,将文件保存为目标机上的 `/etc/moshiro/probe.json`。随后运行: ```sh -monitor-probe --config /etc/monitor/probe.json +moshiro-probe --config /etc/moshiro/probe.json ``` -不传 `--config` 时默认读取 `/etc/monitor/probe.json`。配置中的 `limits` 是本地硬上限,Backend 无法远程放宽;路由器等低性能设备可调低测试码率和包速率,避免 Probe 自身成为测量瓶颈。部署时可使用仓库的 `packaging/` 文件和安装脚本。 +不传 `--config` 时默认读取 `/etc/moshiro/probe.json`。配置中的 `limits` 是本地硬上限,Backend 无法远程放宽;路由器等低性能设备可调低测试码率和包速率,避免 Probe 自身成为测量瓶颈。部署时可使用仓库的 `packaging/` 文件和安装脚本。 -也可使用 OCI 镜像 `registry.oeo.one/monitor/monitor-probe:`,自动选择 Linux amd64 或 arm64 镜像。容器默认读取 `/etc/monitor/probe.json`,并在同一目录维护 `agents.json` 缓存,因此挂载目录必须允许 UID/GID 1654 读写。建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。 +也可使用 OCI 镜像 `registry.oeo.one/moshiro/moshiro-probe:`,自动选择 Linux amd64 或 arm64 镜像。容器默认读取 `/etc/moshiro/probe.json`,并在同一目录维护 `agents.json` 缓存,因此挂载目录必须允许 UID/GID 1654 读写。建议使用宿主网络,并按 ICMP 测量需求添加 `NET_RAW` 能力。 ## 设计说明 - Probe 只执行编译进程序的测量模块。它不落盘测量数据,也不能执行命令或自行更新(更新由宿主机上的更新器完成,Probe 只写清单和请求文件)。 -- 测量模块由 `backend/shared/Monitor.Measurement/` 与 Agent 共用;Probe 负责发起连接、调度任务和记录本端数据。 +- 测量模块由 `backend/shared/MoShiro.Measurement/` 与 Agent 共用;Probe 负责发起连接、调度任务和记录本端数据。 - 测量模型和协议细节见 [`../../docs/measurement.md`](../../docs/measurement.md) 与 [`../../docs/protocol.md`](../../docs/protocol.md);整体架构见 [`../../docs/architecture.md`](../../docs/architecture.md)。 Docker 与 rootless/rootful Podman 共用 `packaging/compose/probe/compose.yaml`,配置、身份和缓存放在命名 volume 中,无需宿主配置目录、keep-id 或手动 chown。首次导入和自动更新见部署文档。 diff --git a/backend/probe/src/Monitor.Probe/Link/AgentConnection.cs b/backend/probe/src/MoShiro.Probe/Link/AgentConnection.cs similarity index 95% rename from backend/probe/src/Monitor.Probe/Link/AgentConnection.cs rename to backend/probe/src/MoShiro.Probe/Link/AgentConnection.cs index f01ad3d..1a2e621 100644 --- a/backend/probe/src/Monitor.Probe/Link/AgentConnection.cs +++ b/backend/probe/src/MoShiro.Probe/Link/AgentConnection.cs @@ -6,10 +6,10 @@ using Fleet.Core; using Fleet.Identity; using Fleet.Link; using Fleet.Rpc; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Probe; +namespace MoShiro.Probe; /// 所有连接共用的东西。 public sealed record ConnectionEnv(NodeIdentity Identity, string ProbeId, ProbeLimits Limits, AgentDirectory Directory, MonoClock Clock, TimeProvider Time, INodeLog Log); @@ -154,7 +154,7 @@ public sealed class AgentConnection for (int i = 0; i < rounds; i++) { long sent = _env.Clock.NowUs(); - Result reply = await rpc.RequestAsync(MonitorMethods.Clock, new ClockParams { ProbeMonoUs = sent }, MonitorJsonContext.Default.ClockParams, MonitorJsonContext.Default.ClockResult, ct); + Result reply = await rpc.RequestAsync(MoShiroMethods.Clock, new ClockParams { ProbeMonoUs = sent }, MoShiroJsonContext.Default.ClockParams, MoShiroJsonContext.Default.ClockResult, ct); if (reply.TryGet(out ClockResult? value, out _)) { clock.Add(sent, value.AgentMonoUs, _env.Clock.NowUs()); @@ -189,7 +189,7 @@ public sealed class AgentConnection var result = new StateSyncResult { AppliedVersion = sync.Version }; if (sync.Type == LineTaskSet.StateType) { - LineTaskSet? set = sync.Doc.Deserialize(MonitorJsonContext.Default.LineTaskSet); + LineTaskSet? set = sync.Doc.Deserialize(MoShiroJsonContext.Default.LineTaskSet); if (set is null) { return Result.Fail(FleetErrors.InvalidParams, "line_task_set could not be parsed"); @@ -201,18 +201,18 @@ public sealed class AgentConnection return Result.Ok(result); }) .On( - MonitorMethods.TestPhase, - MonitorJsonContext.Default.TestPhaseStartParams, - MonitorJsonContext.Default.TestPhaseStartResult, + MoShiroMethods.TestPhase, + MoShiroJsonContext.Default.TestPhaseStartParams, + MoShiroJsonContext.Default.TestPhaseStartResult, (session, start, _) => ValueTask.FromResult(Result.Ok(new TestPhaseStartResult { Accepted = session.StartPhase(start) }))) .On( - MonitorMethods.TestPhaseEnd, - MonitorJsonContext.Default.TestPhaseEndParams, - MonitorJsonContext.Default.TestPhaseEndResult, + MoShiroMethods.TestPhaseEnd, + MoShiroJsonContext.Default.TestPhaseEndParams, + MoShiroJsonContext.Default.TestPhaseEndResult, async (session, end, ct) => Result.Ok(new TestPhaseEndResult { Fragments = await session.EndPhaseAsync(end, ct) })) .OnNotify( - MonitorMethods.TestAbort, - MonitorJsonContext.Default.TestAbortParams, + MoShiroMethods.TestAbort, + MoShiroJsonContext.Default.TestAbortParams, async (session, abort, _) => await session.AbortAsync(abort.TestId)) .OnNotify( FleetMethods.OutboxAck, diff --git a/backend/probe/src/Monitor.Probe/Link/AgentDirectory.cs b/backend/probe/src/MoShiro.Probe/Link/AgentDirectory.cs similarity index 98% rename from backend/probe/src/Monitor.Probe/Link/AgentDirectory.cs rename to backend/probe/src/MoShiro.Probe/Link/AgentDirectory.cs index 4c74202..4a59d2c 100644 --- a/backend/probe/src/Monitor.Probe/Link/AgentDirectory.cs +++ b/backend/probe/src/MoShiro.Probe/Link/AgentDirectory.cs @@ -1,9 +1,9 @@ using System.Text.Json; using Fleet.Core; using Fleet.Hosting; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Probe; +namespace MoShiro.Probe; /// /// Probe 要连接的 Agent 列表:由面板作为期望状态(probe_agents)直接下发;面板连不上时用上一次保存的 agents.json。 diff --git a/backend/probe/src/Monitor.Probe/Link/Support.cs b/backend/probe/src/MoShiro.Probe/Link/Support.cs similarity index 93% rename from backend/probe/src/Monitor.Probe/Link/Support.cs rename to backend/probe/src/MoShiro.Probe/Link/Support.cs index e593ef6..253be3a 100644 --- a/backend/probe/src/Monitor.Probe/Link/Support.cs +++ b/backend/probe/src/MoShiro.Probe/Link/Support.cs @@ -1,13 +1,13 @@ using System.Text.Json; using Fleet.Core; using Fleet.Link; -using Monitor.Measurement; -using Monitor.Protocol; +using MoShiro.Measurement; +using MoShiro.Protocol; -namespace Monitor.Probe; +namespace MoShiro.Probe; /// -/// 估计 Agent 单调时钟 − Probe 单调时钟:每次 monitor.clock 往返取中点,在最近 8 次里取往返最短的一次 +/// 估计 Agent 单调时钟 − Probe 单调时钟:每次 moshiro.clock 往返取中点,在最近 8 次里取往返最短的一次 /// (往返越短,对称性假设的误差越小)。Probe 的列块头里带着这个偏移,Backend 据此对齐两端的时间戳。 /// public sealed class ClockEstimator @@ -82,6 +82,6 @@ public sealed class FragmentShipper : IFragmentSink } } - _outbox.Enqueue(MonitorChannels.ColumnBlock, header.ChunkId.Value, RawChunkCodec.Encode(new RawChunk(header, streams))); + _outbox.Enqueue(MoShiroChannels.ColumnBlock, header.ChunkId.Value, RawChunkCodec.Encode(new RawChunk(header, streams))); } } diff --git a/backend/agent/src/Monitor.Agent/Monitor.Agent.csproj b/backend/probe/src/MoShiro.Probe/MoShiro.Probe.csproj similarity index 78% rename from backend/agent/src/Monitor.Agent/Monitor.Agent.csproj rename to backend/probe/src/MoShiro.Probe/MoShiro.Probe.csproj index f25af4d..54d4202 100644 --- a/backend/agent/src/Monitor.Agent/Monitor.Agent.csproj +++ b/backend/probe/src/MoShiro.Probe/MoShiro.Probe.csproj @@ -4,8 +4,8 @@ Condition="'$(UseAotAnywhere)' == 'true'" /> Exe - monitor-agent - Monitor.Agent + moshiro-probe + MoShiro.Probe true true true @@ -15,10 +15,10 @@ false - + - + /// Probe 的组装点:到面板的长连接(身份、Agent 列表、状态)+ 每个 Agent 一个测量连接(认证、重连、角色)。没有全局状态,测试可以在同一进程里启动多个。 @@ -15,7 +15,7 @@ namespace Monitor.Probe; /// public sealed class ProbeApp : IDisposable { - public const string Version = Monitor.Protocol.BuildInfo.Version; + public const string Version = MoShiro.Protocol.BuildInfo.Version; private readonly ConnectionEnv _env; private readonly Dictionary _connections = []; @@ -38,25 +38,25 @@ public sealed class ProbeApp : IDisposable Identity = identity, NodeId = new NodeId(file.ProbeId), NodeVersion = Version, - Capabilities = [MonitorCapabilities.Base], - HeartbeatExtra = () => JsonSerializer.SerializeToElement(new ProbeHeartbeatExtra { Update = update.ReadStatus() }, MonitorJsonContext.Default.ProbeHeartbeatExtra), - HelloMeta = () => JsonSerializer.SerializeToElement(new ProbeHelloMeta { Platform = PlatformDetector.Detect() }, MonitorJsonContext.Default.ProbeHelloMeta), + Capabilities = [MoShiroCapabilities.Base], + HeartbeatExtra = () => JsonSerializer.SerializeToElement(new ProbeHeartbeatExtra { Update = update.ReadStatus() }, MoShiroJsonContext.Default.ProbeHeartbeatExtra), + HelloMeta = () => JsonSerializer.SerializeToElement(new ProbeHelloMeta { Platform = PlatformDetector.Detect() }, MoShiroJsonContext.Default.ProbeHelloMeta), }; var list = new StateHandler( ProbeAgentList.StateType, - MonitorJsonContext.Default.ProbeAgentList, + MoShiroJsonContext.Default.ProbeAgentList, (version, doc, _) => { directory.Apply(doc); return ValueTask.FromResult(new StateSyncResult { AppliedVersion = version }); }); var router = new RpcRouter() - .OnNotify(MonitorMethods.UpdateOffer, MonitorJsonContext.Default.UpdateOfferParams, (_, offer, _) => + .OnNotify(MoShiroMethods.UpdateOffer, MoShiroJsonContext.Default.UpdateOfferParams, (_, offer, _) => { update.WriteOffer(offer); return ValueTask.CompletedTask; }) - .OnNotify(MonitorMethods.UpdateApply, MonitorJsonContext.Default.UpdateOfferParams, (_, _, _) => + .OnNotify(MoShiroMethods.UpdateApply, MoShiroJsonContext.Default.UpdateOfferParams, (_, _, _) => { update.RequestApply(); return ValueTask.CompletedTask; diff --git a/backend/probe/src/Monitor.Probe/ProbeConfig.cs b/backend/probe/src/MoShiro.Probe/ProbeConfig.cs similarity index 98% rename from backend/probe/src/Monitor.Probe/ProbeConfig.cs rename to backend/probe/src/MoShiro.Probe/ProbeConfig.cs index bcaf11e..5bb9aa8 100644 --- a/backend/probe/src/Monitor.Probe/ProbeConfig.cs +++ b/backend/probe/src/MoShiro.Probe/ProbeConfig.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Monitor.Probe; +namespace MoShiro.Probe; /// /// probe.json:面板 POST /api/admin/probe/identity 生成的文件(只能下载一次)。 diff --git a/backend/probe/src/Monitor.Probe/Program.cs b/backend/probe/src/MoShiro.Probe/Program.cs similarity index 95% rename from backend/probe/src/Monitor.Probe/Program.cs rename to backend/probe/src/MoShiro.Probe/Program.cs index 2a025ac..cadfb11 100644 --- a/backend/probe/src/Monitor.Probe/Program.cs +++ b/backend/probe/src/MoShiro.Probe/Program.cs @@ -1,6 +1,6 @@ using Fleet.Core; using Fleet.Hosting; -using Monitor.Probe; +using MoShiro.Probe; TimeProvider time = TimeProvider.System; INodeLog log = new ConsoleNodeLog(time); @@ -11,7 +11,7 @@ if (PlatformCheck.VerifyCrypto() is { } problem) return 1; } -string configPath = "/etc/monitor/probe.json"; +string configPath = "/etc/moshiro/probe.json"; for (int i = 0; i < args.Length - 1; i++) { if (args[i] == "--config") diff --git a/backend/probe/tests/Monitor.Probe.Tests/Monitor.Probe.Tests.csproj b/backend/probe/tests/MoShiro.Probe.Tests/MoShiro.Probe.Tests.csproj similarity index 82% rename from backend/probe/tests/Monitor.Probe.Tests/Monitor.Probe.Tests.csproj rename to backend/probe/tests/MoShiro.Probe.Tests/MoShiro.Probe.Tests.csproj index 1367d6a..d27508f 100644 --- a/backend/probe/tests/Monitor.Probe.Tests/Monitor.Probe.Tests.csproj +++ b/backend/probe/tests/MoShiro.Probe.Tests/MoShiro.Probe.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/backend/probe/tests/Monitor.Probe.Tests/ProbeTests.cs b/backend/probe/tests/MoShiro.Probe.Tests/ProbeTests.cs similarity index 98% rename from backend/probe/tests/Monitor.Probe.Tests/ProbeTests.cs rename to backend/probe/tests/MoShiro.Probe.Tests/ProbeTests.cs index 89d3f20..d8bfa02 100644 --- a/backend/probe/tests/Monitor.Probe.Tests/ProbeTests.cs +++ b/backend/probe/tests/MoShiro.Probe.Tests/ProbeTests.cs @@ -1,8 +1,8 @@ using Fleet.Core; using Fleet.Hosting; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Probe.Tests; +namespace MoShiro.Probe.Tests; public sealed class ProbeTests { diff --git a/backend/shared/Monitor.Measurement/Blocks/BlockBase.cs b/backend/shared/MoShiro.Measurement/Blocks/BlockBase.cs similarity index 98% rename from backend/shared/Monitor.Measurement/Blocks/BlockBase.cs rename to backend/shared/MoShiro.Measurement/Blocks/BlockBase.cs index 8463d00..afa65cf 100644 --- a/backend/shared/Monitor.Measurement/Blocks/BlockBase.cs +++ b/backend/shared/MoShiro.Measurement/Blocks/BlockBase.cs @@ -1,7 +1,7 @@ using System.Net; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 积木的公共部分:环境、参数、把行切成列块交出去。 public abstract class BlockBase : IBlock diff --git a/backend/shared/Monitor.Measurement/Blocks/BlockCatalog.cs b/backend/shared/MoShiro.Measurement/Blocks/BlockCatalog.cs similarity index 97% rename from backend/shared/Monitor.Measurement/Blocks/BlockCatalog.cs rename to backend/shared/MoShiro.Measurement/Blocks/BlockCatalog.cs index 3970f2b..43465ab 100644 --- a/backend/shared/Monitor.Measurement/Blocks/BlockCatalog.cs +++ b/backend/shared/MoShiro.Measurement/Blocks/BlockCatalog.cs @@ -1,6 +1,6 @@ -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// /// 一种采集方法(Kind)的两个角色:拨号方(Probe)和监听方(Agent),以及它能不能作为目标任务(Agent 自己探测一个地址,没有对端)运行。 diff --git a/backend/shared/Monitor.Measurement/Blocks/Bulk.cs b/backend/shared/MoShiro.Measurement/Blocks/Bulk.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Blocks/Bulk.cs rename to backend/shared/MoShiro.Measurement/Blocks/Bulk.cs index 5fc0591..cc08f70 100644 --- a/backend/shared/Monitor.Measurement/Blocks/Bulk.cs +++ b/backend/shared/MoShiro.Measurement/Blocks/Bulk.cs @@ -1,7 +1,7 @@ using System.Net.Sockets; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 批量传输的收发循环(两端共用)。 internal static class BulkIo diff --git a/backend/shared/Monitor.Measurement/Blocks/Echo.cs b/backend/shared/MoShiro.Measurement/Blocks/Echo.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Blocks/Echo.cs rename to backend/shared/MoShiro.Measurement/Blocks/Echo.cs index 94c51b8..ef639db 100644 --- a/backend/shared/Monitor.Measurement/Blocks/Echo.cs +++ b/backend/shared/MoShiro.Measurement/Blocks/Echo.cs @@ -1,8 +1,8 @@ using System.Net; using System.Net.Sockets; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// /// 回显积木的行表(拨号方,行的主导方):发出时记 size、t1,回复到达时按序号回填 t4,到点把已经稳定的行切成列块。 diff --git a/backend/shared/Monitor.Measurement/Blocks/Session.cs b/backend/shared/MoShiro.Measurement/Blocks/Session.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Blocks/Session.cs rename to backend/shared/MoShiro.Measurement/Blocks/Session.cs index 0e52380..18b96d8 100644 --- a/backend/shared/Monitor.Measurement/Blocks/Session.cs +++ b/backend/shared/MoShiro.Measurement/Blocks/Session.cs @@ -1,9 +1,9 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Security.Authentication; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 什么也不做的监听方:connect 的另一端就是 TCP 监听器本身(接受连接、认证失败就关闭),不需要积木,但需要占一个流编号。 public sealed class IdleListener : BlockBase diff --git a/backend/shared/Monitor.Measurement/Blocks/Train.cs b/backend/shared/MoShiro.Measurement/Blocks/Train.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Blocks/Train.cs rename to backend/shared/MoShiro.Measurement/Blocks/Train.cs index 33a1117..7cfadac 100644 --- a/backend/shared/Monitor.Measurement/Blocks/Train.cs +++ b/backend/shared/MoShiro.Measurement/Blocks/Train.cs @@ -1,8 +1,8 @@ using System.Diagnostics; using System.Net; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// /// 单向数据流(UDP train),监听方(Agent,行的主导方):按声明的码率均匀发包,发出的每个包 (seq, size, t_send) 记在按序号的记录里, diff --git a/backend/shared/Monitor.Measurement/Core/Contracts.cs b/backend/shared/MoShiro.Measurement/Core/Contracts.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Core/Contracts.cs rename to backend/shared/MoShiro.Measurement/Core/Contracts.cs index 9919324..1dea055 100644 --- a/backend/shared/Monitor.Measurement/Core/Contracts.cs +++ b/backend/shared/MoShiro.Measurement/Core/Contracts.cs @@ -1,9 +1,9 @@ using System.Net; using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// /// 数据面会话:session_id 和密钥(由点对点握手里的 ECDH 派生)。两端的数据面包都用它认证。 diff --git a/backend/shared/Monitor.Measurement/Core/MonoClock.cs b/backend/shared/MoShiro.Measurement/Core/MonoClock.cs similarity index 97% rename from backend/shared/Monitor.Measurement/Core/MonoClock.cs rename to backend/shared/MoShiro.Measurement/Core/MonoClock.cs index ca52868..a316445 100644 --- a/backend/shared/Monitor.Measurement/Core/MonoClock.cs +++ b/backend/shared/MoShiro.Measurement/Core/MonoClock.cs @@ -1,4 +1,4 @@ -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 节点的单调时钟(微秒):t1…t4、t_send、t_recv 都用它;Probe 和 Agent 各自一个,块头里的时钟锚点把它对应到 UTC。 public sealed class MonoClock diff --git a/backend/shared/Monitor.Measurement/Core/RowStore.cs b/backend/shared/MoShiro.Measurement/Core/RowStore.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Core/RowStore.cs rename to backend/shared/MoShiro.Measurement/Core/RowStore.cs index 9160195..77845b2 100644 --- a/backend/shared/Monitor.Measurement/Core/RowStore.cs +++ b/backend/shared/MoShiro.Measurement/Core/RowStore.cs @@ -1,6 +1,6 @@ -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// /// 一侧记录的行:只含这一侧拥有的列(见 ),按连接键(通常是 seq)索引的环形缓冲, diff --git a/backend/shared/Monitor.Measurement/Core/RunningBlock.cs b/backend/shared/MoShiro.Measurement/Core/RunningBlock.cs similarity index 98% rename from backend/shared/Monitor.Measurement/Core/RunningBlock.cs rename to backend/shared/MoShiro.Measurement/Core/RunningBlock.cs index c246ba5..1ba7e7b 100644 --- a/backend/shared/Monitor.Measurement/Core/RunningBlock.cs +++ b/backend/shared/MoShiro.Measurement/Core/RunningBlock.cs @@ -1,4 +1,4 @@ -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 一个运行中的积木:自己的取消令牌和后台任务。Probe 和 Agent 共用。 public sealed class RunningBlock : IDisposable diff --git a/backend/shared/Monitor.Measurement/Execution/BudgetGuard.cs b/backend/shared/MoShiro.Measurement/Execution/BudgetGuard.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Execution/BudgetGuard.cs rename to backend/shared/MoShiro.Measurement/Execution/BudgetGuard.cs index f01e5e5..add768e 100644 --- a/backend/shared/Monitor.Measurement/Execution/BudgetGuard.cs +++ b/backend/shared/MoShiro.Measurement/Execution/BudgetGuard.cs @@ -1,8 +1,8 @@ using System.Net; using System.Text.Json; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 节点的本地硬上限(写在本机配置里,Backend 无法修改)。 public sealed class NodeLimits diff --git a/backend/shared/Monitor.Measurement/Merging/ChunkFinalizer.cs b/backend/shared/MoShiro.Measurement/Merging/ChunkFinalizer.cs similarity index 98% rename from backend/shared/Monitor.Measurement/Merging/ChunkFinalizer.cs rename to backend/shared/MoShiro.Measurement/Merging/ChunkFinalizer.cs index c91de05..53379cb 100644 --- a/backend/shared/Monitor.Measurement/Merging/ChunkFinalizer.cs +++ b/backend/shared/MoShiro.Measurement/Merging/ChunkFinalizer.cs @@ -1,8 +1,8 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// Agent 单调时钟(微秒)与 UTC(毫秒)的对应关系:块头里的时钟锚点。 public readonly record struct ClockAnchor(long AgentMonoUs, long UtcMs); diff --git a/backend/shared/Monitor.Measurement/Merging/FragmentMerger.cs b/backend/shared/MoShiro.Measurement/Merging/FragmentMerger.cs similarity index 96% rename from backend/shared/Monitor.Measurement/Merging/FragmentMerger.cs rename to backend/shared/MoShiro.Measurement/Merging/FragmentMerger.cs index e029204..04653d0 100644 --- a/backend/shared/Monitor.Measurement/Merging/FragmentMerger.cs +++ b/backend/shared/MoShiro.Measurement/Merging/FragmentMerger.cs @@ -1,8 +1,8 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// Agent 一侧为合并保留的按键记录(由正在运行的积木提供)。 public interface IRecordSource @@ -29,7 +29,7 @@ public static class FragmentMerger StreamSpec? streamSpec = spec.FindStream(stream.Name); if (streamSpec is null) { - return Result>.Fail(MonitorErrors.ValidationFailed, $"stream {stream.Name} is not declared by {spec.Id}"); + return Result>.Fail(MoShiroErrors.ValidationFailed, $"stream {stream.Name} is not declared by {spec.Id}"); } Result merged = Join(streamSpec, stream, fragment.Header, records.Records(streamId, stream.Name)); @@ -71,7 +71,7 @@ public static class FragmentMerger RawColumn? key = probe.Find(spec.Key); if (key is null || probeColumns.Any(c => probe.Find(c.Name) is null)) { - return Result.Fail(MonitorErrors.ValidationFailed, $"a fragment of stream {spec.Name} needs the columns {string.Join(", ", new[] { spec.Key }.Concat(probeColumns.Select(c => c.Name)))}"); + return Result.Fail(MoShiroErrors.ValidationFailed, $"a fragment of stream {spec.Name} needs the columns {string.Join(", ", new[] { spec.Key }.Concat(probeColumns.Select(c => c.Name)))}"); } return spec.Driver == Side.Probe ? JoinOnProbeRows(spec, probe, key, agent) : JoinOnAgentRows(spec, probe, key, header, agent); @@ -118,7 +118,7 @@ public static class FragmentMerger { if (!TryGetRange(header, out uint from, out uint to)) { - return Result.Fail(MonitorErrors.ValidationFailed, $"a fragment of stream {spec.Name} must declare its sequence range"); + return Result.Fail(MoShiroErrors.ValidationFailed, $"a fragment of stream {spec.Name} must declare its sequence range"); } // 行取自 Agent 的记录:范围内发出过的每一个包都有一行,Probe 没有报告到达的就是丢失 diff --git a/backend/shared/Monitor.Measurement/Monitor.Measurement.csproj b/backend/shared/MoShiro.Measurement/MoShiro.Measurement.csproj similarity index 67% rename from backend/shared/Monitor.Measurement/Monitor.Measurement.csproj rename to backend/shared/MoShiro.Measurement/MoShiro.Measurement.csproj index 8853475..baf3b8d 100644 --- a/backend/shared/Monitor.Measurement/Monitor.Measurement.csproj +++ b/backend/shared/MoShiro.Measurement/MoShiro.Measurement.csproj @@ -3,8 +3,8 @@ true - + - + diff --git a/backend/shared/Monitor.Measurement/Net/Tcp.cs b/backend/shared/MoShiro.Measurement/Net/Tcp.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Net/Tcp.cs rename to backend/shared/MoShiro.Measurement/Net/Tcp.cs index f0e6fe4..30adafb 100644 --- a/backend/shared/Monitor.Measurement/Net/Tcp.cs +++ b/backend/shared/MoShiro.Measurement/Net/Tcp.cs @@ -5,9 +5,9 @@ using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 拨号方的一条数据面 TCP 连接(已经完成 TLS 和首帧认证)。 public sealed class TcpLink : IDisposable diff --git a/backend/shared/Monitor.Measurement/Net/TcpInfo.cs b/backend/shared/MoShiro.Measurement/Net/TcpInfo.cs similarity index 98% rename from backend/shared/Monitor.Measurement/Net/TcpInfo.cs rename to backend/shared/MoShiro.Measurement/Net/TcpInfo.cs index d9a60d7..8a2be0c 100644 --- a/backend/shared/Monitor.Measurement/Net/TcpInfo.cs +++ b/backend/shared/MoShiro.Measurement/Net/TcpInfo.cs @@ -1,8 +1,8 @@ using System.Buffers.Binary; using System.Net.Sockets; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 内核对一条 TCP 连接的采样(Linux TCP_INFO)。 public readonly record struct TcpInfoSample(long SrttUs, long RttVarUs, long Cwnd, long TotalRetrans, long SegsOut, long BytesAcked); diff --git a/backend/shared/Monitor.Measurement/Net/UdpMux.cs b/backend/shared/MoShiro.Measurement/Net/UdpMux.cs similarity index 99% rename from backend/shared/Monitor.Measurement/Net/UdpMux.cs rename to backend/shared/MoShiro.Measurement/Net/UdpMux.cs index dc03456..9d036af 100644 --- a/backend/shared/Monitor.Measurement/Net/UdpMux.cs +++ b/backend/shared/MoShiro.Measurement/Net/UdpMux.cs @@ -2,9 +2,9 @@ using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// 一个通过校验的 UDP 包,交给按 stream_id 登记的处理函数。处理函数在接收循环里同步执行,不能阻塞。 public readonly ref struct UdpDelivery diff --git a/backend/shared/Monitor.Measurement/Platform/PlatformDetector.cs b/backend/shared/MoShiro.Measurement/Platform/PlatformDetector.cs similarity index 98% rename from backend/shared/Monitor.Measurement/Platform/PlatformDetector.cs rename to backend/shared/MoShiro.Measurement/Platform/PlatformDetector.cs index b3e5dd4..39d4fd6 100644 --- a/backend/shared/Monitor.Measurement/Platform/PlatformDetector.cs +++ b/backend/shared/MoShiro.Measurement/Platform/PlatformDetector.cs @@ -1,7 +1,7 @@ using System.Runtime.InteropServices; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// /// 读出本机的系统名和架构,只用于在面板上展示。故意只取粗粒度的信息: diff --git a/backend/shared/Monitor.Measurement/Update/UpdateBridge.cs b/backend/shared/MoShiro.Measurement/Update/UpdateBridge.cs similarity index 96% rename from backend/shared/Monitor.Measurement/Update/UpdateBridge.cs rename to backend/shared/MoShiro.Measurement/Update/UpdateBridge.cs index 255268a..236088c 100644 --- a/backend/shared/Monitor.Measurement/Update/UpdateBridge.cs +++ b/backend/shared/MoShiro.Measurement/Update/UpdateBridge.cs @@ -1,12 +1,12 @@ using System.Text; using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement; +namespace MoShiro.Measurement; /// -/// 节点进程和宿主机更新器之间的文件交换(容器里 /etc/monitor,也就是数据目录): +/// 节点进程和宿主机更新器之间的文件交换(容器里 /etc/moshiro,也就是数据目录): /// 节点把面板发来的清单和签名写进 update/,把「更新」请求写成 update/apply.request;更新器验签并更新,状态由宿主更新器写在只读挂载的 update-status/status.json。 /// 节点进程不执行任何命令,也不信任清单的内容(验签在宿主机上)。没有安装更新器时目录写不了或没人读,调用只是返回 false。 /// @@ -88,7 +88,7 @@ public sealed class UpdateBridge return null; } - return JsonSerializer.Deserialize(File.ReadAllBytes(_statusFile), MonitorJsonContext.Default.UpdateStatus); + return JsonSerializer.Deserialize(File.ReadAllBytes(_statusFile), MoShiroJsonContext.Default.UpdateStatus); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException) { diff --git a/backend/shared/Monitor.Protocol/Catalog/KindSpec.cs b/backend/shared/MoShiro.Protocol/Catalog/KindSpec.cs similarity index 99% rename from backend/shared/Monitor.Protocol/Catalog/KindSpec.cs rename to backend/shared/MoShiro.Protocol/Catalog/KindSpec.cs index 7345228..01059f9 100644 --- a/backend/shared/Monitor.Protocol/Catalog/KindSpec.cs +++ b/backend/shared/MoShiro.Protocol/Catalog/KindSpec.cs @@ -1,4 +1,4 @@ -namespace Monitor.Protocol; +namespace MoShiro.Protocol; [Flags] public enum Scope diff --git a/backend/shared/Monitor.Protocol/Catalog/Kinds/Kinds.cs b/backend/shared/MoShiro.Protocol/Catalog/Kinds/Kinds.cs similarity index 99% rename from backend/shared/Monitor.Protocol/Catalog/Kinds/Kinds.cs rename to backend/shared/MoShiro.Protocol/Catalog/Kinds/Kinds.cs index d311e91..f89639a 100644 --- a/backend/shared/Monitor.Protocol/Catalog/Kinds/Kinds.cs +++ b/backend/shared/MoShiro.Protocol/Catalog/Kinds/Kinds.cs @@ -1,4 +1,4 @@ -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// /// Kind 目录。新增一种采集方法 = 在这里加一个静态字段并放进 ; diff --git a/backend/shared/Monitor.Protocol/Catalog/ParamSpec.cs b/backend/shared/MoShiro.Protocol/Catalog/ParamSpec.cs similarity index 99% rename from backend/shared/Monitor.Protocol/Catalog/ParamSpec.cs rename to backend/shared/MoShiro.Protocol/Catalog/ParamSpec.cs index 0f415cd..133304f 100644 --- a/backend/shared/Monitor.Protocol/Catalog/ParamSpec.cs +++ b/backend/shared/MoShiro.Protocol/Catalog/ParamSpec.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Text.Json; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; public enum ParamType { diff --git a/backend/shared/Monitor.Protocol/Ids/MonitorIds.cs b/backend/shared/MoShiro.Protocol/Ids/MonitorIds.cs similarity index 98% rename from backend/shared/Monitor.Protocol/Ids/MonitorIds.cs rename to backend/shared/MoShiro.Protocol/Ids/MonitorIds.cs index 2881993..2b9f68e 100644 --- a/backend/shared/Monitor.Protocol/Ids/MonitorIds.cs +++ b/backend/shared/MoShiro.Protocol/Ids/MonitorIds.cs @@ -1,7 +1,7 @@ using System.Text.Json.Serialization; using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; [JsonConverter(typeof(ProbeIdJsonConverter))] public readonly record struct ProbeId(string Value) diff --git a/backend/shared/Monitor.Protocol/Monitor.Protocol.csproj b/backend/shared/MoShiro.Protocol/MoShiro.Protocol.csproj similarity index 87% rename from backend/shared/Monitor.Protocol/Monitor.Protocol.csproj rename to backend/shared/MoShiro.Protocol/MoShiro.Protocol.csproj index 1d2bc78..5eb7cd2 100644 --- a/backend/shared/Monitor.Protocol/Monitor.Protocol.csproj +++ b/backend/shared/MoShiro.Protocol/MoShiro.Protocol.csproj @@ -7,7 +7,7 @@ @@ -16,14 +16,14 @@ $(IntermediateOutputPath)BuildInfo.g.cs - + - - - + + + diff --git a/backend/shared/Monitor.Protocol/MonitorErrors.cs b/backend/shared/MoShiro.Protocol/MonitorErrors.cs similarity index 95% rename from backend/shared/Monitor.Protocol/MonitorErrors.cs rename to backend/shared/MoShiro.Protocol/MonitorErrors.cs index 7de2b95..91c7259 100644 --- a/backend/shared/Monitor.Protocol/MonitorErrors.cs +++ b/backend/shared/MoShiro.Protocol/MonitorErrors.cs @@ -1,9 +1,9 @@ using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; -/// Monitor 的错误码(在 Fleet 错误码之上扩展)。REST、JSON-RPC、OpenAPI 共用同一份。 -public static class MonitorErrors +/// MoShiro 的错误码(在 Fleet 错误码之上扩展)。REST、JSON-RPC、OpenAPI 共用同一份。 +public static class MoShiroErrors { public static readonly ErrorCode ValidationFailed = new("validation_failed"); public static readonly ErrorCode BudgetExceeded = new("budget_exceeded"); diff --git a/backend/shared/Monitor.Protocol/ProtocolLimits.cs b/backend/shared/MoShiro.Protocol/ProtocolLimits.cs similarity index 75% rename from backend/shared/Monitor.Protocol/ProtocolLimits.cs rename to backend/shared/MoShiro.Protocol/ProtocolLimits.cs index f227b86..90f3c86 100644 --- a/backend/shared/Monitor.Protocol/ProtocolLimits.cs +++ b/backend/shared/MoShiro.Protocol/ProtocolLimits.cs @@ -1,4 +1,4 @@ -namespace Monitor.Protocol; +namespace MoShiro.Protocol; public static class ProtocolVersion { @@ -25,8 +25,8 @@ public static class ProtocolLimits public const long MaxLineBudgetBitsPerSecond = 100_000; } -/// 二进制通道号(0–15 属于 Fleet,Monitor 从 16 开始)。 -public static class MonitorChannels +/// 二进制通道号(0–15 属于 Fleet,MoShiro 从 16 开始)。 +public static class MoShiroChannels { /// Agent → Backend:RawChunk(发件箱)。 public const byte RawChunk = 16; @@ -35,9 +35,9 @@ public static class MonitorChannels public const byte ColumnBlock = 17; } -public static class MonitorCapabilities +public static class MoShiroCapabilities { - public const string Base = "monitor.v1"; - public const string KindsPrefix = "monitor.kinds:"; - public const string BlocksPrefix = "monitor.blocks:"; + public const string Base = "moshiro.v1"; + public const string KindsPrefix = "moshiro.kinds:"; + public const string BlocksPrefix = "moshiro.blocks:"; } diff --git a/backend/shared/Monitor.Protocol/Raw/Crc32c.cs b/backend/shared/MoShiro.Protocol/Raw/Crc32c.cs similarity index 96% rename from backend/shared/Monitor.Protocol/Raw/Crc32c.cs rename to backend/shared/MoShiro.Protocol/Raw/Crc32c.cs index 0a6d0f9..e18bf9e 100644 --- a/backend/shared/Monitor.Protocol/Raw/Crc32c.cs +++ b/backend/shared/MoShiro.Protocol/Raw/Crc32c.cs @@ -1,4 +1,4 @@ -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// CRC-32C(Castagnoli),RawChunk 的完整性校验。 public static class Crc32c diff --git a/backend/shared/Monitor.Protocol/Raw/RawChunk.cs b/backend/shared/MoShiro.Protocol/Raw/RawChunk.cs similarity index 99% rename from backend/shared/Monitor.Protocol/Raw/RawChunk.cs rename to backend/shared/MoShiro.Protocol/Raw/RawChunk.cs index d6ae3d3..0255d66 100644 --- a/backend/shared/Monitor.Protocol/Raw/RawChunk.cs +++ b/backend/shared/MoShiro.Protocol/Raw/RawChunk.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// Agent 单调时钟与 UTC 的锚点,以及 Probe 时钟相对 Agent 时钟的偏移估计。 public sealed class ChunkClock diff --git a/backend/shared/Monitor.Protocol/Raw/RawChunkCodec.cs b/backend/shared/MoShiro.Protocol/Raw/RawChunkCodec.cs similarity index 97% rename from backend/shared/Monitor.Protocol/Raw/RawChunkCodec.cs rename to backend/shared/MoShiro.Protocol/Raw/RawChunkCodec.cs index 00c5298..aaaca96 100644 --- a/backend/shared/Monitor.Protocol/Raw/RawChunkCodec.cs +++ b/backend/shared/MoShiro.Protocol/Raw/RawChunkCodec.cs @@ -3,18 +3,18 @@ using System.IO.Compression; using System.Text.Json; using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// /// RawChunk 的编解码(Probe、Agent、Backend、离线工具共用同一份代码)。 -/// 布局:"MNRC" | u8 container_version | u32 crc32c(压缩数据) | Brotli( u32 header_len | header JSON | 各列数据 )。 +/// 布局:"MSRC" | u8 container_version | u32 crc32c(压缩数据) | Brotli( u32 header_len | header JSON | 各列数据 )。 /// 列数据:可空列先写有效位图,再写有效值;plain 为无符号 varint(有符号类型用 zigzag),delta 为与上一个有效值之差的 zigzag varint。 /// public static class RawChunkCodec { private const int PrefixBytes = 4 + 1 + 4; - private static ReadOnlySpan Magic => "MNRC"u8; + private static ReadOnlySpan Magic => "MSRC"u8; /// 压缩之前的正文:u32 header_len | header JSON | 各列数据。布局变化时黄金字节测试会失败,提醒升级版本。 public static byte[] EncodeBody(RawChunk chunk) @@ -40,7 +40,7 @@ public static class RawChunkCodec header.Streams.Add(streamHeader); } - byte[] headerJson = JsonSerializer.SerializeToUtf8Bytes(header, MonitorJsonContext.Default.RawChunkHeader); + byte[] headerJson = JsonSerializer.SerializeToUtf8Bytes(header, MoShiroJsonContext.Default.RawChunkHeader); using var body = new MemoryStream(); Span length = stackalloc byte[4]; @@ -114,7 +114,7 @@ public static class RawChunkCodec RawChunkHeader? header; try { - header = JsonSerializer.Deserialize(plain.AsSpan(4, (int)headerLength), MonitorJsonContext.Default.RawChunkHeader); + header = JsonSerializer.Deserialize(plain.AsSpan(4, (int)headerLength), MoShiroJsonContext.Default.RawChunkHeader); } catch (JsonException) { @@ -159,7 +159,7 @@ public static class RawChunkCodec } private static Result Corrupt(string message) => - Result.Fail(MonitorErrors.RawCorrupt, message); + Result.Fail(MoShiroErrors.RawCorrupt, message); private static byte[]? Inflate(ReadOnlySpan payload) { diff --git a/backend/shared/Monitor.Protocol/Raw/StreamBuilder.cs b/backend/shared/MoShiro.Protocol/Raw/StreamBuilder.cs similarity index 98% rename from backend/shared/Monitor.Protocol/Raw/StreamBuilder.cs rename to backend/shared/MoShiro.Protocol/Raw/StreamBuilder.cs index 65a5d38..4c2ec45 100644 --- a/backend/shared/Monitor.Protocol/Raw/StreamBuilder.cs +++ b/backend/shared/MoShiro.Protocol/Raw/StreamBuilder.cs @@ -1,4 +1,4 @@ -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// 按 逐行构造一个 (用于测试、模拟器和离线工具,不用于节点热路径)。 public sealed class StreamBuilder diff --git a/backend/shared/Monitor.Protocol/Rpc/MonitorRpc.cs b/backend/shared/MoShiro.Protocol/Rpc/MonitorRpc.cs similarity index 94% rename from backend/shared/Monitor.Protocol/Rpc/MonitorRpc.cs rename to backend/shared/MoShiro.Protocol/Rpc/MonitorRpc.cs index d5228e7..83864cc 100644 --- a/backend/shared/Monitor.Protocol/Rpc/MonitorRpc.cs +++ b/backend/shared/MoShiro.Protocol/Rpc/MonitorRpc.cs @@ -2,25 +2,25 @@ using System.Text.Json; using System.Text.Json.Serialization; using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; -/// monitor.* 方法名。新增方法 = 在这里定义名称和 params/result,再在组装处加一行 .On(...)。 -public static class MonitorMethods +/// moshiro.* 方法名。新增方法 = 在这里定义名称和 params/result,再在组装处加一行 .On(...)。 +public static class MoShiroMethods { - public const string TestRun = "monitor.test.run"; - public const string TestCancel = "monitor.test.cancel"; - public const string ProbeDisconnect = "monitor.probe.disconnect"; - public const string TestStatus = "monitor.test.status"; - public const string Event = "monitor.event"; - public const string Host = "monitor.host"; - public const string UpdateOffer = "monitor.update.offer"; - public const string UpdateApply = "monitor.update.apply"; + public const string TestRun = "moshiro.test.run"; + public const string TestCancel = "moshiro.test.cancel"; + public const string ProbeDisconnect = "moshiro.probe.disconnect"; + public const string TestStatus = "moshiro.test.status"; + public const string Event = "moshiro.event"; + public const string Host = "moshiro.host"; + public const string UpdateOffer = "moshiro.update.offer"; + public const string UpdateApply = "moshiro.update.apply"; // Probe ↔ Agent(点对点会话) - public const string Clock = "monitor.clock"; - public const string TestPhase = "monitor.test.phase"; - public const string TestPhaseEnd = "monitor.test.phase_end"; - public const string TestAbort = "monitor.test.abort"; + public const string Clock = "moshiro.clock"; + public const string TestPhase = "moshiro.test.phase"; + public const string TestPhaseEnd = "moshiro.test.phase_end"; + public const string TestAbort = "moshiro.test.abort"; } public static class AgentEventKinds @@ -159,7 +159,7 @@ public sealed class ProbeAgentList public List Agents { get; set; } = []; } -/// 注册元数据(Monitor 定义,经 Fleet 的 meta 字段带上来)。 +/// 注册元数据(MoShiro 定义,经 Fleet 的 meta 字段带上来)。 public sealed class AgentEnrollMeta { [JsonPropertyName("probe_tcp_port")] @@ -220,7 +220,7 @@ public sealed class PlatformInfo } /// -/// 面板给节点的更新清单(monitor.update.offer):manifest 是发布清单的原始文本,signature 是 base64 的签名。 +/// 面板给节点的更新清单(moshiro.update.offer):manifest 是发布清单的原始文本,signature 是 base64 的签名。 /// 节点只是把它们原样放进数据目录;验签、拉取、重启都由宿主机上的更新器完成(节点进程不执行任何命令,也不信任清单的内容)。 /// public sealed class UpdateOfferParams @@ -436,7 +436,7 @@ public sealed class LineTaskSet public List TcpPorts { get; set; } = []; } -/// monitor.clock:Probe 用它估计与 Agent 单调时钟的偏移(对称往返假设)。 +/// moshiro.clock:Probe 用它估计与 Agent 单调时钟的偏移(对称往返假设)。 public sealed class ClockParams { [JsonPropertyName("probe_mono_us")] @@ -543,4 +543,4 @@ public static class FragmentKeys [JsonSerializable(typeof(AgentHelloMeta))] [JsonSerializable(typeof(AgentHeartbeatExtra))] [JsonSerializable(typeof(RawChunkHeader))] -public sealed partial class MonitorJsonContext : JsonSerializerContext; +public sealed partial class MoShiroJsonContext : JsonSerializerContext; diff --git a/backend/shared/Monitor.Protocol/Tasks/TaskSet.cs b/backend/shared/MoShiro.Protocol/Tasks/TaskSet.cs similarity index 94% rename from backend/shared/Monitor.Protocol/Tasks/TaskSet.cs rename to backend/shared/MoShiro.Protocol/Tasks/TaskSet.cs index 2c320a5..88771f4 100644 --- a/backend/shared/Monitor.Protocol/Tasks/TaskSet.cs +++ b/backend/shared/MoShiro.Protocol/Tasks/TaskSet.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// Task = { id, scope, kind, target, params, schedule, enabled }。参数已经按 ParamSpec 校验并补全默认值。 public sealed class TaskSpec @@ -79,9 +79,9 @@ public static class TaskSchedule { return minutes >= 60 ? Result.Ok(minutes) - : Result.Fail(MonitorErrors.ValidationFailed, "every:N requires N >= 60 minutes"); + : Result.Fail(MoShiroErrors.ValidationFailed, "every:N requires N >= 60 minutes"); } - return Result.Fail(MonitorErrors.ValidationFailed, "schedule must be continuous, once or every:"); + return Result.Fail(MoShiroErrors.ValidationFailed, "schedule must be continuous, once or every:"); } } diff --git a/backend/shared/Monitor.Protocol/Wire/TcpWire.cs b/backend/shared/MoShiro.Protocol/Wire/TcpWire.cs similarity index 99% rename from backend/shared/Monitor.Protocol/Wire/TcpWire.cs rename to backend/shared/MoShiro.Protocol/Wire/TcpWire.cs index 6ca01af..b03a5b3 100644 --- a/backend/shared/Monitor.Protocol/Wire/TcpWire.cs +++ b/backend/shared/MoShiro.Protocol/Wire/TcpWire.cs @@ -2,7 +2,7 @@ using System.Buffers.Binary; using System.Security.Cryptography; using Fleet.Core; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; /// /// 数据面的 TCP 连接(Fleet 之外的线格式)。连接都由 Probe 发起;TLS 是可选的(ClientHello 以 0x16 开头, diff --git a/backend/shared/Monitor.Protocol/Wire/UdpPacket.cs b/backend/shared/MoShiro.Protocol/Wire/UdpPacket.cs similarity index 99% rename from backend/shared/Monitor.Protocol/Wire/UdpPacket.cs rename to backend/shared/MoShiro.Protocol/Wire/UdpPacket.cs index 2827ba6..737b57c 100644 --- a/backend/shared/Monitor.Protocol/Wire/UdpPacket.cs +++ b/backend/shared/MoShiro.Protocol/Wire/UdpPacket.cs @@ -1,7 +1,7 @@ using System.Buffers.Binary; using System.Security.Cryptography; -namespace Monitor.Protocol; +namespace MoShiro.Protocol; public enum UdpPacketType : byte { diff --git a/backend/shared/tests/Monitor.Measurement.Tests/MeasurementTests.cs b/backend/shared/tests/MoShiro.Measurement.Tests/MeasurementTests.cs similarity index 99% rename from backend/shared/tests/Monitor.Measurement.Tests/MeasurementTests.cs rename to backend/shared/tests/MoShiro.Measurement.Tests/MeasurementTests.cs index f0875a9..1912f09 100644 --- a/backend/shared/tests/Monitor.Measurement.Tests/MeasurementTests.cs +++ b/backend/shared/tests/MoShiro.Measurement.Tests/MeasurementTests.cs @@ -1,8 +1,8 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Measurement.Tests; +namespace MoShiro.Measurement.Tests; public sealed class MeasurementTests { diff --git a/backend/shared/tests/Monitor.Measurement.Tests/Monitor.Measurement.Tests.csproj b/backend/shared/tests/MoShiro.Measurement.Tests/MoShiro.Measurement.Tests.csproj similarity index 82% rename from backend/shared/tests/Monitor.Measurement.Tests/Monitor.Measurement.Tests.csproj rename to backend/shared/tests/MoShiro.Measurement.Tests/MoShiro.Measurement.Tests.csproj index e067df9..c8f64bd 100644 --- a/backend/shared/tests/Monitor.Measurement.Tests/Monitor.Measurement.Tests.csproj +++ b/backend/shared/tests/MoShiro.Measurement.Tests/MoShiro.Measurement.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/backend/shared/tests/Monitor.Measurement.Tests/PlatformDetectorTests.cs b/backend/shared/tests/MoShiro.Measurement.Tests/PlatformDetectorTests.cs similarity index 97% rename from backend/shared/tests/Monitor.Measurement.Tests/PlatformDetectorTests.cs rename to backend/shared/tests/MoShiro.Measurement.Tests/PlatformDetectorTests.cs index 365c7df..ddb2575 100644 --- a/backend/shared/tests/Monitor.Measurement.Tests/PlatformDetectorTests.cs +++ b/backend/shared/tests/MoShiro.Measurement.Tests/PlatformDetectorTests.cs @@ -1,4 +1,4 @@ -namespace Monitor.Measurement.Tests; +namespace MoShiro.Measurement.Tests; public sealed class PlatformDetectorTests { diff --git a/backend/shared/tests/Monitor.Protocol.Tests/CatalogTests.cs b/backend/shared/tests/MoShiro.Protocol.Tests/CatalogTests.cs similarity index 93% rename from backend/shared/tests/Monitor.Protocol.Tests/CatalogTests.cs rename to backend/shared/tests/MoShiro.Protocol.Tests/CatalogTests.cs index 2a98537..56cc60e 100644 --- a/backend/shared/tests/Monitor.Protocol.Tests/CatalogTests.cs +++ b/backend/shared/tests/MoShiro.Protocol.Tests/CatalogTests.cs @@ -1,8 +1,8 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Protocol.Tests; +namespace MoShiro.Protocol.Tests; public sealed class CatalogTests { @@ -101,8 +101,8 @@ public sealed class CatalogTests Tasks = [new TaskSpec { Id = new TaskId("tsk_1"), Kind = "udp_echo", Params = new Dictionary { ["rate_hz"] = Json("10") } }], Peers = [new PeerSpec { ProbeId = new ProbeId("prb_1"), PublicKey = "AAAA" }], }; - string text = JsonSerializer.Serialize(set, MonitorJsonContext.Default.TaskSet); - TaskSet back = JsonSerializer.Deserialize(text, MonitorJsonContext.Default.TaskSet)!; + string text = JsonSerializer.Serialize(set, MoShiroJsonContext.Default.TaskSet); + TaskSet back = JsonSerializer.Deserialize(text, MoShiroJsonContext.Default.TaskSet)!; Assert.Contains("\"id\":\"tsk_1\"", text); Assert.Equal(7, back.Version); @@ -114,7 +114,7 @@ public sealed class CatalogTests [Fact] public void AgentEnrollMeta_ToleratesUnknownFields_AndKeepsDefaults() { - AgentEnrollMeta meta = JsonSerializer.Deserialize("{\"kinds\":[\"udp_echo\"],\"future\":1}", MonitorJsonContext.Default.AgentEnrollMeta)!; + AgentEnrollMeta meta = JsonSerializer.Deserialize("{\"kinds\":[\"udp_echo\"],\"future\":1}", MoShiroJsonContext.Default.AgentEnrollMeta)!; Assert.Equal(7443, meta.ProbeTcpPort); Assert.Equal(["udp_echo"], meta.Kinds); } @@ -122,7 +122,7 @@ public sealed class CatalogTests [Fact] public void ErrorCodes_AreSnakeCase() { - foreach (System.Reflection.FieldInfo field in typeof(MonitorErrors).GetFields()) + foreach (System.Reflection.FieldInfo field in typeof(MoShiroErrors).GetFields()) { string value = ((ErrorCode)field.GetValue(null)!).Value; Assert.Matches("^[a-z]+(_[a-z]+)*$", value); diff --git a/backend/shared/tests/Monitor.Protocol.Tests/Monitor.Protocol.Tests.csproj b/backend/shared/tests/MoShiro.Protocol.Tests/MoShiro.Protocol.Tests.csproj similarity index 65% rename from backend/shared/tests/Monitor.Protocol.Tests/Monitor.Protocol.Tests.csproj rename to backend/shared/tests/MoShiro.Protocol.Tests/MoShiro.Protocol.Tests.csproj index 644ddc0..c7bb997 100644 --- a/backend/shared/tests/Monitor.Protocol.Tests/Monitor.Protocol.Tests.csproj +++ b/backend/shared/tests/MoShiro.Protocol.Tests/MoShiro.Protocol.Tests.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/backend/shared/tests/Monitor.Protocol.Tests/RawChunkCodecTests.cs b/backend/shared/tests/MoShiro.Protocol.Tests/RawChunkCodecTests.cs similarity index 97% rename from backend/shared/tests/Monitor.Protocol.Tests/RawChunkCodecTests.cs rename to backend/shared/tests/MoShiro.Protocol.Tests/RawChunkCodecTests.cs index 6d65147..1f659ec 100644 --- a/backend/shared/tests/Monitor.Protocol.Tests/RawChunkCodecTests.cs +++ b/backend/shared/tests/MoShiro.Protocol.Tests/RawChunkCodecTests.cs @@ -1,10 +1,10 @@ using System.Buffers.Binary; using System.IO.Compression; using Fleet.Core; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Protocol.Tests; +namespace MoShiro.Protocol.Tests; public sealed class RawChunkCodecTests { @@ -103,7 +103,7 @@ public sealed class RawChunkCodecTests byte[] badCrc = (byte[])good.Clone(); badCrc[^1] ^= 0xFF; - Assert.Equal(MonitorErrors.RawCorrupt, RawChunkCodec.Decode(badMagic).Error.Code); + Assert.Equal(MoShiroErrors.RawCorrupt, RawChunkCodec.Decode(badMagic).Error.Code); Assert.Contains("version", RawChunkCodec.Decode(badVersion).Error.Message); Assert.Contains("crc", RawChunkCodec.Decode(badCrc).Error.Message); Assert.False(RawChunkCodec.Decode(good.AsSpan(0, good.Length / 2)).IsSuccess); @@ -190,7 +190,7 @@ public sealed class RawChunkCodecTests private static byte[] Wrap(byte[] payload) { byte[] result = new byte[9 + payload.Length]; - "MNRC"u8.CopyTo(result); + "MSRC"u8.CopyTo(result); result[4] = ProtocolVersion.Container; BinaryPrimitives.WriteUInt32LittleEndian(result.AsSpan(5), Crc32c.Compute(payload)); payload.CopyTo(result, 9); diff --git a/backend/src/Monitor.Backend.Analysis/Accumulators/Accumulator.cs b/backend/src/MoShiro.Backend.Analysis/Accumulators/Accumulator.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Accumulators/Accumulator.cs rename to backend/src/MoShiro.Backend.Analysis/Accumulators/Accumulator.cs index 06077e3..197ae00 100644 --- a/backend/src/Monitor.Backend.Analysis/Accumulators/Accumulator.cs +++ b/backend/src/MoShiro.Backend.Analysis/Accumulators/Accumulator.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; public enum AccKind : byte { diff --git a/backend/src/Monitor.Backend.Analysis/Accumulators/AccumulatorCodec.cs b/backend/src/MoShiro.Backend.Analysis/Accumulators/AccumulatorCodec.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Accumulators/AccumulatorCodec.cs rename to backend/src/MoShiro.Backend.Analysis/Accumulators/AccumulatorCodec.cs index b0c987a..81af219 100644 --- a/backend/src/Monitor.Backend.Analysis/Accumulators/AccumulatorCodec.cs +++ b/backend/src/MoShiro.Backend.Analysis/Accumulators/AccumulatorCodec.cs @@ -2,7 +2,7 @@ using System.Buffers.Binary; using System.Text; using Fleet.Core; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 一个时间桶里所有指标的累加器:指标 id → 累加器。 public sealed class MetricSet diff --git a/backend/src/Monitor.Backend.Analysis/Accumulators/HistogramAcc.cs b/backend/src/MoShiro.Backend.Analysis/Accumulators/HistogramAcc.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Accumulators/HistogramAcc.cs rename to backend/src/MoShiro.Backend.Analysis/Accumulators/HistogramAcc.cs index 4f58669..3027934 100644 --- a/backend/src/Monitor.Backend.Analysis/Accumulators/HistogramAcc.cs +++ b/backend/src/MoShiro.Backend.Analysis/Accumulators/HistogramAcc.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// /// 固定对数桶直方图:100 µs ~ 30 s,每 2 倍 8 个桶(相对误差 ≤ 4.5%)。 diff --git a/backend/src/Monitor.Backend.Analysis/Clock/ClockDriftFit.cs b/backend/src/MoShiro.Backend.Analysis/Clock/ClockDriftFit.cs similarity index 98% rename from backend/src/Monitor.Backend.Analysis/Clock/ClockDriftFit.cs rename to backend/src/MoShiro.Backend.Analysis/Clock/ClockDriftFit.cs index 838ffac..7952041 100644 --- a/backend/src/Monitor.Backend.Analysis/Clock/ClockDriftFit.cs +++ b/backend/src/MoShiro.Backend.Analysis/Clock/ClockDriftFit.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// /// 估计两端的时钟偏移和线性漂移:单向时延 x(t) = 偏移 + 漂移·t + 真实的时延变化。 diff --git a/backend/src/Monitor.Backend.Analysis/Engine/ChangePoint.cs b/backend/src/MoShiro.Backend.Analysis/Engine/ChangePoint.cs similarity index 98% rename from backend/src/Monitor.Backend.Analysis/Engine/ChangePoint.cs rename to backend/src/MoShiro.Backend.Analysis/Engine/ChangePoint.cs index e5e3e67..b061bf9 100644 --- a/backend/src/Monitor.Backend.Analysis/Engine/ChangePoint.cs +++ b/backend/src/MoShiro.Backend.Analysis/Engine/ChangePoint.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 变点检测(均值漂移):找到使前后两段均值差最显著的位置,用来判断 QoS 从什么时候开始限速。 public static class ChangePoint diff --git a/backend/src/Monitor.Backend.Analysis/Engine/ChunkView.cs b/backend/src/MoShiro.Backend.Analysis/Engine/ChunkView.cs similarity index 95% rename from backend/src/Monitor.Backend.Analysis/Engine/ChunkView.cs rename to backend/src/MoShiro.Backend.Analysis/Engine/ChunkView.cs index ad7d536..3208a72 100644 --- a/backend/src/Monitor.Backend.Analysis/Engine/ChunkView.cs +++ b/backend/src/MoShiro.Backend.Analysis/Engine/ChunkView.cs @@ -1,7 +1,7 @@ using System.Text.Json; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 算子把观测值交给这里;窗口评估器负责按时间分桶。 public interface IObservationSink diff --git a/backend/src/Monitor.Backend.Analysis/Engine/MetricDef.cs b/backend/src/MoShiro.Backend.Analysis/Engine/MetricDef.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Engine/MetricDef.cs rename to backend/src/MoShiro.Backend.Analysis/Engine/MetricDef.cs index d257f0e..1a60b6a 100644 --- a/backend/src/Monitor.Backend.Analysis/Engine/MetricDef.cs +++ b/backend/src/MoShiro.Backend.Analysis/Engine/MetricDef.cs @@ -1,8 +1,8 @@ using System.Security.Cryptography; using System.Text; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; public enum Unit { diff --git a/backend/src/Monitor.Backend.Analysis/Engine/TestEvaluator.cs b/backend/src/MoShiro.Backend.Analysis/Engine/TestEvaluator.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Engine/TestEvaluator.cs rename to backend/src/MoShiro.Backend.Analysis/Engine/TestEvaluator.cs index 14fede2..5988e98 100644 --- a/backend/src/Monitor.Backend.Analysis/Engine/TestEvaluator.cs +++ b/backend/src/MoShiro.Backend.Analysis/Engine/TestEvaluator.cs @@ -1,7 +1,7 @@ using System.Text.Json; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; public sealed class TestStepResult { diff --git a/backend/src/Monitor.Backend.Analysis/Engine/WindowEvaluator.cs b/backend/src/MoShiro.Backend.Analysis/Engine/WindowEvaluator.cs similarity index 97% rename from backend/src/Monitor.Backend.Analysis/Engine/WindowEvaluator.cs rename to backend/src/MoShiro.Backend.Analysis/Engine/WindowEvaluator.cs index d6f41b0..2b50fa8 100644 --- a/backend/src/Monitor.Backend.Analysis/Engine/WindowEvaluator.cs +++ b/backend/src/MoShiro.Backend.Analysis/Engine/WindowEvaluator.cs @@ -1,6 +1,6 @@ -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// /// 把原始块按时间分桶算成累加器。流式路径(1s 桶)和持久路径(分钟窗口)调用同一份代码, diff --git a/backend/src/Monitor.Backend.Analysis/Metrics/LinkMetrics.cs b/backend/src/MoShiro.Backend.Analysis/Metrics/LinkMetrics.cs similarity index 98% rename from backend/src/Monitor.Backend.Analysis/Metrics/LinkMetrics.cs rename to backend/src/MoShiro.Backend.Analysis/Metrics/LinkMetrics.cs index d76d8b0..090686e 100644 --- a/backend/src/Monitor.Backend.Analysis/Metrics/LinkMetrics.cs +++ b/backend/src/MoShiro.Backend.Analysis/Metrics/LinkMetrics.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 线路基础指标(udp_echo / icmp_echo 的回显流)。新增指标 = 加一行声明。 public static class LinkMetrics diff --git a/backend/src/Monitor.Backend.Analysis/Metrics/MetricRegistry.cs b/backend/src/MoShiro.Backend.Analysis/Metrics/MetricRegistry.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Metrics/MetricRegistry.cs rename to backend/src/MoShiro.Backend.Analysis/Metrics/MetricRegistry.cs index 140bd8a..e332a84 100644 --- a/backend/src/Monitor.Backend.Analysis/Metrics/MetricRegistry.cs +++ b/backend/src/MoShiro.Backend.Analysis/Metrics/MetricRegistry.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; public sealed record PresetDef(string Id, string Label, IReadOnlyList MetricIds); diff --git a/backend/src/Monitor.Backend.Analysis/Metrics/SessionMetrics.cs b/backend/src/MoShiro.Backend.Analysis/Metrics/SessionMetrics.cs similarity index 98% rename from backend/src/Monitor.Backend.Analysis/Metrics/SessionMetrics.cs rename to backend/src/MoShiro.Backend.Analysis/Metrics/SessionMetrics.cs index 0a164a2..6f5acbb 100644 --- a/backend/src/Monitor.Backend.Analysis/Metrics/SessionMetrics.cs +++ b/backend/src/MoShiro.Backend.Analysis/Metrics/SessionMetrics.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 连接建立、长连接、UDP 压测(udp_train)的指标。 public static class SessionMetrics diff --git a/backend/src/Monitor.Backend.Analysis/Metrics/TcpMetrics.cs b/backend/src/MoShiro.Backend.Analysis/Metrics/TcpMetrics.cs similarity index 98% rename from backend/src/Monitor.Backend.Analysis/Metrics/TcpMetrics.cs rename to backend/src/MoShiro.Backend.Analysis/Metrics/TcpMetrics.cs index 83851a2..a4759f1 100644 --- a/backend/src/Monitor.Backend.Analysis/Metrics/TcpMetrics.cs +++ b/backend/src/MoShiro.Backend.Analysis/Metrics/TcpMetrics.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// TCP 指标:回显的 RTT 与卡顿、内核采样的重传、批量传输的吞吐。 public static class TcpMetrics diff --git a/backend/src/Monitor.Backend.Analysis/Monitor.Backend.Analysis.csproj b/backend/src/MoShiro.Backend.Analysis/MoShiro.Backend.Analysis.csproj similarity index 63% rename from backend/src/Monitor.Backend.Analysis/Monitor.Backend.Analysis.csproj rename to backend/src/MoShiro.Backend.Analysis/MoShiro.Backend.Analysis.csproj index a546806..b2d12ee 100644 --- a/backend/src/Monitor.Backend.Analysis/Monitor.Backend.Analysis.csproj +++ b/backend/src/MoShiro.Backend.Analysis/MoShiro.Backend.Analysis.csproj @@ -3,6 +3,6 @@ true - + diff --git a/backend/src/Monitor.Backend.Analysis/Models/Models.cs b/backend/src/MoShiro.Backend.Analysis/Models/Models.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Models/Models.cs rename to backend/src/MoShiro.Backend.Analysis/Models/Models.cs index 506b574..aeb9825 100644 --- a/backend/src/Monitor.Backend.Analysis/Models/Models.cs +++ b/backend/src/MoShiro.Backend.Analysis/Models/Models.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 线路基础质量:RTT、丢包、抖动、连续丢包。 public sealed class BaseModel : ServiceModelBase diff --git a/backend/src/Monitor.Backend.Analysis/Models/ServiceModel.cs b/backend/src/MoShiro.Backend.Analysis/Models/ServiceModel.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Models/ServiceModel.cs rename to backend/src/MoShiro.Backend.Analysis/Models/ServiceModel.cs index d37b2b7..843b318 100644 --- a/backend/src/Monitor.Backend.Analysis/Models/ServiceModel.cs +++ b/backend/src/MoShiro.Backend.Analysis/Models/ServiceModel.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 模型的输入:一个时间段内的导出值(例如 rtt.p95)。没有数据的指标不存在。 public sealed class MetricView diff --git a/backend/src/Monitor.Backend.Analysis/Ops/EchoOps.cs b/backend/src/MoShiro.Backend.Analysis/Ops/EchoOps.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Ops/EchoOps.cs rename to backend/src/MoShiro.Backend.Analysis/Ops/EchoOps.cs index 7ac94c4..f160ef9 100644 --- a/backend/src/Monitor.Backend.Analysis/Ops/EchoOps.cs +++ b/backend/src/MoShiro.Backend.Analysis/Ops/EchoOps.cs @@ -1,6 +1,6 @@ -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; public enum Dir { diff --git a/backend/src/Monitor.Backend.Analysis/Ops/Ops.cs b/backend/src/MoShiro.Backend.Analysis/Ops/Ops.cs similarity index 98% rename from backend/src/Monitor.Backend.Analysis/Ops/Ops.cs rename to backend/src/MoShiro.Backend.Analysis/Ops/Ops.cs index 4ee101d..ab9ecd1 100644 --- a/backend/src/Monitor.Backend.Analysis/Ops/Ops.cs +++ b/backend/src/MoShiro.Backend.Analysis/Ops/Ops.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// 算子工厂:Ops.Rtt、Ops.Loss(Dir.Up) 等,供 MetricDef 声明使用。 public static class Ops diff --git a/backend/src/Monitor.Backend.Analysis/Ops/SessionOps.cs b/backend/src/MoShiro.Backend.Analysis/Ops/SessionOps.cs similarity index 99% rename from backend/src/Monitor.Backend.Analysis/Ops/SessionOps.cs rename to backend/src/MoShiro.Backend.Analysis/Ops/SessionOps.cs index fb521fe..2fd97fb 100644 --- a/backend/src/Monitor.Backend.Analysis/Ops/SessionOps.cs +++ b/backend/src/MoShiro.Backend.Analysis/Ops/SessionOps.cs @@ -1,6 +1,6 @@ -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Analysis; +namespace MoShiro.Backend.Analysis; /// tcp_info 流的辅助:side 为 0 表示 Probe 一侧记录(Probe 时钟),否则为 Agent 一侧。 internal static class TcpInfoTime diff --git a/backend/src/Monitor.Backend.Api/Admin/AgentEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/AgentEndpoints.cs similarity index 97% rename from backend/src/Monitor.Backend.Api/Admin/AgentEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/AgentEndpoints.cs index a9ff75d..9360dd0 100644 --- a/backend/src/Monitor.Backend.Api/Admin/AgentEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/AgentEndpoints.cs @@ -2,10 +2,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// Agent 管理:列表、修改、审批、启停、接管、删除;加入令牌;Probe 身份。 public static class AgentEndpoints @@ -77,7 +77,7 @@ public static class AgentEndpoints AgentView? view = await agents.GetAsync(new NodeId(id), ct); if (view is null) { - return Problems.Create(http, MonitorErrors.AgentNotFound, "agent not found"); + return Problems.Create(http, MoShiroErrors.AgentNotFound, "agent not found"); } Concurrency.SetETag(http, view.Agent.RowVersion); @@ -166,7 +166,7 @@ public static class AgentEndpoints private static async Task GetProbeAsync(HttpContext http, ProbeService probes, string id, CancellationToken ct) => await probes.GetAsync(id, ct) is { } view ? TypedResults.Json(Mappers.ToDto(view), ApiJsonContext.Default.ProbeDto) - : Problems.Create(http, MonitorErrors.ProbeNotFound, "probe not found"); + : Problems.Create(http, MoShiroErrors.ProbeNotFound, "probe not found"); private static async Task PatchProbeAsync(HttpContext http, ProbeService probes, PatchProbeRequest request, string id, CancellationToken ct) { @@ -211,7 +211,7 @@ public static class AgentEndpoints return Problems.From(http, error); } - return dropped ? TypedResults.NoContent() : Problems.Create(http, MonitorErrors.ProbeOffline, "this probe has no session with this agent"); + return dropped ? TypedResults.NoContent() : Problems.Create(http, MoShiroErrors.ProbeOffline, "this probe has no session with this agent"); } /// Probe 要连的面板地址:配置的公开地址,没有就用这次请求的地址。 diff --git a/backend/src/Monitor.Backend.Api/Admin/AuthEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/AuthEndpoints.cs similarity index 94% rename from backend/src/Monitor.Backend.Api/Admin/AuthEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/AuthEndpoints.cs index 6294413..54a47d4 100644 --- a/backend/src/Monitor.Backend.Api/Admin/AuthEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/AuthEndpoints.cs @@ -2,10 +2,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 登录、退出、当前身份。登录接口限流,失败一律返回 401,不区分“用户不存在”和“密码错误”。 public static class AuthEndpoints @@ -35,7 +35,7 @@ public static class AuthEndpoints string source = http.Connection.RemoteIpAddress?.ToString() ?? "unknown"; if (!limiter.TryAcquire(source, out int retryAfter)) { - return Problems.RetryAfter(http, new FleetError(MonitorErrors.TooManyRequests, "too many sign-in attempts"), retryAfter); + return Problems.RetryAfter(http, new FleetError(MoShiroErrors.TooManyRequests, "too many sign-in attempts"), retryAfter); } Result result = await auth.LoginAsync(request.Username ?? "", request.Password ?? "", http.RequestAborted); diff --git a/backend/src/Monitor.Backend.Api/Admin/BuildEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/BuildEndpoints.cs similarity index 96% rename from backend/src/Monitor.Backend.Api/Admin/BuildEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/BuildEndpoints.cs index c85ef3f..e59949a 100644 --- a/backend/src/Monitor.Backend.Api/Admin/BuildEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/BuildEndpoints.cs @@ -2,10 +2,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 构建信息与更新:后端和每个节点的版本、tag、构建时间,CI 签名的发布清单,以及「更新这个节点」的请求。更新本身由节点宿主机上的更新器验签并执行。 public static class BuildEndpoints @@ -54,7 +54,7 @@ public static class BuildEndpoints } private static async Task RequestUpdateAsync(HttpContext http, ReleaseService releases, string id, CancellationToken ct) => - await releases.RequestUpdateAsync(new NodeId(id), ct) ? TypedResults.NoContent() : Problems.Create(http, MonitorErrors.AgentOffline, "the node is not connected"); + await releases.RequestUpdateAsync(new NodeId(id), ct) ? TypedResults.NoContent() : Problems.Create(http, MoShiroErrors.AgentOffline, "the node is not connected"); private static ReleaseDto ToDto(ReleaseOffer offer) => new(offer.Component, offer.Seq, offer.Tag, offer.Version, offer.Digest); diff --git a/backend/src/Monitor.Backend.Api/Admin/DataEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/DataEndpoints.cs similarity index 94% rename from backend/src/Monitor.Backend.Api/Admin/DataEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/DataEndpoints.cs index d85927a..3dda979 100644 --- a/backend/src/Monitor.Backend.Api/Admin/DataEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/DataEndpoints.cs @@ -3,10 +3,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 原始数据检索与下载、事件、指标重算进度。 public static class DataEndpoints @@ -18,8 +18,8 @@ public static class DataEndpoints group.MapGet("/raw/chunks", ListChunksAsync) .Api("raw_chunks_list", "Raw chunk metadata", Tag).Returns>().ProducesProblems(400, 401, 403); group.MapGet("/raw/chunks/{chunk_id}", DownloadChunkAsync) - .Api("raw_chunks_get", "Download a raw chunk (application/vnd.monitor.rawchunk)", Tag) - .Produces(StatusCodes.Status200OK, contentType: "application/vnd.monitor.rawchunk").ProducesProblems(401, 403, 404, 410); + .Api("raw_chunks_get", "Download a raw chunk (application/vnd.moshiro.rawchunk)", Tag) + .Produces(StatusCodes.Status200OK, contentType: "application/vnd.moshiro.rawchunk").ProducesProblems(401, 403, 404, 410); group.MapGet("/events", ListEventsAsync) .Api("events_list", "Agent events", Tag).Returns>().ProducesProblems(400, 401, 403); group.MapGet("/analysis/recompute", RecomputeAsync) @@ -57,7 +57,7 @@ public static class DataEndpoints { if (!Guid.TryParse(chunk_id, out Guid id)) { - return Problems.Create(http, MonitorErrors.ChunkNotFound, "chunk not found"); + return Problems.Create(http, MoShiroErrors.ChunkNotFound, "chunk not found"); } Result<(RawChunkMeta Meta, byte[] Bytes)> result = await raw.GetAsync(new ChunkId(id), http.RequestAborted); @@ -67,7 +67,7 @@ public static class DataEndpoints } http.Response.Headers.CacheControl = "private, max-age=3600"; - return TypedResults.File(found.Bytes, "application/vnd.monitor.rawchunk", chunk_id + ".mnrc"); + return TypedResults.File(found.Bytes, "application/vnd.moshiro.rawchunk", chunk_id + ".msrc"); } private static async Task ListEventsAsync( diff --git a/backend/src/Monitor.Backend.Api/Admin/LinkEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/LinkEndpoints.cs similarity index 92% rename from backend/src/Monitor.Backend.Api/Admin/LinkEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/LinkEndpoints.cs index 5737f62..7ee75d6 100644 --- a/backend/src/Monitor.Backend.Api/Admin/LinkEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/LinkEndpoints.cs @@ -2,10 +2,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 线路开关:哪些(Probe,Agent)要连。自动连接的总开关是设置 fleet.auto_link;这里看每一对的结果,并且手动强制开、关或取消手动设置。 public static class LinkEndpoints @@ -40,12 +40,12 @@ public static class LinkEndpoints { if (await probes.GetAsync(probe_id, ct) is not { Revoked: false } probe) { - return Problems.Create(http, MonitorErrors.ProbeNotFound, "probe not found"); + return Problems.Create(http, MoShiroErrors.ProbeNotFound, "probe not found"); } if (await agents.GetAsync(new NodeId(agent_id), ct) is not { } agent) { - return Problems.Create(http, MonitorErrors.AgentNotFound, "agent not found"); + return Problems.Create(http, MoShiroErrors.AgentNotFound, "agent not found"); } await links.SetAsync(probe_id, agent_id, request.Enabled, ct); diff --git a/backend/src/Monitor.Backend.Api/Admin/SystemEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/SystemEndpoints.cs similarity index 98% rename from backend/src/Monitor.Backend.Api/Admin/SystemEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/SystemEndpoints.cs index d833295..00a6037 100644 --- a/backend/src/Monitor.Backend.Api/Admin/SystemEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/SystemEndpoints.cs @@ -3,10 +3,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// API 令牌、站点设置与保留期、用户。令牌明文只在创建的响应里出现一次。 public static class SystemEndpoints diff --git a/backend/src/Monitor.Backend.Api/Admin/TaskEndpoints.cs b/backend/src/MoShiro.Backend.Api/Admin/TaskEndpoints.cs similarity index 97% rename from backend/src/Monitor.Backend.Api/Admin/TaskEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Admin/TaskEndpoints.cs index eb30c46..66b8c41 100644 --- a/backend/src/Monitor.Backend.Api/Admin/TaskEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Admin/TaskEndpoints.cs @@ -2,10 +2,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 任务(线路模板、单个 Agent 的覆盖、目标任务)与按需测试。参数按 Kind 的 ParamSpec 校验,并检查带宽预算。 public static class TaskEndpoints @@ -76,7 +76,7 @@ public static class TaskEndpoints TaskRecord? task = await tasks.GetAsync(new TaskId(id), ct); if (task is null) { - return Problems.Create(http, MonitorErrors.TaskNotFound, "task not found"); + return Problems.Create(http, MoShiroErrors.TaskNotFound, "task not found"); } Concurrency.SetETag(http, task.RowVersion); diff --git a/backend/src/Monitor.Backend.Api/ApiModule.cs b/backend/src/MoShiro.Backend.Api/ApiModule.cs similarity index 93% rename from backend/src/Monitor.Backend.Api/ApiModule.cs rename to backend/src/MoShiro.Backend.Api/ApiModule.cs index bbab80b..6cb4d31 100644 --- a/backend/src/Monitor.Backend.Api/ApiModule.cs +++ b/backend/src/MoShiro.Backend.Api/ApiModule.cs @@ -5,17 +5,17 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Json; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// -/// Api 层的入口:服务注册、中间件、端点映射。新增资源时在 里加一行。 +/// Api 层的入口:服务注册、中间件、端点映射。新增资源时在 里加一行。 /// Api 只调用 Application;错误码到 HTTP 状态的映射只在这一层做一次()。 /// public static class ApiModule { - public static IServiceCollection AddMonitorApi(this IServiceCollection services) + public static IServiceCollection AddMoShiroApi(this IServiceCollection services) { services.AddSingleton(); services.Configure(options => @@ -48,7 +48,7 @@ public static class ApiModule /// 跨域(可选):只对 /api/public 和 /api/admin 生效,只放行名单里的来源,且不带凭据(Cookie 不会跨域), /// 所以跨域的前端只能用 Bearer 令牌。需要放在认证和路由之前。 /// - public static IApplicationBuilder UseMonitorCors(this IApplicationBuilder app, string? origins) + public static IApplicationBuilder UseMoShiroCors(this IApplicationBuilder app, string? origins) { string[] allowed = (origins ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (allowed.Length == 0) @@ -83,7 +83,7 @@ public static class ApiModule } /// 认证中间件:解析 Bearer 令牌或会话 Cookie。需要放在路由之后、端点之前。 - public static IApplicationBuilder UseMonitorApi(this IApplicationBuilder app) + public static IApplicationBuilder UseMoShiroApi(this IApplicationBuilder app) { app.UseExceptionHandler(); app.UseStatusCodePages(); @@ -91,7 +91,7 @@ public static class ApiModule return app; } - public static IEndpointRouteBuilder MapMonitorApi(this IEndpointRouteBuilder app) + public static IEndpointRouteBuilder MapMoShiroApi(this IEndpointRouteBuilder app) { RouteGroupBuilder publicGroup = app.MapGroup("/api/public").AddEndpointFilter(PublicGuard.InvokeAsync); QueryEndpoints.Map(publicGroup); diff --git a/backend/src/Monitor.Backend.Api/Common/Concurrency.cs b/backend/src/MoShiro.Backend.Api/Common/Concurrency.cs similarity index 97% rename from backend/src/Monitor.Backend.Api/Common/Concurrency.cs rename to backend/src/MoShiro.Backend.Api/Common/Concurrency.cs index 1916238..60bc8e7 100644 --- a/backend/src/Monitor.Backend.Api/Common/Concurrency.cs +++ b/backend/src/MoShiro.Backend.Api/Common/Concurrency.cs @@ -1,7 +1,7 @@ using System.Globalization; using Microsoft.AspNetCore.Http; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 并发控制:配置类资源返回 ETag(行版本),PATCH/DELETE 支持 If-Match,不匹配时返回 412。 internal static class Concurrency diff --git a/backend/src/Monitor.Backend.Api/Common/EndpointConventions.cs b/backend/src/MoShiro.Backend.Api/Common/EndpointConventions.cs similarity index 96% rename from backend/src/Monitor.Backend.Api/Common/EndpointConventions.cs rename to backend/src/MoShiro.Backend.Api/Common/EndpointConventions.cs index 9158ec2..f749fd0 100644 --- a/backend/src/Monitor.Backend.Api/Common/EndpointConventions.cs +++ b/backend/src/MoShiro.Backend.Api/Common/EndpointConventions.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; public static class EndpointConventions { diff --git a/backend/src/Monitor.Backend.Api/Common/Guards.cs b/backend/src/MoShiro.Backend.Api/Common/Guards.cs similarity index 90% rename from backend/src/Monitor.Backend.Api/Common/Guards.cs rename to backend/src/MoShiro.Backend.Api/Common/Guards.cs index 9e865c5..0be49b8 100644 --- a/backend/src/Monitor.Backend.Api/Common/Guards.cs +++ b/backend/src/MoShiro.Backend.Api/Common/Guards.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; public static class HttpContextPrincipalExtensions { - private const string Key = "monitor.principal"; + private const string Key = "moshiro.principal"; public static Principal GetPrincipal(this HttpContext http) => http.Items.TryGetValue(Key, out object? value) && value is Principal principal ? principal : Principal.Anonymous; @@ -20,7 +20,7 @@ public static class HttpContextPrincipalExtensions /// public static class PrincipalResolver { - public const string SessionCookie = "monitor_session"; + public const string SessionCookie = "moshiro_session"; public const string CsrfHeader = "X-CSRF-Token"; public static string? BearerOf(HttpContext http) @@ -63,8 +63,8 @@ public static class AdminGuard if (!principal.IsAdmin) { return principal.Scope != ApiScope.None - ? Problems.Create(http, MonitorErrors.Forbidden, "the token scope does not allow this operation") - : Problems.Create(http, MonitorErrors.Unauthorized, "authentication is required"); + ? Problems.Create(http, MoShiroErrors.Forbidden, "the token scope does not allow this operation") + : Problems.Create(http, MoShiroErrors.Unauthorized, "authentication is required"); } if (principal.Kind == PrincipalKind.Session && !HttpMethods.IsGet(http.Request.Method) && !HttpMethods.IsHead(http.Request.Method) && !HttpMethods.IsOptions(http.Request.Method)) @@ -72,7 +72,7 @@ public static class AdminGuard string? csrf = http.Request.Headers[PrincipalResolver.CsrfHeader]; if (csrf is null || principal.CsrfToken is null || !AuthService.FixedTimeEquals(csrf, principal.CsrfToken)) { - return Problems.Create(http, MonitorErrors.CsrfFailed, "a valid X-CSRF-Token header is required"); + return Problems.Create(http, MoShiroErrors.CsrfFailed, "a valid X-CSRF-Token header is required"); } } @@ -95,7 +95,7 @@ public static class PublicGuard HttpContext http = context.HttpContext; if (!http.GetPrincipal().CanRead && !await http.RequestServices.GetRequiredService().IsSitePublicAsync(http.RequestAborted)) { - return Problems.Create(http, MonitorErrors.SitePrivate, "this site is private; sign in or use a read-only token"); + return Problems.Create(http, MoShiroErrors.SitePrivate, "this site is private; sign in or use a read-only token"); } return await next(context); diff --git a/backend/src/Monitor.Backend.Api/Common/Mappers.cs b/backend/src/MoShiro.Backend.Api/Common/Mappers.cs similarity index 98% rename from backend/src/Monitor.Backend.Api/Common/Mappers.cs rename to backend/src/MoShiro.Backend.Api/Common/Mappers.cs index 98ea0d1..39fd43e 100644 --- a/backend/src/Monitor.Backend.Api/Common/Mappers.cs +++ b/backend/src/MoShiro.Backend.Api/Common/Mappers.cs @@ -1,10 +1,10 @@ using System.Text.Json; using Fleet.Server; -using Monitor.Backend.Analysis; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 应用层模型 → REST DTO。public 层的 DTO 只暴露可以公开的字段。 public static class Mappers diff --git a/backend/src/Monitor.Backend.Api/Common/Problems.cs b/backend/src/MoShiro.Backend.Api/Common/Problems.cs similarity index 71% rename from backend/src/Monitor.Backend.Api/Common/Problems.cs rename to backend/src/MoShiro.Backend.Api/Common/Problems.cs index 841ca91..87576e9 100644 --- a/backend/src/Monitor.Backend.Api/Common/Problems.cs +++ b/backend/src/MoShiro.Backend.Api/Common/Problems.cs @@ -1,10 +1,10 @@ using System.Diagnostics; using Fleet.Core; using Microsoft.AspNetCore.Http; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 错误码到 HTTP 状态的映射:只在 Api 层做一次。 public static class ErrorCodeHttpMap @@ -44,19 +44,19 @@ public static class ErrorCodeHttpMap } } - Add(400, MonitorErrors.ValidationFailed, MonitorErrors.BudgetExceeded, FleetErrors.InvalidParams); - Add(401, MonitorErrors.Unauthorized, MonitorErrors.CsrfFailed, FleetErrors.Unauthorized); - Add(403, MonitorErrors.Forbidden, MonitorErrors.SitePrivate, FleetErrors.Forbidden, FleetErrors.ProofInvalid); + Add(400, MoShiroErrors.ValidationFailed, MoShiroErrors.BudgetExceeded, FleetErrors.InvalidParams); + Add(401, MoShiroErrors.Unauthorized, MoShiroErrors.CsrfFailed, FleetErrors.Unauthorized); + Add(403, MoShiroErrors.Forbidden, MoShiroErrors.SitePrivate, FleetErrors.Forbidden, FleetErrors.ProofInvalid); Add( - 404, MonitorErrors.NotFound, MonitorErrors.AgentNotFound, MonitorErrors.ProbeNotFound, MonitorErrors.TaskNotFound, MonitorErrors.TestNotFound, MonitorErrors.ChunkNotFound, - MonitorErrors.TokenNotFound, MonitorErrors.UnknownKind, MonitorErrors.UnknownPlan, FleetErrors.NotFound); - Add(409, MonitorErrors.NameConflict, MonitorErrors.ProbeRevoked, MonitorErrors.ProbeOffline, MonitorErrors.TestBusy, MonitorErrors.AgentNotActive, MonitorErrors.AgentOffline, FleetErrors.NameConflict, FleetErrors.NodeOffline); - Add(410, MonitorErrors.RawExpired, MonitorErrors.JoinTokenExpired, FleetErrors.JoinTokenExpired); - Add(412, MonitorErrors.PreconditionFailed); - Add(413, MonitorErrors.PayloadTooLarge); - Add(422, MonitorErrors.ProbeRequired, MonitorErrors.RangeTooLarge, MonitorErrors.InvalidStep, MonitorErrors.InvalidRange, MonitorErrors.UnknownMetric, MonitorErrors.UnknownPreset, MonitorErrors.TooManyMetrics, FleetErrors.Unsupported); - Add(429, MonitorErrors.TooManyRequests, MonitorErrors.TooManyStreams); - Add(503, MonitorErrors.StorageUnavailable, FleetErrors.Timeout); + 404, MoShiroErrors.NotFound, MoShiroErrors.AgentNotFound, MoShiroErrors.ProbeNotFound, MoShiroErrors.TaskNotFound, MoShiroErrors.TestNotFound, MoShiroErrors.ChunkNotFound, + MoShiroErrors.TokenNotFound, MoShiroErrors.UnknownKind, MoShiroErrors.UnknownPlan, FleetErrors.NotFound); + Add(409, MoShiroErrors.NameConflict, MoShiroErrors.ProbeRevoked, MoShiroErrors.ProbeOffline, MoShiroErrors.TestBusy, MoShiroErrors.AgentNotActive, MoShiroErrors.AgentOffline, FleetErrors.NameConflict, FleetErrors.NodeOffline); + Add(410, MoShiroErrors.RawExpired, MoShiroErrors.JoinTokenExpired, FleetErrors.JoinTokenExpired); + Add(412, MoShiroErrors.PreconditionFailed); + Add(413, MoShiroErrors.PayloadTooLarge); + Add(422, MoShiroErrors.ProbeRequired, MoShiroErrors.RangeTooLarge, MoShiroErrors.InvalidStep, MoShiroErrors.InvalidRange, MoShiroErrors.UnknownMetric, MoShiroErrors.UnknownPreset, MoShiroErrors.TooManyMetrics, FleetErrors.Unsupported); + Add(429, MoShiroErrors.TooManyRequests, MoShiroErrors.TooManyStreams); + Add(503, MoShiroErrors.StorageUnavailable, FleetErrors.Timeout); map[NotImplemented.Value] = 501; return map; } diff --git a/backend/src/Monitor.Backend.Api/Common/QueryHelpers.cs b/backend/src/MoShiro.Backend.Api/Common/QueryHelpers.cs similarity index 93% rename from backend/src/Monitor.Backend.Api/Common/QueryHelpers.cs rename to backend/src/MoShiro.Backend.Api/Common/QueryHelpers.cs index f6e8ef8..f37b141 100644 --- a/backend/src/Monitor.Backend.Api/Common/QueryHelpers.cs +++ b/backend/src/MoShiro.Backend.Api/Common/QueryHelpers.cs @@ -3,10 +3,10 @@ using System.Text; using Fleet.Core; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; internal static class QueryHelpers { @@ -22,7 +22,7 @@ internal static class QueryHelpers } catch (OperationCanceledException) when (!http.RequestAborted.IsCancellationRequested) { - return Problems.Create(http, MonitorErrors.StorageUnavailable, null); + return Problems.Create(http, MoShiroErrors.StorageUnavailable, null); } } @@ -81,7 +81,7 @@ internal static class QueryHelpers return line.Agent.Agent.Status == Fleet.Server.NodeStatus.Active && (line.Agent.Agent.PublicVisible || PublicGuard.CanSeeHidden(http)) && line.Probe.RevokedAtMs is null ? found - : Result.Fail(MonitorErrors.AgentNotFound, "line not found"); + : Result.Fail(MoShiroErrors.AgentNotFound, "line not found"); } /// 找到可以展示的线路:已激活,并且(对外可见,或者访问者已认证)。否则 404(不暴露被隐藏的线路存在与否)。 @@ -90,6 +90,6 @@ internal static class QueryHelpers AgentView? view = await agents.GetAsync(new NodeId(id), ct); return view is { Agent.Status: Fleet.Server.NodeStatus.Active } && (view.Agent.PublicVisible || PublicGuard.CanSeeHidden(http)) ? Result.Ok(view) - : Result.Fail(MonitorErrors.AgentNotFound, "agent not found"); + : Result.Fail(MoShiroErrors.AgentNotFound, "agent not found"); } } diff --git a/backend/src/Monitor.Backend.Api/Contracts/AdminDtos.cs b/backend/src/MoShiro.Backend.Api/Contracts/AdminDtos.cs similarity index 99% rename from backend/src/Monitor.Backend.Api/Contracts/AdminDtos.cs rename to backend/src/MoShiro.Backend.Api/Contracts/AdminDtos.cs index 45236ab..d01021e 100644 --- a/backend/src/Monitor.Backend.Api/Contracts/AdminDtos.cs +++ b/backend/src/MoShiro.Backend.Api/Contracts/AdminDtos.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// return_session:跨域的前端用——不设 Cookie,把会话令牌放在响应里,之后用 Authorization: Bearer 带上。 public sealed record LoginRequest(string Username, string Password, bool? ReturnSession); diff --git a/backend/src/Monitor.Backend.Api/Contracts/ApiJsonContext.cs b/backend/src/MoShiro.Backend.Api/Contracts/ApiJsonContext.cs similarity index 97% rename from backend/src/Monitor.Backend.Api/Contracts/ApiJsonContext.cs rename to backend/src/MoShiro.Backend.Api/Contracts/ApiJsonContext.cs index 8a355b9..2ce9de8 100644 --- a/backend/src/Monitor.Backend.Api/Contracts/ApiJsonContext.cs +++ b/backend/src/MoShiro.Backend.Api/Contracts/ApiJsonContext.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// /// REST 的 JSON 源生成上下文(snake_case,字段始终存在、未知值写 null,枚举与时间的格式见数据表示规则)。 @@ -80,6 +80,6 @@ namespace Monitor.Backend.Api; [JsonSerializable(typeof(CreateUserRequest))] [JsonSerializable(typeof(ChangePasswordRequest))] [JsonSerializable(typeof(RecomputeDto))] -[JsonSerializable(typeof(Monitor.Protocol.LocalLimits))] +[JsonSerializable(typeof(MoShiro.Protocol.LocalLimits))] [JsonSerializable(typeof(List))] public sealed partial class ApiJsonContext : JsonSerializerContext; diff --git a/backend/src/Monitor.Backend.Api/Contracts/CommonDtos.cs b/backend/src/MoShiro.Backend.Api/Contracts/CommonDtos.cs similarity index 97% rename from backend/src/Monitor.Backend.Api/Contracts/CommonDtos.cs rename to backend/src/MoShiro.Backend.Api/Contracts/CommonDtos.cs index 0095b20..ea982a1 100644 --- a/backend/src/Monitor.Backend.Api/Contracts/CommonDtos.cs +++ b/backend/src/MoShiro.Backend.Api/Contracts/CommonDtos.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 时间点统一使用 RFC 3339 UTC,以 Z 结尾,精确到毫秒(唯一例外:列式时序里的时间轴 t 是 Unix 毫秒)。 public sealed class Rfc3339Converter : JsonConverter diff --git a/backend/src/Monitor.Backend.Api/Contracts/PublicDtos.cs b/backend/src/MoShiro.Backend.Api/Contracts/PublicDtos.cs similarity index 99% rename from backend/src/Monitor.Backend.Api/Contracts/PublicDtos.cs rename to backend/src/MoShiro.Backend.Api/Contracts/PublicDtos.cs index c52543e..4c1cdf0 100644 --- a/backend/src/Monitor.Backend.Api/Contracts/PublicDtos.cs +++ b/backend/src/MoShiro.Backend.Api/Contracts/PublicDtos.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 汇总:整个时间段由累加器精确合并得出的值(不是各点的平均)。 public sealed record SummaryDto(IReadOnlyDictionary Metrics, IReadOnlyDictionary Samples, double Coverage); diff --git a/backend/src/Monitor.Backend.Api/Monitor.Backend.Api.csproj b/backend/src/MoShiro.Backend.Api/MoShiro.Backend.Api.csproj similarity index 76% rename from backend/src/Monitor.Backend.Api/Monitor.Backend.Api.csproj rename to backend/src/MoShiro.Backend.Api/MoShiro.Backend.Api.csproj index 95eddf2..0111063 100644 --- a/backend/src/Monitor.Backend.Api/Monitor.Backend.Api.csproj +++ b/backend/src/MoShiro.Backend.Api/MoShiro.Backend.Api.csproj @@ -6,6 +6,6 @@ - + diff --git a/backend/src/Monitor.Backend.Api/OpsEndpoints.cs b/backend/src/MoShiro.Backend.Api/OpsEndpoints.cs similarity index 91% rename from backend/src/Monitor.Backend.Api/OpsEndpoints.cs rename to backend/src/MoShiro.Backend.Api/OpsEndpoints.cs index 1e51dbc..75779be 100644 --- a/backend/src/Monitor.Backend.Api/OpsEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/OpsEndpoints.cs @@ -1,10 +1,10 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 运维端点:/healthz、/metrics(admin 令牌)、安装脚本。 public static class OpsEndpoints @@ -40,8 +40,8 @@ public static class OpsEndpoints if (!principal.IsAdmin) { return principal.CanRead - ? Problems.Create(http, MonitorErrors.Forbidden, "an admin token is required") - : Problems.Create(http, MonitorErrors.Unauthorized, "an admin token is required"); + ? Problems.Create(http, MoShiroErrors.Forbidden, "an admin token is required") + : Problems.Create(http, MoShiroErrors.Unauthorized, "an admin token is required"); } return TypedResults.Text(await ops.RenderPrometheusAsync(http.RequestAborted), "text/plain; version=0.0.4"); diff --git a/backend/src/Monitor.Backend.Api/Public/CatalogEndpoints.cs b/backend/src/MoShiro.Backend.Api/Public/CatalogEndpoints.cs similarity index 90% rename from backend/src/Monitor.Backend.Api/Public/CatalogEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Public/CatalogEndpoints.cs index 34f1779..b2f6e9c 100644 --- a/backend/src/Monitor.Backend.Api/Public/CatalogEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Public/CatalogEndpoints.cs @@ -1,10 +1,10 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 目录:由代码里的注册表生成。新增 Kind、方案、指标、分组、模型后,目录自动更新,前端不需要改。 public static class CatalogEndpoints @@ -27,12 +27,12 @@ public static class CatalogEndpoints } private static IResult Kinds(HttpContext http) => - QueryHelpers.ApplyCatalogCaching(http, string.Join(',', Monitor.Protocol.Kinds.All.Select(k => k.Id))) - ?? TypedResults.Json(new PageDto([.. Monitor.Protocol.Kinds.All.Select(Mappers.ToDto)], null), ApiJsonContext.Default.PageDtoKindDto); + QueryHelpers.ApplyCatalogCaching(http, string.Join(',', MoShiro.Protocol.Kinds.All.Select(k => k.Id))) + ?? TypedResults.Json(new PageDto([.. MoShiro.Protocol.Kinds.All.Select(Mappers.ToDto)], null), ApiJsonContext.Default.PageDtoKindDto); private static IResult Plans(HttpContext http) => - QueryHelpers.ApplyCatalogCaching(http, string.Join(',', Monitor.Backend.Application.Plans.All.Select(p => p.Id))) - ?? TypedResults.Json(new PageDto([.. Monitor.Backend.Application.Plans.All.Select(Mappers.ToDto)], null), ApiJsonContext.Default.PageDtoPlanDto); + QueryHelpers.ApplyCatalogCaching(http, string.Join(',', MoShiro.Backend.Application.Plans.All.Select(p => p.Id))) + ?? TypedResults.Json(new PageDto([.. MoShiro.Backend.Application.Plans.All.Select(Mappers.ToDto)], null), ApiJsonContext.Default.PageDtoPlanDto); private static IResult Metrics(HttpContext http) => QueryHelpers.ApplyCatalogCaching(http, string.Join(',', MetricRegistry.All.Select(m => m.Fingerprint))) diff --git a/backend/src/Monitor.Backend.Api/Public/HostEndpoints.cs b/backend/src/MoShiro.Backend.Api/Public/HostEndpoints.cs similarity index 95% rename from backend/src/Monitor.Backend.Api/Public/HostEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Public/HostEndpoints.cs index 1430e74..1dc2031 100644 --- a/backend/src/Monitor.Backend.Api/Public/HostEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Public/HostEndpoints.cs @@ -2,10 +2,10 @@ using Fleet.Core; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// Agent 的主机资源占用:最新值(实时的部分走 SSE 的 hosts 主题)和历史曲线。只有汇总的数字,没有型号和 IP。 public static class HostEndpoints @@ -44,12 +44,12 @@ public static class HostEndpoints AgentView? view = await agents.GetAsync(new NodeId(agent_id), ct); if (view is not { Agent.Status: Fleet.Server.NodeStatus.Active } || !(view.Agent.PublicVisible || PublicGuard.CanSeeHidden(http))) { - return Problems.Create(http, MonitorErrors.AgentNotFound, "agent not found"); + return Problems.Create(http, MoShiroErrors.AgentNotFound, "agent not found"); } if (!RangeResolver.TryParseSpan(range ?? "1h", out long span) || span < Steps.Hour || span > 30 * Steps.Day) { - return Problems.From(http, new FleetError(MonitorErrors.InvalidRange, "range must look like 1h, 6h, 24h or 7d (between 1 hour and 30 days)")); + return Problems.From(http, new FleetError(MoShiroErrors.InvalidRange, "range must look like 1h, 6h, 24h or 7d (between 1 hour and 30 days)")); } long now = time.GetUtcNow().ToUnixTimeMilliseconds(); diff --git a/backend/src/Monitor.Backend.Api/Public/LiveEndpoints.cs b/backend/src/MoShiro.Backend.Api/Public/LiveEndpoints.cs similarity index 99% rename from backend/src/Monitor.Backend.Api/Public/LiveEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Public/LiveEndpoints.cs index 8c6f2cb..b2de354 100644 --- a/backend/src/Monitor.Backend.Api/Public/LiveEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Public/LiveEndpoints.cs @@ -7,10 +7,10 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Primitives; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// /// 实时推送(SSE):先推送当前快照,再推送增量(不补发历史)。SSE 只负责“更快地获得变化”, @@ -67,7 +67,7 @@ public static class LiveEndpoints string client = http.Connection.RemoteIpAddress?.ToString() ?? "unknown"; if (!limiter.TryAcquire(client)) { - return Problems.Create(http, MonitorErrors.TooManyStreams, $"at most {options.MaxSsePerClient} live connections per client"); + return Problems.Create(http, MoShiroErrors.TooManyStreams, $"at most {options.MaxSsePerClient} live connections per client"); } counters.SseOpened(); diff --git a/backend/src/Monitor.Backend.Api/Public/LiveTopics.cs b/backend/src/MoShiro.Backend.Api/Public/LiveTopics.cs similarity index 95% rename from backend/src/Monitor.Backend.Api/Public/LiveTopics.cs rename to backend/src/MoShiro.Backend.Api/Public/LiveTopics.cs index d483eff..c9c3e31 100644 --- a/backend/src/Monitor.Backend.Api/Public/LiveTopics.cs +++ b/backend/src/MoShiro.Backend.Api/Public/LiveTopics.cs @@ -1,9 +1,9 @@ using System.Collections.Concurrent; using Fleet.Core; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; internal enum LiveTopicKind { @@ -46,7 +46,7 @@ internal sealed record LiveTopic(LiveTopicKind Kind, string Raw, string? Range, if (tokens.Count > maxTopics) { - return Result>.Fail(MonitorErrors.TooManyStreams, $"at most {maxTopics} topics per connection"); + return Result>.Fail(MoShiroErrors.TooManyStreams, $"at most {maxTopics} topics per connection"); } var topics = new List(); @@ -83,7 +83,7 @@ internal sealed record LiveTopic(LiveTopicKind Kind, string Raw, string? Range, case "series" when parts.Length == 4: if (!Steps.TryParse(parts[2], out long step) || step > Steps.Minute) { - return Result.Fail(MonitorErrors.InvalidStep, $"{token}: series topics support steps up to 1m; refetch coarser steps over REST"); + return Result.Fail(MoShiroErrors.InvalidStep, $"{token}: series topics support steps up to 1m; refetch coarser steps over REST"); } return Result.Ok(new LiveTopic(LiveTopicKind.Series, token, null, step, parts[3], parts[1], null)); diff --git a/backend/src/Monitor.Backend.Api/Public/NodeEndpoints.cs b/backend/src/MoShiro.Backend.Api/Public/NodeEndpoints.cs similarity index 92% rename from backend/src/Monitor.Backend.Api/Public/NodeEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Public/NodeEndpoints.cs index 35da907..e71ba87 100644 --- a/backend/src/Monitor.Backend.Api/Public/NodeEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Public/NodeEndpoints.cs @@ -1,9 +1,9 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// /// 节点的展示信息:系统、架构、国家、运营商。只有这几项,没有任何 IP 地址、城市、ASN、经纬度—— @@ -25,7 +25,7 @@ public static class NodeEndpoints /// 这个后端自己的构建信息;没有(本地构建)的字段是 null。 internal static BuildDto Backend(bool withCommit) => - new(Monitor.Protocol.BuildInfo.Version, Empty(Monitor.Protocol.BuildInfo.Tag), withCommit ? Empty(Monitor.Protocol.BuildInfo.Commit) : null, Empty(Monitor.Protocol.BuildInfo.BuiltAt)); + new(MoShiro.Protocol.BuildInfo.Version, Empty(MoShiro.Protocol.BuildInfo.Tag), withCommit ? Empty(MoShiro.Protocol.BuildInfo.Commit) : null, Empty(MoShiro.Protocol.BuildInfo.BuiltAt)); private static string? Empty(string value) => value.Length == 0 ? null : value; diff --git a/backend/src/Monitor.Backend.Api/Public/QueryEndpoints.cs b/backend/src/MoShiro.Backend.Api/Public/QueryEndpoints.cs similarity index 98% rename from backend/src/Monitor.Backend.Api/Public/QueryEndpoints.cs rename to backend/src/MoShiro.Backend.Api/Public/QueryEndpoints.cs index dcadd8d..9c8be7d 100644 --- a/backend/src/Monitor.Backend.Api/Public/QueryEndpoints.cs +++ b/backend/src/MoShiro.Backend.Api/Public/QueryEndpoints.cs @@ -3,11 +3,11 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; -using Monitor.Backend.Analysis; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Api; +namespace MoShiro.Backend.Api; /// 展示数据的查询端点:参数解析、校验与缓存头,业务都在 Application.Query。 public static class QueryEndpoints @@ -211,13 +211,13 @@ public static class QueryEndpoints TestDetail? detail = await tests.GetAsync(new TestId(test_id), ct); if (detail is null) { - return Problems.Create(http, MonitorErrors.TestNotFound, "test not found"); + return Problems.Create(http, MoShiroErrors.TestNotFound, "test not found"); } Result visible = await QueryHelpers.FindVisibleAgentAsync(http, agents, detail.Test.AgentId.Value, ct); if (!visible.IsSuccess) { - return Problems.Create(http, MonitorErrors.TestNotFound, "test not found"); + return Problems.Create(http, MoShiroErrors.TestNotFound, "test not found"); } return TypedResults.Json(Mappers.ToDto(detail), ApiJsonContext.Default.TestDto); diff --git a/backend/src/Monitor.Backend.Application/Admin/EventService.cs b/backend/src/MoShiro.Backend.Application/Admin/EventService.cs similarity index 91% rename from backend/src/Monitor.Backend.Application/Admin/EventService.cs rename to backend/src/MoShiro.Backend.Application/Admin/EventService.cs index 1c4ff5d..0f59b3b 100644 --- a/backend/src/Monitor.Backend.Application/Admin/EventService.cs +++ b/backend/src/MoShiro.Backend.Application/Admin/EventService.cs @@ -1,6 +1,6 @@ using Fleet.Core; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed class EventService { diff --git a/backend/src/Monitor.Backend.Application/Admin/JoinTokenService.cs b/backend/src/MoShiro.Backend.Application/Admin/JoinTokenService.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Admin/JoinTokenService.cs rename to backend/src/MoShiro.Backend.Application/Admin/JoinTokenService.cs index 813bca0..382267e 100644 --- a/backend/src/Monitor.Backend.Application/Admin/JoinTokenService.cs +++ b/backend/src/MoShiro.Backend.Application/Admin/JoinTokenService.cs @@ -1,6 +1,6 @@ using Fleet.Core; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record CreateJoinTokenRequest(string? Note, IReadOnlyList? Labels, bool AutoApprove, int MaxUses, int ExpiresInHours); diff --git a/backend/src/Monitor.Backend.Application/Admin/OpsService.cs b/backend/src/MoShiro.Backend.Application/Admin/OpsService.cs similarity index 75% rename from backend/src/Monitor.Backend.Application/Admin/OpsService.cs rename to backend/src/MoShiro.Backend.Application/Admin/OpsService.cs index 13ebe0f..913b85a 100644 --- a/backend/src/Monitor.Backend.Application/Admin/OpsService.cs +++ b/backend/src/MoShiro.Backend.Application/Admin/OpsService.cs @@ -1,7 +1,7 @@ using System.Globalization; using System.Text; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 运维:健康检查和 Prometheus 指标(在线数、块入库速率、分析队列长度、重算进度、磁盘占用、SSE 连接数)。 public sealed class OpsService @@ -40,17 +40,17 @@ public sealed class OpsService } RecomputeProgress progress = _recompute.Progress; - Metric("monitor_agents_online", "Agents with an open control connection.", _gateway.OnlineAgents.Count); - Metric("monitor_chunks_ingested_total", "Raw chunks stored.", _counters.ChunksIngested, "counter"); - Metric("monitor_chunks_duplicate_total", "Raw chunks received more than once.", _counters.ChunksDuplicate, "counter"); - Metric("monitor_chunks_discarded_total", "Raw chunks rejected as invalid.", _counters.ChunksDiscarded, "counter"); - Metric("monitor_ingest_bytes_total", "Bytes of raw chunks stored.", _counters.BytesIngested, "counter"); - Metric("monitor_windows_analyzed_total", "Minute windows computed.", _counters.WindowsAnalyzed, "counter"); - Metric("monitor_analysis_queue_length", "Minute windows waiting to be computed.", await _metrics.QueueLengthAsync(ct)); - Metric("monitor_recompute_windows_done", "Windows recomputed by the current backfill.", progress.WindowsDone); - Metric("monitor_recompute_windows_total", "Windows to recompute in the current backfill.", progress.WindowsTotal); - Metric("monitor_raw_bytes", "Disk used by raw segment files.", _raw.TotalBytes); - Metric("monitor_sse_connections", "Open server-sent event connections.", _counters.SseConnections); + Metric("moshiro_agents_online", "Agents with an open control connection.", _gateway.OnlineAgents.Count); + Metric("moshiro_chunks_ingested_total", "Raw chunks stored.", _counters.ChunksIngested, "counter"); + Metric("moshiro_chunks_duplicate_total", "Raw chunks received more than once.", _counters.ChunksDuplicate, "counter"); + Metric("moshiro_chunks_discarded_total", "Raw chunks rejected as invalid.", _counters.ChunksDiscarded, "counter"); + Metric("moshiro_ingest_bytes_total", "Bytes of raw chunks stored.", _counters.BytesIngested, "counter"); + Metric("moshiro_windows_analyzed_total", "Minute windows computed.", _counters.WindowsAnalyzed, "counter"); + Metric("moshiro_analysis_queue_length", "Minute windows waiting to be computed.", await _metrics.QueueLengthAsync(ct)); + Metric("moshiro_recompute_windows_done", "Windows recomputed by the current backfill.", progress.WindowsDone); + Metric("moshiro_recompute_windows_total", "Windows to recompute in the current backfill.", progress.WindowsTotal); + Metric("moshiro_raw_bytes", "Disk used by raw segment files.", _raw.TotalBytes); + Metric("moshiro_sse_connections", "Open server-sent event connections.", _counters.SseConnections); return text.ToString(); } } diff --git a/backend/src/Monitor.Backend.Application/Admin/RawBrowserService.cs b/backend/src/MoShiro.Backend.Application/Admin/RawBrowserService.cs similarity index 88% rename from backend/src/Monitor.Backend.Application/Admin/RawBrowserService.cs rename to backend/src/MoShiro.Backend.Application/Admin/RawBrowserService.cs index b69fa06..e3541b3 100644 --- a/backend/src/Monitor.Backend.Application/Admin/RawBrowserService.cs +++ b/backend/src/MoShiro.Backend.Application/Admin/RawBrowserService.cs @@ -1,7 +1,7 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 原始数据的检索与下载(管理员)。块过了保留期返回 raw_expired,从来不存在返回 chunk_not_found。 public sealed class RawBrowserService @@ -26,7 +26,7 @@ public sealed class RawBrowserService { byte[]? bytes = await _raw.ReadAsync(meta, ct); return bytes is null - ? Result<(RawChunkMeta, byte[])>.Fail(MonitorErrors.RawExpired, "the raw data has expired") + ? Result<(RawChunkMeta, byte[])>.Fail(MoShiroErrors.RawExpired, "the raw data has expired") : Result<(RawChunkMeta, byte[])>.Ok((meta, bytes)); } @@ -34,8 +34,8 @@ public sealed class RawBrowserService int days = await _settings.GetIntAsync(SettingsService.TestRawDays, ct); long cutoff = _time.GetUtcNow().ToUnixTimeMilliseconds() - (days * 86_400_000L); return ChunkTimestampMs(id.Value) is { } created && created < cutoff - ? Result<(RawChunkMeta, byte[])>.Fail(MonitorErrors.RawExpired, "the raw data has expired") - : Result<(RawChunkMeta, byte[])>.Fail(MonitorErrors.ChunkNotFound, "chunk not found"); + ? Result<(RawChunkMeta, byte[])>.Fail(MoShiroErrors.RawExpired, "the raw data has expired") + : Result<(RawChunkMeta, byte[])>.Fail(MoShiroErrors.ChunkNotFound, "chunk not found"); } /// UUIDv7 的前 48 位是毫秒时间戳。 diff --git a/backend/src/Monitor.Backend.Application/Admin/SettingsService.cs b/backend/src/MoShiro.Backend.Application/Admin/SettingsService.cs similarity index 99% rename from backend/src/Monitor.Backend.Application/Admin/SettingsService.cs rename to backend/src/MoShiro.Backend.Application/Admin/SettingsService.cs index eb05808..1577ec3 100644 --- a/backend/src/Monitor.Backend.Application/Admin/SettingsService.cs +++ b/backend/src/MoShiro.Backend.Application/Admin/SettingsService.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.Text.Json; using Fleet.Core; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record SettingSpec(string Key, string Type, string Default, long Min, long Max, string Description); diff --git a/backend/src/Monitor.Backend.Application/Agents/AgentEventService.cs b/backend/src/MoShiro.Backend.Application/Agents/AgentEventService.cs similarity index 93% rename from backend/src/Monitor.Backend.Application/Agents/AgentEventService.cs rename to backend/src/MoShiro.Backend.Application/Agents/AgentEventService.cs index 957624c..8f50d8b 100644 --- a/backend/src/Monitor.Backend.Application/Agents/AgentEventService.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/AgentEventService.cs @@ -1,9 +1,9 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; -/// monitor.event:Agent 报告的事件(Probe 连上/断开、端点变化、时钟跳变、缺口、CPU 饱和)。 +/// moshiro.event:Agent 报告的事件(Probe 连上/断开、端点变化、时钟跳变、缺口、CPU 饱和)。 public sealed class AgentEventService { private readonly AgentRuntime _runtime; diff --git a/backend/src/Monitor.Backend.Application/Agents/AgentRuntime.cs b/backend/src/MoShiro.Backend.Application/Agents/AgentRuntime.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Agents/AgentRuntime.cs rename to backend/src/MoShiro.Backend.Application/Agents/AgentRuntime.cs index 98e7738..6fe7134 100644 --- a/backend/src/Monitor.Backend.Application/Agents/AgentRuntime.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/AgentRuntime.cs @@ -1,8 +1,8 @@ using System.Collections.Concurrent; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// Agent 眼中的一个已连接 Probe(Agent 在事件和心跳里上报)。 public sealed record ConnectedProbeInfo(string Id, string? Version, string? Remote, long SinceMs); diff --git a/backend/src/Monitor.Backend.Application/Agents/AgentService.cs b/backend/src/MoShiro.Backend.Application/Agents/AgentService.cs similarity index 99% rename from backend/src/Monitor.Backend.Application/Agents/AgentService.cs rename to backend/src/MoShiro.Backend.Application/Agents/AgentService.cs index 8c95cc2..5b09036 100644 --- a/backend/src/Monitor.Backend.Application/Agents/AgentService.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/AgentService.cs @@ -1,9 +1,9 @@ using System.Text.RegularExpressions; using Fleet.Core; using Fleet.Server; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record AgentView(AgentRecord Agent, bool Online, IReadOnlyList Probes, AgentHeartbeatExtra? Heartbeat, int EndpointChanges, long? HeartbeatAtMs = null, HostSnapshot? Host = null) { diff --git a/backend/src/Monitor.Backend.Application/Agents/HostStatusService.cs b/backend/src/MoShiro.Backend.Application/Agents/HostStatusService.cs similarity index 94% rename from backend/src/Monitor.Backend.Application/Agents/HostStatusService.cs rename to backend/src/MoShiro.Backend.Application/Agents/HostStatusService.cs index 284fa15..2c7c0df 100644 --- a/backend/src/Monitor.Backend.Application/Agents/HostStatusService.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/HostStatusService.cs @@ -1,10 +1,10 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// -/// monitor.host:Agent 每隔几秒上报的主机资源占用快照。最新值放在内存里并实时推给订阅者;每个 Agent 每分钟存一个样本做历史。 +/// moshiro.host:Agent 每隔几秒上报的主机资源占用快照。最新值放在内存里并实时推给订阅者;每个 Agent 每分钟存一个样本做历史。 /// 快照里没有 CPU 型号、主机名、网卡名和 IP(Agent 根本不采集它们)。 /// public sealed class HostStatusService diff --git a/backend/src/Monitor.Backend.Application/Agents/LinkService.cs b/backend/src/MoShiro.Backend.Application/Agents/LinkService.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Agents/LinkService.cs rename to backend/src/MoShiro.Backend.Application/Agents/LinkService.cs index 63347d9..0ee14ea 100644 --- a/backend/src/Monitor.Backend.Application/Agents/LinkService.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/LinkService.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 某一时刻的「线路」开关:自动连接的总开关,加上每一对(Probe,Agent)的手动设置。 public sealed class LinkSnapshot diff --git a/backend/src/Monitor.Backend.Application/Agents/MonitorFleetHooks.cs b/backend/src/MoShiro.Backend.Application/Agents/MonitorFleetHooks.cs similarity index 90% rename from backend/src/Monitor.Backend.Application/Agents/MonitorFleetHooks.cs rename to backend/src/MoShiro.Backend.Application/Agents/MonitorFleetHooks.cs index 380b9e6..492bc1f 100644 --- a/backend/src/Monitor.Backend.Application/Agents/MonitorFleetHooks.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/MonitorFleetHooks.cs @@ -2,17 +2,17 @@ using System.Text.Json; using Fleet.Core; using Fleet.Rpc; using Fleet.Server; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; -/// Monitor 在 Fleet 之上的接入点:注册之后补全公开地址与端口。 -public sealed class MonitorEnrollmentHook : IEnrollmentHook +/// MoShiro 在 Fleet 之上的接入点:注册之后补全公开地址与端口。 +public sealed class MoShiroEnrollmentHook : IEnrollmentHook { private readonly IAgentStore _agents; private readonly AgentService _service; - public MonitorEnrollmentHook(IAgentStore agents, AgentService service) + public MoShiroEnrollmentHook(IAgentStore agents, AgentService service) { _agents = agents; _service = service; @@ -25,7 +25,7 @@ public sealed class MonitorEnrollmentHook : IEnrollmentHook { try { - meta = raw.Deserialize(MonitorJsonContext.Default.AgentEnrollMeta) ?? meta; + meta = raw.Deserialize(MoShiroJsonContext.Default.AgentEnrollMeta) ?? meta; } catch (JsonException) { @@ -44,7 +44,7 @@ public sealed class MonitorEnrollmentHook : IEnrollmentHook } /// 节点上线、下线、hello、心跳:更新运行时状态,补全 Agent 信息,并推送给 SSE。 -public sealed class MonitorNodeEventSink : INodeEventSink +public sealed class MoShiroNodeEventSink : INodeEventSink { private readonly IAgentStore _agents; private readonly AgentRuntime _runtime; @@ -54,7 +54,7 @@ public sealed class MonitorNodeEventSink : INodeEventSink private readonly NodeInfoService _nodes; private readonly ReleaseService _releases; - public MonitorNodeEventSink(IAgentStore agents, AgentRuntime runtime, IEventStore events, LiveHub live, AgentService service, NodeInfoService nodes, ReleaseService releases) + public MoShiroNodeEventSink(IAgentStore agents, AgentRuntime runtime, IEventStore events, LiveHub live, AgentService service, NodeInfoService nodes, ReleaseService releases) { _releases = releases; _nodes = nodes; @@ -93,7 +93,7 @@ public sealed class MonitorNodeEventSink : INodeEventSink try { AgentRuntimeState state = _runtime.Get(id); - AgentHeartbeatExtra? beat = extra.Deserialize(MonitorJsonContext.Default.AgentHeartbeatExtra); + AgentHeartbeatExtra? beat = extra.Deserialize(MoShiroJsonContext.Default.AgentHeartbeatExtra); state.LastHeartbeat = beat; state.LastHeartbeatAtMs = nodeEvent.AtMs; _releases.RecordStatus(id, beat?.Update); @@ -128,7 +128,7 @@ public sealed class MonitorNodeEventSink : INodeEventSink { try { - ProbeHelloMeta? meta = raw.Deserialize(MonitorJsonContext.Default.ProbeHelloMeta); + ProbeHelloMeta? meta = raw.Deserialize(MoShiroJsonContext.Default.ProbeHelloMeta); await _nodes.SetPlatformAsync(id.Value, NodeKinds.Probe, meta?.Platform, ct); } catch (JsonException) @@ -146,7 +146,7 @@ public sealed class MonitorNodeEventSink : INodeEventSink case NodeEventKind.Heartbeat when nodeEvent.Extra is { } extra: try { - _releases.RecordStatus(id, extra.Deserialize(MonitorJsonContext.Default.ProbeHeartbeatExtra)?.Update); + _releases.RecordStatus(id, extra.Deserialize(MoShiroJsonContext.Default.ProbeHeartbeatExtra)?.Update); } catch (JsonException) { @@ -162,9 +162,9 @@ public sealed class MonitorNodeEventSink : INodeEventSink var kinds = new List(); foreach (string capability in hello.Capabilities) { - if (capability.StartsWith(MonitorCapabilities.KindsPrefix, StringComparison.Ordinal)) + if (capability.StartsWith(MoShiroCapabilities.KindsPrefix, StringComparison.Ordinal)) { - kinds.AddRange(capability[MonitorCapabilities.KindsPrefix.Length..].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); + kinds.AddRange(capability[MoShiroCapabilities.KindsPrefix.Length..].Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)); } } @@ -173,7 +173,7 @@ public sealed class MonitorNodeEventSink : INodeEventSink { try { - meta = raw.Deserialize(MonitorJsonContext.Default.AgentHelloMeta) ?? meta; + meta = raw.Deserialize(MoShiroJsonContext.Default.AgentHelloMeta) ?? meta; } catch (JsonException) { diff --git a/backend/src/Monitor.Backend.Application/Agents/ProbeService.cs b/backend/src/MoShiro.Backend.Application/Agents/ProbeService.cs similarity index 93% rename from backend/src/Monitor.Backend.Application/Agents/ProbeService.cs rename to backend/src/MoShiro.Backend.Application/Agents/ProbeService.cs index 8b24e6c..e47df2b 100644 --- a/backend/src/Monitor.Backend.Application/Agents/ProbeService.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/ProbeService.cs @@ -2,9 +2,9 @@ using System.Text.RegularExpressions; using Fleet.Core; using Fleet.Identity; using Fleet.Server; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 本地硬上限:写在 Probe 本机的配置里,面板无法修改(在路由器上建议调低)。 public sealed record ProbeLimits(double MaxTestMbps, long MaxTotalPps); @@ -56,7 +56,7 @@ public sealed partial class ProbeService if ((await _probes.ListAsync(ct)).Any(p => string.Equals(p.Name, finalName, StringComparison.OrdinalIgnoreCase))) { - return Result.Fail(MonitorErrors.NameConflict, $"a probe named {finalName} already exists"); + return Result.Fail(MoShiroErrors.NameConflict, $"a probe named {finalName} already exists"); } using NodeIdentity identity = NodeIdentity.Generate(); @@ -80,7 +80,7 @@ public sealed partial class ProbeService { if (await _probes.GetAsync(id, ct) is not { } probe) { - return Result.Fail(MonitorErrors.ProbeNotFound, "probe not found"); + return Result.Fail(MoShiroErrors.ProbeNotFound, "probe not found"); } string trimmed = name.Trim(); @@ -91,7 +91,7 @@ public sealed partial class ProbeService if ((await _probes.ListAsync(ct)).Any(p => p.Id != id && string.Equals(p.Name, trimmed, StringComparison.OrdinalIgnoreCase))) { - return Result.Fail(MonitorErrors.NameConflict, $"a probe named {trimmed} already exists"); + return Result.Fail(MoShiroErrors.NameConflict, $"a probe named {trimmed} already exists"); } await _probes.SaveAsync(probe with { Name = trimmed }, ct); @@ -103,12 +103,12 @@ public sealed partial class ProbeService { if (await _probes.GetAsync(id, ct) is not { } probe) { - return Result.Fail(MonitorErrors.ProbeNotFound, "probe not found"); + return Result.Fail(MoShiroErrors.ProbeNotFound, "probe not found"); } if (probe.RevokedAtMs is not null) { - return Result.Fail(MonitorErrors.ProbeRevoked, "a revoked probe cannot get a new key; create a new probe instead"); + return Result.Fail(MoShiroErrors.ProbeRevoked, "a revoked probe cannot get a new key; create a new probe instead"); } using NodeIdentity identity = NodeIdentity.Generate(); @@ -124,7 +124,7 @@ public sealed partial class ProbeService { if (await _probes.GetAsync(id, ct) is null) { - return Result.Fail(MonitorErrors.ProbeNotFound, "probe not found"); + return Result.Fail(MoShiroErrors.ProbeNotFound, "probe not found"); } await _probes.RevokeAsync(id, _time.GetUtcNow().ToUnixTimeMilliseconds(), ct); @@ -151,12 +151,12 @@ public sealed partial class ProbeService { if (await _probes.GetAsync(probeId, ct) is null) { - return Result.Fail(MonitorErrors.ProbeNotFound, "probe not found"); + return Result.Fail(MoShiroErrors.ProbeNotFound, "probe not found"); } if (await _agents.GetAsync(agentId, ct) is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "agent not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "agent not found"); } return await _gateway.DisconnectProbeAsync(agentId, new ProbeId(probeId), ct); diff --git a/backend/src/Monitor.Backend.Application/Analysis/AnalysisScheduler.cs b/backend/src/MoShiro.Backend.Application/Analysis/AnalysisScheduler.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Analysis/AnalysisScheduler.cs rename to backend/src/MoShiro.Backend.Application/Analysis/AnalysisScheduler.cs index e29a1f1..3259509 100644 --- a/backend/src/Monitor.Backend.Application/Analysis/AnalysisScheduler.cs +++ b/backend/src/MoShiro.Backend.Application/Analysis/AnalysisScheduler.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Monitor.Backend.Analysis; +using MoShiro.Backend.Analysis; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 持久路径:分钟窗口就绪(窗口结束 + 宽限,或迟到的块到达)之后从原始块计算 → 写入 metric_1m → 标记降采样脏桶; diff --git a/backend/src/Monitor.Backend.Application/Analysis/MetricRecompute.cs b/backend/src/MoShiro.Backend.Application/Analysis/MetricRecompute.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Analysis/MetricRecompute.cs rename to backend/src/MoShiro.Backend.Application/Analysis/MetricRecompute.cs index 0214343..50893d8 100644 --- a/backend/src/Monitor.Backend.Application/Analysis/MetricRecompute.cs +++ b/backend/src/MoShiro.Backend.Application/Analysis/MetricRecompute.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Monitor.Backend.Analysis; +using MoShiro.Backend.Analysis; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record RecomputeProgress(string State, IReadOnlyList Metrics, long WindowsDone, long WindowsTotal, long? FromMs, long? ToMs); diff --git a/backend/src/Monitor.Backend.Application/Analysis/WindowComputer.cs b/backend/src/MoShiro.Backend.Application/Analysis/WindowComputer.cs similarity index 95% rename from backend/src/Monitor.Backend.Application/Analysis/WindowComputer.cs rename to backend/src/MoShiro.Backend.Application/Analysis/WindowComputer.cs index f5328f2..0d33caa 100644 --- a/backend/src/Monitor.Backend.Application/Analysis/WindowComputer.cs +++ b/backend/src/MoShiro.Backend.Application/Analysis/WindowComputer.cs @@ -1,8 +1,8 @@ using Fleet.Core; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 从原始块重新计算一个窗口(持久路径与回填共用):读取与窗口重叠的块 → 评估 → 取出窗口对应的累加器。 diff --git a/backend/src/Monitor.Backend.Application/ApplicationServiceCollectionExtensions.cs b/backend/src/MoShiro.Backend.Application/ApplicationServiceCollectionExtensions.cs similarity index 91% rename from backend/src/Monitor.Backend.Application/ApplicationServiceCollectionExtensions.cs rename to backend/src/MoShiro.Backend.Application/ApplicationServiceCollectionExtensions.cs index 2ad95a9..569f932 100644 --- a/backend/src/Monitor.Backend.Application/ApplicationServiceCollectionExtensions.cs +++ b/backend/src/MoShiro.Backend.Application/ApplicationServiceCollectionExtensions.cs @@ -2,7 +2,7 @@ using Fleet.Server; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public static class ApplicationServiceCollectionExtensions { @@ -10,7 +10,7 @@ public static class ApplicationServiceCollectionExtensions /// 注册 Application 层的全部服务。需要事先注册端口的实现(存储、)和 。 /// 后台任务按这里的顺序启动: 必须在最前面。 /// - public static IServiceCollection AddMonitorApplication(this IServiceCollection services, BackendOptions options) + public static IServiceCollection AddMoShiroApplication(this IServiceCollection services, BackendOptions options) { services.AddSingleton(options); services.AddHostedService(); @@ -47,8 +47,8 @@ public static class ApplicationServiceCollectionExtensions services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddHostedService(provider => provider.GetRequiredService()); diff --git a/backend/src/Monitor.Backend.Application/Auth/AuthModels.cs b/backend/src/MoShiro.Backend.Application/Auth/AuthModels.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/Auth/AuthModels.cs rename to backend/src/MoShiro.Backend.Application/Auth/AuthModels.cs index 9caf89d..9f438c5 100644 --- a/backend/src/Monitor.Backend.Application/Auth/AuthModels.cs +++ b/backend/src/MoShiro.Backend.Application/Auth/AuthModels.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public enum PrincipalKind { diff --git a/backend/src/Monitor.Backend.Application/Auth/AuthService.cs b/backend/src/MoShiro.Backend.Application/Auth/AuthService.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Auth/AuthService.cs rename to backend/src/MoShiro.Backend.Application/Auth/AuthService.cs index 81e4086..30cad19 100644 --- a/backend/src/Monitor.Backend.Application/Auth/AuthService.cs +++ b/backend/src/MoShiro.Backend.Application/Auth/AuthService.cs @@ -2,9 +2,9 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 面板用户、Cookie 会话、API 令牌。密码用 PBKDF2-SHA256,令牌和会话只保存 SHA-256。 public sealed partial class AuthService @@ -34,7 +34,7 @@ public sealed partial class AuthService byte[] hash = Hash(password, salt, iterations); if (user is null || !CryptographicOperations.FixedTimeEquals(hash, user.PasswordHash)) { - return Result.Fail(MonitorErrors.Unauthorized, "invalid credentials"); + return Result.Fail(MoShiroErrors.Unauthorized, "invalid credentials"); } byte[] token = RandomNumberGenerator.GetBytes(32); @@ -111,7 +111,7 @@ public sealed partial class AuthService var user = new UserRecord(Ulid.NewPrefixed("usr", _time), username, Hash(password, salt, _options.Pbkdf2Iterations), salt, _options.Pbkdf2Iterations, _time.GetUtcNow().ToUnixTimeMilliseconds()); return await _store.CreateUserAsync(user, ct) ? Result.Ok(user) - : Result.Fail(MonitorErrors.NameConflict, "username already exists"); + : Result.Fail(MoShiroErrors.NameConflict, "username already exists"); } public async ValueTask> ChangePasswordAsync(string userId, string newPassword, CancellationToken ct) diff --git a/backend/src/Monitor.Backend.Application/Auth/LoginRateLimiter.cs b/backend/src/MoShiro.Backend.Application/Auth/LoginRateLimiter.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Auth/LoginRateLimiter.cs rename to backend/src/MoShiro.Backend.Application/Auth/LoginRateLimiter.cs index 134cce6..ca7fede 100644 --- a/backend/src/Monitor.Backend.Application/Auth/LoginRateLimiter.cs +++ b/backend/src/MoShiro.Backend.Application/Auth/LoginRateLimiter.cs @@ -1,6 +1,6 @@ using System.Collections.Concurrent; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 登录限流:每个来源在滑动的一分钟内最多尝试若干次。 public sealed class LoginRateLimiter diff --git a/backend/src/Monitor.Backend.Application/BackendCounters.cs b/backend/src/MoShiro.Backend.Application/BackendCounters.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/BackendCounters.cs rename to backend/src/MoShiro.Backend.Application/BackendCounters.cs index 032831f..28c1036 100644 --- a/backend/src/Monitor.Backend.Application/BackendCounters.cs +++ b/backend/src/MoShiro.Backend.Application/BackendCounters.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 进程内计数器,用于 /metrics(Prometheus 格式)。 public sealed class BackendCounters diff --git a/backend/src/Monitor.Backend.Application/BackendOptions.cs b/backend/src/MoShiro.Backend.Application/BackendOptions.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/BackendOptions.cs rename to backend/src/MoShiro.Backend.Application/BackendOptions.cs index f38727d..4fb34a2 100644 --- a/backend/src/Monitor.Backend.Application/BackendOptions.cs +++ b/backend/src/MoShiro.Backend.Application/BackendOptions.cs @@ -1,9 +1,9 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// Backend 的可调参数(保留期等也可以通过 settings 改,这里是默认值与硬上限)。 public sealed class BackendOptions { - /// 数据目录:monitor.db 与 raw/。 + /// 数据目录:moshiro.db 与 raw/。 public string DataDirectory { get; set; } = "data"; /// 面板的对外地址(用于生成安装命令)。 diff --git a/backend/src/Monitor.Backend.Application/Ingest/IngestService.cs b/backend/src/MoShiro.Backend.Application/Ingest/IngestService.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Ingest/IngestService.cs rename to backend/src/MoShiro.Backend.Application/Ingest/IngestService.cs index 4bbc381..5b6b8ae 100644 --- a/backend/src/Monitor.Backend.Application/Ingest/IngestService.cs +++ b/backend/src/MoShiro.Backend.Application/Ingest/IngestService.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Extensions.Logging; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 和 都会让节点把消息从发件箱里移除。 diff --git a/backend/src/Monitor.Backend.Application/Live/HotLayer.cs b/backend/src/MoShiro.Backend.Application/Live/HotLayer.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Live/HotLayer.cs rename to backend/src/MoShiro.Backend.Application/Live/HotLayer.cs index 2b921b7..747cf7d 100644 --- a/backend/src/Monitor.Backend.Application/Live/HotLayer.cs +++ b/backend/src/MoShiro.Backend.Application/Live/HotLayer.cs @@ -1,9 +1,9 @@ using System.Collections.Concurrent; using Fleet.Core; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 1 秒精度的内存热层:最近 1 小时的累加器。桶以 编码后的字节保存, diff --git a/backend/src/Monitor.Backend.Application/Live/HotLayerBackfill.cs b/backend/src/MoShiro.Backend.Application/Live/HotLayerBackfill.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Live/HotLayerBackfill.cs rename to backend/src/MoShiro.Backend.Application/Live/HotLayerBackfill.cs index 73dd177..917509c 100644 --- a/backend/src/Monitor.Backend.Application/Live/HotLayerBackfill.cs +++ b/backend/src/MoShiro.Backend.Application/Live/HotLayerBackfill.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 重启后热层为空:从原始块回填最近 1 小时(低优先级、逐块让出)。完成之前查询规划器不会选用热层。 public sealed partial class HotLayerBackfill : BackgroundService diff --git a/backend/src/Monitor.Backend.Application/Live/LiveHub.cs b/backend/src/MoShiro.Backend.Application/Live/LiveHub.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Live/LiveHub.cs rename to backend/src/MoShiro.Backend.Application/Live/LiveHub.cs index f0dc7cc..7f5a1d2 100644 --- a/backend/src/Monitor.Backend.Application/Live/LiveHub.cs +++ b/backend/src/MoShiro.Backend.Application/Live/LiveHub.cs @@ -1,9 +1,9 @@ using System.Collections.Concurrent; using System.Threading.Channels; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 发布到 SSE 订阅者的信号。SSE 只负责“更快地获得变化”,推送的内容都能由 REST 查询重建。 public abstract record LiveSignal; diff --git a/backend/src/Monitor.Backend.Application/Monitor.Backend.Application.csproj b/backend/src/MoShiro.Backend.Application/MoShiro.Backend.Application.csproj similarity index 81% rename from backend/src/Monitor.Backend.Application/Monitor.Backend.Application.csproj rename to backend/src/MoShiro.Backend.Application/MoShiro.Backend.Application.csproj index 611afa2..02dcdf9 100644 --- a/backend/src/Monitor.Backend.Application/Monitor.Backend.Application.csproj +++ b/backend/src/MoShiro.Backend.Application/MoShiro.Backend.Application.csproj @@ -4,7 +4,7 @@ - + diff --git a/backend/src/Monitor.Backend.Application/Models/AgentModels.cs b/backend/src/MoShiro.Backend.Application/Models/AgentModels.cs similarity index 94% rename from backend/src/Monitor.Backend.Application/Models/AgentModels.cs rename to backend/src/MoShiro.Backend.Application/Models/AgentModels.cs index 2b7dd90..b44c53e 100644 --- a/backend/src/Monitor.Backend.Application/Models/AgentModels.cs +++ b/backend/src/MoShiro.Backend.Application/Models/AgentModels.cs @@ -1,7 +1,7 @@ using Fleet.Core; using Fleet.Server; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record AgentRecord( NodeId Id, @@ -45,7 +45,7 @@ public sealed record AgentHello( int? ProbeTcpPort, IReadOnlyList? ProbeUdpPorts); -/// 注册之后 Monitor 的补充:公开地址和端口。 +/// 注册之后 MoShiro 的补充:公开地址和端口。 public sealed record AgentEnrollment(string PublicHost, int ProbeTcpPort, IReadOnlyList ProbeUdpPorts, IReadOnlyList Kinds); public sealed record JoinTokenInfo( diff --git a/backend/src/Monitor.Backend.Application/Models/RawModels.cs b/backend/src/MoShiro.Backend.Application/Models/RawModels.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/Models/RawModels.cs rename to backend/src/MoShiro.Backend.Application/Models/RawModels.cs index 4f2be36..52f133b 100644 --- a/backend/src/Monitor.Backend.Application/Models/RawModels.cs +++ b/backend/src/MoShiro.Backend.Application/Models/RawModels.cs @@ -1,7 +1,7 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 原始块的索引信息(块本身在段文件里)。 public sealed record RawChunkMeta( diff --git a/backend/src/Monitor.Backend.Application/Models/TaskAndTestModels.cs b/backend/src/MoShiro.Backend.Application/Models/TaskAndTestModels.cs similarity index 93% rename from backend/src/Monitor.Backend.Application/Models/TaskAndTestModels.cs rename to backend/src/MoShiro.Backend.Application/Models/TaskAndTestModels.cs index 7e732a1..6502332 100644 --- a/backend/src/Monitor.Backend.Application/Models/TaskAndTestModels.cs +++ b/backend/src/MoShiro.Backend.Application/Models/TaskAndTestModels.cs @@ -1,8 +1,8 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record TaskRecord( TaskId Id, diff --git a/backend/src/Monitor.Backend.Application/Nodes/IspClassifier.cs b/backend/src/MoShiro.Backend.Application/Nodes/IspClassifier.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Nodes/IspClassifier.cs rename to backend/src/MoShiro.Backend.Application/Nodes/IspClassifier.cs index 46205d9..c0c45c4 100644 --- a/backend/src/Monitor.Backend.Application/Nodes/IspClassifier.cs +++ b/backend/src/MoShiro.Backend.Application/Nodes/IspClassifier.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 把「ASN 和组织名」归成几个常见的国内运营商:mobile / telecom / unicom / broadnet;别的是 other(显示组织名)。 diff --git a/backend/src/Monitor.Backend.Application/Nodes/NodeInfo.cs b/backend/src/MoShiro.Backend.Application/Nodes/NodeInfo.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Nodes/NodeInfo.cs rename to backend/src/MoShiro.Backend.Application/Nodes/NodeInfo.cs index b5212d1..af23b65 100644 --- a/backend/src/Monitor.Backend.Application/Nodes/NodeInfo.cs +++ b/backend/src/MoShiro.Backend.Application/Nodes/NodeInfo.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 一个节点(Agent 或 Probe)用来展示的信息:系统和架构、国家、运营商,以及它自己上报的构建信息(版本、tag、提交、构建时间)。 diff --git a/backend/src/Monitor.Backend.Application/Nodes/NodeInfoService.cs b/backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs similarity index 99% rename from backend/src/Monitor.Backend.Application/Nodes/NodeInfoService.cs rename to backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs index 9a9d3ea..09930ab 100644 --- a/backend/src/Monitor.Backend.Application/Nodes/NodeInfoService.cs +++ b/backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs @@ -1,9 +1,9 @@ using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 节点的展示信息:系统和架构(节点自报),国家和运营商(Backend 用节点的来源地址查一次)。 diff --git a/backend/src/Monitor.Backend.Application/Ports/IAgentGateway.cs b/backend/src/MoShiro.Backend.Application/Ports/IAgentGateway.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/Ports/IAgentGateway.cs rename to backend/src/MoShiro.Backend.Application/Ports/IAgentGateway.cs index a022297..ed855df 100644 --- a/backend/src/Monitor.Backend.Application/Ports/IAgentGateway.cs +++ b/backend/src/MoShiro.Backend.Application/Ports/IAgentGateway.cs @@ -1,7 +1,7 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// Application 通往 Agent 连接的端口(由 Gateway 实现):下发状态变化、发起按需测试。 diff --git a/backend/src/Monitor.Backend.Application/Ports/IAuthStore.cs b/backend/src/MoShiro.Backend.Application/Ports/IAuthStore.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Ports/IAuthStore.cs rename to backend/src/MoShiro.Backend.Application/Ports/IAuthStore.cs index 02c7e35..235dcf3 100644 --- a/backend/src/Monitor.Backend.Application/Ports/IAuthStore.cs +++ b/backend/src/MoShiro.Backend.Application/Ports/IAuthStore.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record UserRecord(string Id, string Username, byte[] PasswordHash, byte[] PasswordSalt, int Iterations, long CreatedAtMs); diff --git a/backend/src/Monitor.Backend.Application/Ports/StorePorts.cs b/backend/src/MoShiro.Backend.Application/Ports/StorePorts.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Ports/StorePorts.cs rename to backend/src/MoShiro.Backend.Application/Ports/StorePorts.cs index b600021..a126798 100644 --- a/backend/src/Monitor.Backend.Application/Ports/StorePorts.cs +++ b/backend/src/MoShiro.Backend.Application/Ports/StorePorts.cs @@ -1,8 +1,8 @@ using Fleet.Core; using Fleet.Server; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public interface IAgentStore { @@ -137,7 +137,7 @@ public interface IEventStore /// 主机资源占用的历史:每个 Agent 每分钟一个样本。 public interface IHostStore { - ValueTask SaveAsync(NodeId agent, long minuteMs, Monitor.Protocol.HostSnapshot snapshot, CancellationToken ct); + ValueTask SaveAsync(NodeId agent, long minuteMs, MoShiro.Protocol.HostSnapshot snapshot, CancellationToken ct); ValueTask> ReadAsync(NodeId agent, long fromMs, long toMs, CancellationToken ct); diff --git a/backend/src/Monitor.Backend.Application/Query/Bucketing.cs b/backend/src/MoShiro.Backend.Application/Query/Bucketing.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/Query/Bucketing.cs rename to backend/src/MoShiro.Backend.Application/Query/Bucketing.cs index b959fd3..0eaae6d 100644 --- a/backend/src/Monitor.Backend.Application/Query/Bucketing.cs +++ b/backend/src/MoShiro.Backend.Application/Query/Bucketing.cs @@ -1,6 +1,6 @@ -using Monitor.Backend.Analysis; +using MoShiro.Backend.Analysis; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 把源行合并成查询步长的桶:计数相加、直方图逐桶相加,所以结果是精确的,不是各点的平均。 public sealed class BucketedRows diff --git a/backend/src/Monitor.Backend.Application/Query/Lines.cs b/backend/src/MoShiro.Backend.Application/Query/Lines.cs similarity index 92% rename from backend/src/Monitor.Backend.Application/Query/Lines.cs rename to backend/src/MoShiro.Backend.Application/Query/Lines.cs index 53240c1..8c07a0b 100644 --- a/backend/src/Monitor.Backend.Application/Query/Lines.cs +++ b/backend/src/MoShiro.Backend.Application/Query/Lines.cs @@ -1,7 +1,7 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 一条线路 = 一对(Agent,Probe)。存储里它的序列是 line:<probe_id>,对外的 id 是 <agent_id>~<probe_id>。 public readonly record struct LineKey(NodeId Agent, ProbeId Probe) @@ -68,7 +68,7 @@ public sealed class LineService { if (!LineKey.TryParse(id, out LineKey key)) { - return Result.Fail(MonitorErrors.AgentNotFound, "line not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "line not found"); } agentPart = key.Agent.Value; @@ -78,7 +78,7 @@ public sealed class LineService AgentView? agent = await _agents.GetAsync(new NodeId(agentPart), ct); if (agent is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "line not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "line not found"); } IReadOnlyList probes = await _probes.ListAsync(ct); @@ -88,7 +88,7 @@ public sealed class LineService probe = probes.FirstOrDefault(p => p.Id == probePart); if (probe is null) { - return Result.Fail(MonitorErrors.ProbeNotFound, "probe not found"); + return Result.Fail(MoShiroErrors.ProbeNotFound, "probe not found"); } } else @@ -96,7 +96,7 @@ public sealed class LineService ProbeRecord[] active = [.. probes.Where(p => p.RevokedAtMs is null)]; if (active.Length != 1) { - return Result.Fail(MonitorErrors.ProbeRequired, active.Length == 0 ? "there is no active probe" : "several probes exist: use ~ as the line id"); + return Result.Fail(MoShiroErrors.ProbeRequired, active.Length == 0 ? "there is no active probe" : "several probes exist: use ~ as the line id"); } probe = active[0]; diff --git a/backend/src/Monitor.Backend.Application/Query/MetricSelection.cs b/backend/src/MoShiro.Backend.Application/Query/MetricSelection.cs similarity index 89% rename from backend/src/Monitor.Backend.Application/Query/MetricSelection.cs rename to backend/src/MoShiro.Backend.Application/Query/MetricSelection.cs index ca74d91..e3b34f1 100644 --- a/backend/src/Monitor.Backend.Application/Query/MetricSelection.cs +++ b/backend/src/MoShiro.Backend.Application/Query/MetricSelection.cs @@ -1,8 +1,8 @@ using Fleet.Core; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 请求的指标:导出 id(rtt.p95)与评分 id(score.mc)。 public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyList ScoreModelIds) @@ -21,7 +21,7 @@ public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyL PresetDef? preset = MetricRegistry.FindPreset(presetId); if (preset is null) { - return Result.Fail(MonitorErrors.UnknownPreset, $"unknown preset {presetId}"); + return Result.Fail(MoShiroErrors.UnknownPreset, $"unknown preset {presetId}"); } ids.AddRange(preset.MetricIds); @@ -36,7 +36,7 @@ public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyL ids = [.. ids.Distinct(StringComparer.Ordinal)]; if (ids.Count > maxMetrics) { - return Result.Fail(MonitorErrors.TooManyMetrics, $"at most {maxMetrics} metrics per request"); + return Result.Fail(MoShiroErrors.TooManyMetrics, $"at most {maxMetrics} metrics per request"); } var exports = new List(); @@ -48,7 +48,7 @@ public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyL string model = id["score.".Length..]; if (ModelRegistry.Find(model) is null) { - return Result.Fail(MonitorErrors.UnknownMetric, $"unknown metric {id}"); + return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}"); } scores.Add(model); @@ -59,7 +59,7 @@ public sealed record MetricSelection(IReadOnlyList ExportIds, IReadOnlyL } else { - return Result.Fail(MonitorErrors.UnknownMetric, $"unknown metric {id}"); + return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {id}"); } } diff --git a/backend/src/Monitor.Backend.Application/Query/QueryResults.cs b/backend/src/MoShiro.Backend.Application/Query/QueryResults.cs similarity index 95% rename from backend/src/Monitor.Backend.Application/Query/QueryResults.cs rename to backend/src/MoShiro.Backend.Application/Query/QueryResults.cs index 996a527..6392195 100644 --- a/backend/src/Monitor.Backend.Application/Query/QueryResults.cs +++ b/backend/src/MoShiro.Backend.Application/Query/QueryResults.cs @@ -1,7 +1,7 @@ using Fleet.Core; -using Monitor.Backend.Analysis; +using MoShiro.Backend.Analysis; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record SummaryResult( IReadOnlyDictionary Values, diff --git a/backend/src/Monitor.Backend.Application/Query/QueryService.cs b/backend/src/MoShiro.Backend.Application/Query/QueryService.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Query/QueryService.cs rename to backend/src/MoShiro.Backend.Application/Query/QueryService.cs index a082026..d484acb 100644 --- a/backend/src/Monitor.Backend.Application/Query/QueryService.cs +++ b/backend/src/MoShiro.Backend.Application/Query/QueryService.cs @@ -1,9 +1,9 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 展示数据查询:任意时间段、自动步长、数据分层与现场合并、精确汇总、列式结果。 @@ -185,7 +185,7 @@ public sealed class QueryService if (!MetricRegistry.TryFindExport(exportId, out _, out _)) { - return Result.Fail(MonitorErrors.UnknownMetric, $"unknown metric {exportId}"); + return Result.Fail(MoShiroErrors.UnknownMetric, $"unknown metric {exportId}"); } Result<(QueryRange Range, SourcePlan Plan)> planned = Plan(request, "1h"); @@ -229,7 +229,7 @@ public sealed class QueryService MetricDef? def = MetricRegistry.Find(metricId); if (def is null || def.Kind != AccKind.Histogram) { - return Result.Fail(MonitorErrors.UnknownMetric, $"{metricId} is not a histogram metric"); + return Result.Fail(MoShiroErrors.UnknownMetric, $"{metricId} is not a histogram metric"); } Result<(QueryRange Range, SourcePlan Plan)> planned = Plan(request, "15m"); @@ -273,7 +273,7 @@ public sealed class QueryService MetricDef? def = MetricRegistry.Find(metricId); if (def is null || def.Kind != AccKind.Histogram) { - return Result.Fail(MonitorErrors.UnknownMetric, $"{metricId} is not a histogram metric"); + return Result.Fail(MoShiroErrors.UnknownMetric, $"{metricId} is not a histogram metric"); } Result<(QueryRange Range, SourcePlan Plan)> planned = Plan(request, "6h"); diff --git a/backend/src/Monitor.Backend.Application/Query/RangeResolver.cs b/backend/src/MoShiro.Backend.Application/Query/RangeResolver.cs similarity index 92% rename from backend/src/Monitor.Backend.Application/Query/RangeResolver.cs rename to backend/src/MoShiro.Backend.Application/Query/RangeResolver.cs index b5a0558..68e0921 100644 --- a/backend/src/Monitor.Backend.Application/Query/RangeResolver.cs +++ b/backend/src/MoShiro.Backend.Application/Query/RangeResolver.cs @@ -1,9 +1,9 @@ using System.Globalization; using System.Text.RegularExpressions; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// now(默认)或 latest:相对时间段(range=)以当前时间还是以这条线路最后一个样本为终点。后者让离线的线路显示它最后一段数据,而不是空图。 public sealed record RangeRequest(string? Range, DateTimeOffset? From, DateTimeOffset? To, string? Step, string? TimeZone, string? Anchor = null); @@ -63,7 +63,7 @@ public sealed partial class RangeResolver { if (!TryParseSpan(request.Range ?? defaultRange, out long span)) { - return Result.Fail(MonitorErrors.InvalidRange, "range must look like 5m, 6h or 7d"); + return Result.Fail(MoShiroErrors.InvalidRange, "range must look like 5m, 6h or 7d"); } to = nowMs; @@ -73,12 +73,12 @@ public sealed partial class RangeResolver if (from >= to) { - return Result.Fail(MonitorErrors.InvalidRange, "from must be earlier than to"); + return Result.Fail(MoShiroErrors.InvalidRange, "from must be earlier than to"); } if (to - from > MaxSpanMs) { - return Result.Fail(MonitorErrors.RangeTooLarge, "the range is longer than 400 days"); + return Result.Fail(MoShiroErrors.RangeTooLarge, "the range is longer than 400 days"); } long step; @@ -88,14 +88,14 @@ public sealed partial class RangeResolver } else if (!Steps.TryParse(request.Step, out step)) { - return Result.Fail(MonitorErrors.InvalidStep, "step must be one of: " + string.Join(", ", Steps.Common.Select(Steps.Format))); + return Result.Fail(MoShiroErrors.InvalidStep, "step must be one of: " + string.Join(", ", Steps.Common.Select(Steps.Format))); } var range = new QueryRange(Steps.FloorTo(from, step), Steps.CeilTo(to, step), step, relative, from, to); if (range.Points > _options.MaxPointsPerSeries) { long suggested = Steps.Common.FirstOrDefault(s => (Steps.CeilTo(to, s) - Steps.FloorTo(from, s)) / s <= _options.MaxPointsPerSeries, Steps.Day); - return Result.Fail(MonitorErrors.RangeTooLarge, $"{range.Points} points exceed the limit of {_options.MaxPointsPerSeries}; use step={Steps.Format(suggested)} or a shorter range"); + return Result.Fail(MoShiroErrors.RangeTooLarge, $"{range.Points} points exceed the limit of {_options.MaxPointsPerSeries}; use step={Steps.Format(suggested)} or a shorter range"); } return Result.Ok(range); diff --git a/backend/src/Monitor.Backend.Application/Query/RowLoader.cs b/backend/src/MoShiro.Backend.Application/Query/RowLoader.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Query/RowLoader.cs rename to backend/src/MoShiro.Backend.Application/Query/RowLoader.cs index e9f397e..4f29b51 100644 --- a/backend/src/Monitor.Backend.Application/Query/RowLoader.cs +++ b/backend/src/MoShiro.Backend.Application/Query/RowLoader.cs @@ -1,8 +1,8 @@ using Fleet.Core; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public readonly record struct SourceRow(long Start, MetricSet Set, bool Partial); @@ -92,7 +92,7 @@ public sealed class RowLoader case DataSource.Raw: if (!_gate.TryEnter()) { - return Result>.Fail(MonitorErrors.TooManyRequests, "too many queries are reading raw data; retry shortly or use a coarser step"); + return Result>.Fail(MoShiroErrors.TooManyRequests, "too many queries are reading raw data; retry shortly or use a coarser step"); } try diff --git a/backend/src/Monitor.Backend.Application/Query/SourcePlanner.cs b/backend/src/MoShiro.Backend.Application/Query/SourcePlanner.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Query/SourcePlanner.cs rename to backend/src/MoShiro.Backend.Application/Query/SourcePlanner.cs index 4e3df11..4e4de46 100644 --- a/backend/src/Monitor.Backend.Application/Query/SourcePlanner.cs +++ b/backend/src/MoShiro.Backend.Application/Query/SourcePlanner.cs @@ -1,4 +1,4 @@ -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public enum DataSource { diff --git a/backend/src/Monitor.Backend.Application/Query/Steps.cs b/backend/src/MoShiro.Backend.Application/Query/Steps.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Query/Steps.cs rename to backend/src/MoShiro.Backend.Application/Query/Steps.cs index b1830f1..a0f250e 100644 --- a/backend/src/Monitor.Backend.Application/Query/Steps.cs +++ b/backend/src/MoShiro.Backend.Application/Query/Steps.cs @@ -1,6 +1,6 @@ using System.Globalization; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 常用步长:1s 2s 5s 10s 15s 30s 1m 2m 5m 10m 15m 30m 1h 2h 3h 6h 12h 1d。 public static class Steps diff --git a/backend/src/Monitor.Backend.Application/Retention/RetentionService.cs b/backend/src/MoShiro.Backend.Application/Retention/RetentionService.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Retention/RetentionService.cs rename to backend/src/MoShiro.Backend.Application/Retention/RetentionService.cs index 2b2db9c..85448fb 100644 --- a/backend/src/Monitor.Backend.Application/Retention/RetentionService.cs +++ b/backend/src/MoShiro.Backend.Application/Retention/RetentionService.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 保留清理:原始段文件(持续任务与按需测试保留期不同)、各层指标、事件。保留期来自 settings,可以在线修改。 public sealed partial class RetentionService : BackgroundService diff --git a/backend/src/Monitor.Backend.Application/StartupTasks.cs b/backend/src/MoShiro.Backend.Application/StartupTasks.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/StartupTasks.cs rename to backend/src/MoShiro.Backend.Application/StartupTasks.cs index 318fe29..3716e30 100644 --- a/backend/src/Monitor.Backend.Application/StartupTasks.cs +++ b/backend/src/MoShiro.Backend.Application/StartupTasks.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Hosting; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 启动时最先执行:截断没有索引记录的段文件尾部、创建初始管理员、创建默认的线路任务模板。 diff --git a/backend/src/Monitor.Backend.Application/Tasks/EffectiveTasks.cs b/backend/src/MoShiro.Backend.Application/Tasks/EffectiveTasks.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Tasks/EffectiveTasks.cs rename to backend/src/MoShiro.Backend.Application/Tasks/EffectiveTasks.cs index 504ce09..ae996ef 100644 --- a/backend/src/Monitor.Backend.Application/Tasks/EffectiveTasks.cs +++ b/backend/src/MoShiro.Backend.Application/Tasks/EffectiveTasks.cs @@ -1,7 +1,7 @@ using System.Text.Json; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record EffectiveTask( TaskId Id, diff --git a/backend/src/Monitor.Backend.Application/Tasks/TaskPlanner.cs b/backend/src/MoShiro.Backend.Application/Tasks/TaskPlanner.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/Tasks/TaskPlanner.cs rename to backend/src/MoShiro.Backend.Application/Tasks/TaskPlanner.cs index 526bb98..8a21a96 100644 --- a/backend/src/Monitor.Backend.Application/Tasks/TaskPlanner.cs +++ b/backend/src/MoShiro.Backend.Application/Tasks/TaskPlanner.cs @@ -2,9 +2,9 @@ using System.Security.Cryptography; using System.Text.Json; using Fleet.Core; using Fleet.Server; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record DesiredTaskSet(long Version, TaskSet Document); @@ -65,7 +65,7 @@ public sealed class TaskPlanner : IDisposable document.Peers.Add(new PeerSpec { ProbeId = new ProbeId(probe.Id), PublicKey = Convert.ToBase64String(probe.PublicKey) }); } - string hash = Convert.ToHexStringLower(SHA256.HashData(JsonSerializer.SerializeToUtf8Bytes(document, MonitorJsonContext.Default.TaskSet))); + string hash = Convert.ToHexStringLower(SHA256.HashData(JsonSerializer.SerializeToUtf8Bytes(document, MoShiroJsonContext.Default.TaskSet))); await _gate.WaitAsync(ct); try diff --git a/backend/src/Monitor.Backend.Application/Tasks/TaskService.cs b/backend/src/MoShiro.Backend.Application/Tasks/TaskService.cs similarity index 96% rename from backend/src/Monitor.Backend.Application/Tasks/TaskService.cs rename to backend/src/MoShiro.Backend.Application/Tasks/TaskService.cs index 00547e9..6bafbfd 100644 --- a/backend/src/Monitor.Backend.Application/Tasks/TaskService.cs +++ b/backend/src/MoShiro.Backend.Application/Tasks/TaskService.cs @@ -2,9 +2,9 @@ using System.Net; using System.Text.Json; using Fleet.Core; using Fleet.Server; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record TaskInput( string Scope, @@ -97,7 +97,7 @@ public sealed class TaskService TaskRecord? current = await _tasks.GetAsync(id, ct); if (current is null) { - return Result.Fail(MonitorErrors.TaskNotFound, "task not found"); + return Result.Fail(MoShiroErrors.TaskNotFound, "task not found"); } IReadOnlyDictionary parameters = patch.Params ?? current.Params; @@ -178,7 +178,7 @@ public sealed class TaskService KindSpec? spec = Kinds.Find(kind); if (spec is null) { - return Result.Fail(MonitorErrors.UnknownKind, $"unknown kind {kind}"); + return Result.Fail(MoShiroErrors.UnknownKind, $"unknown kind {kind}"); } Scope wanted = scope == EffectiveTasks.LineScope ? Scope.Line : Scope.Target; @@ -206,7 +206,7 @@ public sealed class TaskService if (agentId is { } id && await _agents.GetAsync(id, ct) is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "agent not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "agent not found"); } if (scope == EffectiveTasks.TargetScope) @@ -259,7 +259,7 @@ public sealed class TaskService long bits = EffectiveTasks.ContinuousLineBudget(EffectiveTasks.Compute(after, agent with { Kinds = [] }, filterByAgentKinds: false)); if (bits > ProtocolLimits.MaxLineBudgetBitsPerSecond) { - return Result.Fail(MonitorErrors.BudgetExceeded, $"continuous tasks for {agent.Name} would use {bits / 1000} kbps (limit {ProtocolLimits.MaxLineBudgetBitsPerSecond / 1000} kbps)"); + return Result.Fail(MoShiroErrors.BudgetExceeded, $"continuous tasks for {agent.Name} would use {bits / 1000} kbps (limit {ProtocolLimits.MaxLineBudgetBitsPerSecond / 1000} kbps)"); } } @@ -270,7 +270,7 @@ public sealed class TaskService long bits = EffectiveTasks.ContinuousLineBudget(EffectiveTasks.Compute(after, virtualAgent, filterByAgentKinds: false)); if (bits > ProtocolLimits.MaxLineBudgetBitsPerSecond) { - return Result.Fail(MonitorErrors.BudgetExceeded, $"continuous line templates would use {bits / 1000} kbps (limit {ProtocolLimits.MaxLineBudgetBitsPerSecond / 1000} kbps)"); + return Result.Fail(MoShiroErrors.BudgetExceeded, $"continuous line templates would use {bits / 1000} kbps (limit {ProtocolLimits.MaxLineBudgetBitsPerSecond / 1000} kbps)"); } } diff --git a/backend/src/Monitor.Backend.Application/Tests/Plans.cs b/backend/src/MoShiro.Backend.Application/Tests/Plans.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Tests/Plans.cs rename to backend/src/MoShiro.Backend.Application/Tests/Plans.cs index 31a6a87..2d4d1e1 100644 --- a/backend/src/Monitor.Backend.Application/Tests/Plans.cs +++ b/backend/src/MoShiro.Backend.Application/Tests/Plans.cs @@ -1,8 +1,8 @@ using System.Text.Json; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed record PhaseSpec(string Kind, IReadOnlyDictionary Overrides, int DurationSeconds); diff --git a/backend/src/Monitor.Backend.Application/Tests/TestFinalizer.cs b/backend/src/MoShiro.Backend.Application/Tests/TestFinalizer.cs similarity index 97% rename from backend/src/Monitor.Backend.Application/Tests/TestFinalizer.cs rename to backend/src/MoShiro.Backend.Application/Tests/TestFinalizer.cs index c095a2d..def6e09 100644 --- a/backend/src/Monitor.Backend.Application/Tests/TestFinalizer.cs +++ b/backend/src/MoShiro.Backend.Application/Tests/TestFinalizer.cs @@ -1,10 +1,10 @@ using Fleet.Core; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// /// 后台任务:给已成功结束的测试算结果(所有阶段的块到达之后,或结束一分钟后以现有的块为准), diff --git a/backend/src/Monitor.Backend.Application/Tests/TestModels.cs b/backend/src/MoShiro.Backend.Application/Tests/TestModels.cs similarity index 94% rename from backend/src/Monitor.Backend.Application/Tests/TestModels.cs rename to backend/src/MoShiro.Backend.Application/Tests/TestModels.cs index c983179..e41669c 100644 --- a/backend/src/Monitor.Backend.Application/Tests/TestModels.cs +++ b/backend/src/MoShiro.Backend.Application/Tests/TestModels.cs @@ -1,8 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Monitor.Backend.Analysis; +using MoShiro.Backend.Analysis; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; public sealed class StoredTestMetrics { diff --git a/backend/src/Monitor.Backend.Application/Tests/TestService.cs b/backend/src/MoShiro.Backend.Application/Tests/TestService.cs similarity index 94% rename from backend/src/Monitor.Backend.Application/Tests/TestService.cs rename to backend/src/MoShiro.Backend.Application/Tests/TestService.cs index d0dfaad..b0af831 100644 --- a/backend/src/Monitor.Backend.Application/Tests/TestService.cs +++ b/backend/src/MoShiro.Backend.Application/Tests/TestService.cs @@ -1,10 +1,10 @@ using System.Text.Json; using Fleet.Core; using Fleet.Server; -using Monitor.Backend.Analysis; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 按需测试(once):校验与排队、下发给 Agent、状态更新、结果查询。 public sealed class TestService @@ -33,18 +33,18 @@ public sealed class TestService PlanSpec? plan = Plans.Find(request.Plan); if (plan is null) { - return Result.Fail(MonitorErrors.UnknownPlan, $"unknown plan {request.Plan}"); + return Result.Fail(MoShiroErrors.UnknownPlan, $"unknown plan {request.Plan}"); } AgentRecord? agent = await _agents.GetAsync(agentId, ct); if (agent is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "agent not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "agent not found"); } if (agent.Status != NodeStatus.Active) { - return Result.Fail(MonitorErrors.AgentNotActive, "agent is not active"); + return Result.Fail(MoShiroErrors.AgentNotActive, "agent is not active"); } // Agent 在 hello 里声明了它支持的采集方法:方案里有它不支持的就不下发(否则要等到阶段开始才失败) @@ -62,17 +62,17 @@ public sealed class TestService if (!_gateway.IsOnline(agentId)) { - return Result.Fail(MonitorErrors.AgentOffline, "agent is offline"); + return Result.Fail(MoShiroErrors.AgentOffline, "agent is offline"); } if (!(_runtime.TryGet(agentId, out AgentRuntimeState? state) && state.IsConnected(probeId.Value))) { - return Result.Fail(MonitorErrors.ProbeOffline, $"probe {probeId.Value} has no session with this agent"); + return Result.Fail(MoShiroErrors.ProbeOffline, $"probe {probeId.Value} has no session with this agent"); } if (await _tests.HasActiveAsync(agentId, probeId, ct)) { - return Result.Fail(MonitorErrors.TestBusy, "another test is already running on this line"); + return Result.Fail(MoShiroErrors.TestBusy, "another test is already running on this line"); } var testId = new TestId(Ulid.NewPrefixed(TestId.Prefix, _time)); @@ -96,7 +96,7 @@ public sealed class TestService TestRecord? test = await _tests.GetAsync(id, ct); if (test is null) { - return Result.Fail(MonitorErrors.TestNotFound, "test not found"); + return Result.Fail(MoShiroErrors.TestNotFound, "test not found"); } if (IsTerminal(test.Status)) @@ -115,7 +115,7 @@ public sealed class TestService return Result.Ok(await _tests.GetAsync(id, ct) ?? test); } - /// monitor.test.status:只接受该测试所属 Agent 的汇报。 + /// moshiro.test.status:只接受该测试所属 Agent 的汇报。 public async ValueTask OnStatusAsync(NodeId agent, TestStatusParams status, CancellationToken ct) { TestRecord? test = await _tests.GetAsync(status.TestId, ct); @@ -244,7 +244,7 @@ public sealed class TestService long bits = kind.BudgetBitsPerSecond(resolved.Values!); if (limitMbps is > 0 && bits > limitMbps * 1_000_000) { - return Result>.Fail(MonitorErrors.BudgetExceeded, $"{phase.Kind} needs {bits / 1_000_000.0:0.#} Mbit/s but the agent allows {limitMbps} Mbit/s"); + return Result>.Fail(MoShiroErrors.BudgetExceeded, $"{phase.Kind} needs {bits / 1_000_000.0:0.#} Mbit/s but the agent allows {limitMbps} Mbit/s"); } int duration = resolved.Values!.Values.TryGetValue("duration_s", out JsonElement d) && d.TryGetInt64(out long seconds) ? (int)seconds : Math.Max(1, (int)Math.Round(phase.DurationSeconds * phaseScale)); diff --git a/backend/src/Monitor.Backend.Application/Updates/ReleaseService.cs b/backend/src/MoShiro.Backend.Application/Updates/ReleaseService.cs similarity index 98% rename from backend/src/Monitor.Backend.Application/Updates/ReleaseService.cs rename to backend/src/MoShiro.Backend.Application/Updates/ReleaseService.cs index ad1e055..39cc37b 100644 --- a/backend/src/Monitor.Backend.Application/Updates/ReleaseService.cs +++ b/backend/src/MoShiro.Backend.Application/Updates/ReleaseService.cs @@ -3,9 +3,9 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.RegularExpressions; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 面板记下的、CI 签名的发布清单(只留每个组件最新的一份)。Manifest 是签名对象的原始文本,面板一个字节也不改。 public sealed record ReleaseOffer(string Component, long Seq, string Tag, string Version, string Digest, string Manifest, string Signature); @@ -42,7 +42,7 @@ public sealed partial class ReleaseService ReleaseOffer? current = await GetAsync(offer.Component, ct); if (current is not null && offer.Seq <= current.Seq) { - return Result.Fail(MonitorErrors.NameConflict, $"a release with seq {current.Seq} or newer is already registered"); + return Result.Fail(MoShiroErrors.NameConflict, $"a release with seq {current.Seq} or newer is already registered"); } await _settings.SetAsync(Key(offer.Component, "manifest"), offer.Manifest, ct); diff --git a/backend/src/Monitor.Backend.Application/ValidationError.cs b/backend/src/MoShiro.Backend.Application/ValidationError.cs similarity index 85% rename from backend/src/Monitor.Backend.Application/ValidationError.cs rename to backend/src/MoShiro.Backend.Application/ValidationError.cs index c1468f9..fb2d2a7 100644 --- a/backend/src/Monitor.Backend.Application/ValidationError.cs +++ b/backend/src/MoShiro.Backend.Application/ValidationError.cs @@ -1,11 +1,11 @@ using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.Backend.Application; +namespace MoShiro.Backend.Application; /// 校验失败,带逐字段的原因(键是 JSON 字段路径)。Api 层把它映射成 problem+json 的 errors。 public sealed record ValidationError(string Message, IReadOnlyDictionary Fields) - : FleetError(MonitorErrors.ValidationFailed, Message) + : FleetError(MoShiroErrors.ValidationFailed, Message) { public static ValidationError Single(string field, string message) => new("Validation failed", new Dictionary { [field] = message }); diff --git a/backend/src/Monitor.Backend.Gateway/ConnectionShutdown.cs b/backend/src/MoShiro.Backend.Gateway/ConnectionShutdown.cs similarity index 97% rename from backend/src/Monitor.Backend.Gateway/ConnectionShutdown.cs rename to backend/src/MoShiro.Backend.Gateway/ConnectionShutdown.cs index 83227d4..07a6c56 100644 --- a/backend/src/Monitor.Backend.Gateway/ConnectionShutdown.cs +++ b/backend/src/MoShiro.Backend.Gateway/ConnectionShutdown.cs @@ -2,7 +2,7 @@ using Fleet.Server; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace Monitor.Backend.Gateway; +namespace MoShiro.Backend.Gateway; /// /// 宿主开始停止时先关闭所有 Agent 的长连接(Close 握手,不发 revoked:Agent 会重连)。 diff --git a/backend/src/Monitor.Backend.Gateway/FleetGateway.cs b/backend/src/MoShiro.Backend.Gateway/FleetGateway.cs similarity index 72% rename from backend/src/Monitor.Backend.Gateway/FleetGateway.cs rename to backend/src/MoShiro.Backend.Gateway/FleetGateway.cs index 89aaa98..d8fae4d 100644 --- a/backend/src/Monitor.Backend.Gateway/FleetGateway.cs +++ b/backend/src/MoShiro.Backend.Gateway/FleetGateway.cs @@ -1,10 +1,10 @@ using Fleet.Core; using Fleet.Server; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Gateway; +namespace MoShiro.Backend.Gateway; /// /// Application 通往 Agent 连接的端口的实现,建立在 Fleet.Server.Link 的在线表之上。 @@ -37,26 +37,26 @@ public sealed class FleetGateway : IAgentGateway public async ValueTask> RunTestAsync(NodeId agent, TestRunParams run, CancellationToken ct) { Result result = await Connections.RequestAsync( - agent, MonitorMethods.TestRun, run, MonitorJsonContext.Default.TestRunParams, MonitorJsonContext.Default.TestRunResult, ct); + agent, MoShiroMethods.TestRun, run, MoShiroJsonContext.Default.TestRunParams, MoShiroJsonContext.Default.TestRunResult, ct); if (!result.TryGet(out TestRunResult? accepted, out FleetError? error)) { - return Result.Fail(error.Code == FleetErrors.NodeOffline ? MonitorErrors.AgentOffline : error.Code, error.Message); + return Result.Fail(error.Code == FleetErrors.NodeOffline ? MoShiroErrors.AgentOffline : error.Code, error.Message); } - return accepted.Accepted ? Result.Ok(true) : Result.Fail(MonitorErrors.TestBusy, "the agent did not accept the test"); + return accepted.Accepted ? Result.Ok(true) : Result.Fail(MoShiroErrors.TestBusy, "the agent did not accept the test"); } public async ValueTask CancelTestAsync(NodeId agent, TestId test, CancellationToken ct) => await Connections.RequestAsync( - agent, MonitorMethods.TestCancel, new TestCancelParams { TestId = test }, MonitorJsonContext.Default.TestCancelParams, MonitorJsonContext.Default.TestRunResult, ct); + agent, MoShiroMethods.TestCancel, new TestCancelParams { TestId = test }, MoShiroJsonContext.Default.TestCancelParams, MoShiroJsonContext.Default.TestRunResult, ct); public async ValueTask> DisconnectProbeAsync(NodeId agent, ProbeId probe, CancellationToken ct) { Result result = await Connections.RequestAsync( - agent, MonitorMethods.ProbeDisconnect, new ProbeDisconnectParams { ProbeId = probe }, MonitorJsonContext.Default.ProbeDisconnectParams, MonitorJsonContext.Default.TestRunResult, ct); + agent, MoShiroMethods.ProbeDisconnect, new ProbeDisconnectParams { ProbeId = probe }, MoShiroJsonContext.Default.ProbeDisconnectParams, MoShiroJsonContext.Default.TestRunResult, ct); if (!result.TryGet(out TestRunResult? done, out FleetError? error)) { - return Result.Fail(error.Code == FleetErrors.NodeOffline ? MonitorErrors.AgentOffline : error.Code, error.Message); + return Result.Fail(error.Code == FleetErrors.NodeOffline ? MoShiroErrors.AgentOffline : error.Code, error.Message); } return Result.Ok(done.Accepted); @@ -65,8 +65,8 @@ public sealed class FleetGateway : IAgentGateway public ValueTask DisconnectAsync(NodeId agent, string reason, CancellationToken ct) => Connections.DisconnectAsync(agent, reason, ct); public async ValueTask NotifyUpdateOfferAsync(NodeId node, string manifest, string signature, CancellationToken ct) => - await Connections.NotifyAsync(node, MonitorMethods.UpdateOffer, new UpdateOfferParams { Manifest = manifest, Signature = signature }, MonitorJsonContext.Default.UpdateOfferParams, ct); + await Connections.NotifyAsync(node, MoShiroMethods.UpdateOffer, new UpdateOfferParams { Manifest = manifest, Signature = signature }, MoShiroJsonContext.Default.UpdateOfferParams, ct); public async ValueTask NotifyUpdateApplyAsync(NodeId node, CancellationToken ct) => - await Connections.NotifyAsync(node, MonitorMethods.UpdateApply, new UpdateOfferParams(), MonitorJsonContext.Default.UpdateOfferParams, ct); + await Connections.NotifyAsync(node, MoShiroMethods.UpdateApply, new UpdateOfferParams(), MoShiroJsonContext.Default.UpdateOfferParams, ct); } diff --git a/backend/src/Monitor.Backend.Gateway/GatewayServiceCollectionExtensions.cs b/backend/src/MoShiro.Backend.Gateway/GatewayServiceCollectionExtensions.cs similarity index 63% rename from backend/src/Monitor.Backend.Gateway/GatewayServiceCollectionExtensions.cs rename to backend/src/MoShiro.Backend.Gateway/GatewayServiceCollectionExtensions.cs index b8701aa..8cdb3ac 100644 --- a/backend/src/Monitor.Backend.Gateway/GatewayServiceCollectionExtensions.cs +++ b/backend/src/MoShiro.Backend.Gateway/GatewayServiceCollectionExtensions.cs @@ -2,18 +2,18 @@ using Fleet.Core; using Fleet.Rpc; using Fleet.Server; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Gateway; +namespace MoShiro.Backend.Gateway; public static class GatewayServiceCollectionExtensions { /// - /// 在 Fleet.Server 之上接入 Monitor:期望状态(TaskSet)、RawChunk 收件箱、monitor.* 方法、注册挂钩和节点事件。 - /// 需要事先注册 和 ,并在 AddMonitorApplication 之后调用。 + /// 在 Fleet.Server 之上接入 MoShiro:期望状态(TaskSet)、RawChunk 收件箱、moshiro.* 方法、注册挂钩和节点事件。 + /// 需要事先注册 和 ,并在 AddMoShiroApplication 之后调用。 /// - public static IServiceCollection AddMonitorGateway(this IServiceCollection services) + public static IServiceCollection AddMoShiroGateway(this IServiceCollection services) { services.AddSingleton(); services.AddHostedService(); @@ -22,24 +22,24 @@ public static class GatewayServiceCollectionExtensions .AddState() .AddState() .AddInbox() - .MapMethods(MapMonitorMethods); + .MapMethods(MapMoShiroMethods); return services; } - private static void MapMonitorMethods(RpcRouter router, IServiceProvider services) + private static void MapMoShiroMethods(RpcRouter router, IServiceProvider services) { router .OnNotify( - MonitorMethods.Event, - MonitorJsonContext.Default.AgentEventParams, + MoShiroMethods.Event, + MoShiroJsonContext.Default.AgentEventParams, (context, parameters, ct) => services.GetRequiredService().OnEventAsync(context.Node.Id, parameters, ct)) .OnNotify( - MonitorMethods.Host, - MonitorJsonContext.Default.HostSnapshot, + MoShiroMethods.Host, + MoShiroJsonContext.Default.HostSnapshot, (context, snapshot, ct) => services.GetRequiredService().OnHostAsync(context.Node.Id, snapshot, ct)) .OnNotify( - MonitorMethods.TestStatus, - MonitorJsonContext.Default.TestStatusParams, + MoShiroMethods.TestStatus, + MoShiroJsonContext.Default.TestStatusParams, (context, parameters, ct) => services.GetRequiredService().OnStatusAsync(context.Node.Id, parameters, ct)); } } diff --git a/backend/src/Monitor.Backend.Gateway/Monitor.Backend.Gateway.csproj b/backend/src/MoShiro.Backend.Gateway/MoShiro.Backend.Gateway.csproj similarity index 79% rename from backend/src/Monitor.Backend.Gateway/Monitor.Backend.Gateway.csproj rename to backend/src/MoShiro.Backend.Gateway/MoShiro.Backend.Gateway.csproj index e9f9ceb..d57222a 100644 --- a/backend/src/Monitor.Backend.Gateway/Monitor.Backend.Gateway.csproj +++ b/backend/src/MoShiro.Backend.Gateway/MoShiro.Backend.Gateway.csproj @@ -4,7 +4,7 @@ true - + diff --git a/backend/src/Monitor.Backend.Gateway/ProbeAgentsStateProvider.cs b/backend/src/MoShiro.Backend.Gateway/ProbeAgentsStateProvider.cs similarity index 86% rename from backend/src/Monitor.Backend.Gateway/ProbeAgentsStateProvider.cs rename to backend/src/MoShiro.Backend.Gateway/ProbeAgentsStateProvider.cs index e3f95de..d8f4697 100644 --- a/backend/src/Monitor.Backend.Gateway/ProbeAgentsStateProvider.cs +++ b/backend/src/MoShiro.Backend.Gateway/ProbeAgentsStateProvider.cs @@ -1,10 +1,10 @@ using System.Text.Json; using Fleet.Rpc; using Fleet.Server; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Gateway; +namespace MoShiro.Backend.Gateway; /// 期望状态:为每个 Probe 下发要连接的 Agent 列表(地址、端口、证书指纹)。Agent 集合变化时由 NotifyAllChanged 一起重算推送。 public sealed class ProbeAgentsStateProvider : IStateProvider @@ -26,7 +26,7 @@ public sealed class ProbeAgentsStateProvider : IStateProvider } ProbeAgentList list = await _agents.ProbeAgentsAsync(node.Id.Value, ct); - return new StateDocument(list.Version, JsonSerializer.SerializeToElement(list, MonitorJsonContext.Default.ProbeAgentList)); + return new StateDocument(list.Version, JsonSerializer.SerializeToElement(list, MoShiroJsonContext.Default.ProbeAgentList)); } public ValueTask OnAppliedAsync(NodeRecord node, long version, StateSyncResult result, CancellationToken ct) => ValueTask.CompletedTask; diff --git a/backend/src/Monitor.Backend.Gateway/RawChunkInbox.cs b/backend/src/MoShiro.Backend.Gateway/RawChunkInbox.cs similarity index 84% rename from backend/src/Monitor.Backend.Gateway/RawChunkInbox.cs rename to backend/src/MoShiro.Backend.Gateway/RawChunkInbox.cs index 9aaedc8..5860d9e 100644 --- a/backend/src/Monitor.Backend.Gateway/RawChunkInbox.cs +++ b/backend/src/MoShiro.Backend.Gateway/RawChunkInbox.cs @@ -1,8 +1,8 @@ using Fleet.Server; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Gateway; +namespace MoShiro.Backend.Gateway; /// /// RawChunk 收件箱(二进制通道 16):持久化之后才返回,Fleet 随后才回复 ACK,按 chunk_id 去重。 @@ -17,7 +17,7 @@ public sealed class RawChunkInbox : IInbox _ingest = ingest; } - public byte Channel => MonitorChannels.RawChunk; + public byte Channel => MoShiroChannels.RawChunk; public async ValueTask HandleAsync(NodeRecord node, Guid msgId, ReadOnlyMemory payload, CancellationToken ct) => await _ingest.IngestAsync(node.Id, msgId, payload, ct) switch diff --git a/backend/src/Monitor.Backend.Gateway/TaskSetStateProvider.cs b/backend/src/MoShiro.Backend.Gateway/TaskSetStateProvider.cs similarity index 92% rename from backend/src/Monitor.Backend.Gateway/TaskSetStateProvider.cs rename to backend/src/MoShiro.Backend.Gateway/TaskSetStateProvider.cs index 6f7bfcc..e257f5a 100644 --- a/backend/src/Monitor.Backend.Gateway/TaskSetStateProvider.cs +++ b/backend/src/MoShiro.Backend.Gateway/TaskSetStateProvider.cs @@ -2,10 +2,10 @@ using System.Text.Json; using System.Text.Json.Serialization; using Fleet.Rpc; using Fleet.Server; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Gateway; +namespace MoShiro.Backend.Gateway; /// /// 期望状态:为每个 Agent 下发完整的 快照(有效任务集 + Probe 公钥)。 @@ -32,7 +32,7 @@ public sealed class TaskSetStateProvider : IStateProvider } DesiredTaskSet? desired = await _planner.GetDesiredAsync(node.Id, ct); - return desired is null ? null : new StateDocument(desired.Version, JsonSerializer.SerializeToElement(desired.Document, MonitorJsonContext.Default.TaskSet)); + return desired is null ? null : new StateDocument(desired.Version, JsonSerializer.SerializeToElement(desired.Document, MoShiroJsonContext.Default.TaskSet)); } public async ValueTask OnAppliedAsync(NodeRecord node, long version, StateSyncResult result, CancellationToken ct) diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/Monitor.Backend.Storage.Sqlite.csproj b/backend/src/MoShiro.Backend.Storage.Sqlite/MoShiro.Backend.Storage.Sqlite.csproj similarity index 60% rename from backend/src/Monitor.Backend.Storage.Sqlite/Monitor.Backend.Storage.Sqlite.csproj rename to backend/src/MoShiro.Backend.Storage.Sqlite/MoShiro.Backend.Storage.Sqlite.csproj index 88d4c26..c816f7d 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/Monitor.Backend.Storage.Sqlite.csproj +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/MoShiro.Backend.Storage.Sqlite.csproj @@ -3,10 +3,10 @@ true - + - + diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/Schema.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/Schema.cs similarity index 99% rename from backend/src/Monitor.Backend.Storage.Sqlite/Schema.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/Schema.cs index a1276dd..69b20f4 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/Schema.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/Schema.cs @@ -1,6 +1,6 @@ using Microsoft.Data.Sqlite; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; /// 表结构。版本记录在 PRAGMA user_version;以后的迁移是按版本号追加的内置 SQL。 internal static class Schema diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/Sql.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/Sql.cs similarity index 98% rename from backend/src/Monitor.Backend.Storage.Sqlite/Sql.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/Sql.cs index 6819ac9..d6751c1 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/Sql.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/Sql.cs @@ -1,6 +1,6 @@ using Microsoft.Data.Sqlite; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; /// 手写的参数化 SQL 的小帮手:禁止字符串拼接值,所有值都经过参数。 internal static class Sql diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteAgentStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteAgentStore.cs similarity index 94% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteAgentStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteAgentStore.cs index 9685b25..1774315 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteAgentStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteAgentStore.cs @@ -1,14 +1,14 @@ using Fleet.Core; using Fleet.Server; using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; /// -/// Agent 表同时是 Fleet 的节点表:实现 (Monitor 的视图)和 (注册与认证)。 -/// 加入令牌的校验也在这里,和 Monitor 的其他数据放在同一个 monitor.db 里,由同一个单写入者写入。 +/// Agent 表同时是 Fleet 的节点表:实现 (MoShiro 的视图)和 (注册与认证)。 +/// 加入令牌的校验也在这里,和 MoShiro 的其他数据放在同一个 moshiro.db 里,由同一个单写入者写入。 /// public sealed class SqliteAgentStore : IAgentStore, IFleetStore { @@ -47,17 +47,17 @@ public sealed class SqliteAgentStore : IAgentStore, IFleetStore AgentRecord? current = GetInternal(c, id); if (current is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "agent not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "agent not found"); } if (expectedRowVersion is { } expected && expected != current.RowVersion) { - return Result.Fail(MonitorErrors.PreconditionFailed, "the agent was modified by someone else"); + return Result.Fail(MoShiroErrors.PreconditionFailed, "the agent was modified by someone else"); } if (patch.Name is { } name && name != current.Name && current.Status != NodeStatus.Pending && NameTaken(c, name, id)) { - return Result.Fail(MonitorErrors.NameConflict, "another agent already uses this name"); + return Result.Fail(MoShiroErrors.NameConflict, "another agent already uses this name"); } c.Exec( @@ -74,12 +74,12 @@ public sealed class SqliteAgentStore : IAgentStore, IFleetStore AgentRecord? current = GetInternal(c, id); if (current is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "agent not found"); + return Result.Fail(MoShiroErrors.AgentNotFound, "agent not found"); } if (status != NodeStatus.Pending && NameTaken(c, current.Name, id)) { - return Result.Fail(MonitorErrors.NameConflict, "another agent already uses this name; rename it or take over the existing node"); + return Result.Fail(MoShiroErrors.NameConflict, "another agent already uses this name; rename it or take over the existing node"); } c.Exec("UPDATE agents SET status = @status, row_version = row_version + 1 WHERE id = @id", ("@id", id.Value), ("@status", StatusText(status))); @@ -92,13 +92,13 @@ public sealed class SqliteAgentStore : IAgentStore, IFleetStore AgentRecord? pending = GetInternal(c, pendingId); if (pending is null || pending.Status != NodeStatus.Pending) { - return Result.Fail(MonitorErrors.AgentNotFound, "no pending agent with this id"); + return Result.Fail(MoShiroErrors.AgentNotFound, "no pending agent with this id"); } string? existingId = c.Scalar("SELECT id FROM agents WHERE name = @name AND status <> 'pending' AND id <> @id", ("@name", pending.Name), ("@id", pendingId.Value)); if (existingId is null) { - return Result.Fail(MonitorErrors.AgentNotFound, "there is no established agent with this name to take over"); + return Result.Fail(MoShiroErrors.AgentNotFound, "there is no established agent with this name to take over"); } // 旧记录换成新公钥,历史数据保留;待审批记录删除 diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteAuthStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteAuthStore.cs similarity index 98% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteAuthStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteAuthStore.cs index 683f477..b9a7092 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteAuthStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteAuthStore.cs @@ -1,7 +1,7 @@ using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteAuthStore : IAuthStore { diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteConfigStores.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteConfigStores.cs similarity index 97% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteConfigStores.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteConfigStores.cs index 96e0f55..e62854b 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteConfigStores.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteConfigStores.cs @@ -1,10 +1,10 @@ using System.Text.Json; using Fleet.Core; using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteJoinTokenStore : IJoinTokenStore { @@ -132,12 +132,12 @@ public sealed class SqliteTaskStore : ITaskStore TaskRecord? current = c.QueryOne($"SELECT {Columns} FROM tasks WHERE id = @id", Read, ("@id", task.Id.Value)); if (current is null) { - return Result.Fail(MonitorErrors.TaskNotFound, "task not found"); + return Result.Fail(MoShiroErrors.TaskNotFound, "task not found"); } if (expectedRowVersion is { } expected && expected != current.RowVersion) { - return Result.Fail(MonitorErrors.PreconditionFailed, "the task was modified by someone else"); + return Result.Fail(MoShiroErrors.PreconditionFailed, "the task was modified by someone else"); } c.Exec( diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteDatabase.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteDatabase.cs similarity index 98% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteDatabase.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteDatabase.cs index a433ffc..e7a2217 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteDatabase.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteDatabase.cs @@ -1,7 +1,7 @@ using System.Threading.Channels; using Microsoft.Data.Sqlite; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; /// /// 连接与单写入者:所有写操作经过一个有界 Channel,由一个后台循环依次执行(每个操作一个事务); @@ -27,7 +27,7 @@ public sealed class SqliteDatabase : IDisposable, IAsyncDisposable RawDirectory = Path.Combine(DataDirectory, "raw"); Directory.CreateDirectory(RawDirectory); - string path = Path.Combine(DataDirectory, "monitor.db"); + string path = Path.Combine(DataDirectory, "moshiro.db"); _readConnectionString = new SqliteConnectionStringBuilder { DataSource = path, Mode = SqliteOpenMode.ReadOnly, Pooling = true }.ToString(); _writer = new SqliteConnection(new SqliteConnectionStringBuilder { DataSource = path, Mode = SqliteOpenMode.ReadWriteCreate, Pooling = false }.ToString()); diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteHostStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteHostStore.cs similarity index 95% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteHostStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteHostStore.cs index 06d9c89..3aded0d 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteHostStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteHostStore.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteHostStore : IHostStore { diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteMetricStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteMetricStore.cs similarity index 99% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteMetricStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteMetricStore.cs index b2cd990..155faf1 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteMetricStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteMetricStore.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Data.Sqlite; -using Monitor.Backend.Analysis; -using Monitor.Backend.Application; +using MoShiro.Backend.Analysis; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; /// 指标窗口的通用存储:每个桶一行,内容是 指标 id → 累加器状态(对本层不透明,只在合并和补丁时解码)。 public sealed class SqliteMetricStore : IMetricStore diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteNodeMetaStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteNodeMetaStore.cs similarity index 96% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteNodeMetaStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteNodeMetaStore.cs index 4dc92dc..c2d8c63 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteNodeMetaStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteNodeMetaStore.cs @@ -1,7 +1,7 @@ using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteNodeMetaStore : INodeMetaStore { diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteRawStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteRawStore.cs similarity index 99% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteRawStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteRawStore.cs index 9787003..a667d99 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteRawStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteRawStore.cs @@ -2,10 +2,10 @@ using System.Text; using System.Text.RegularExpressions; using Fleet.Core; using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; /// /// 原始块的存储:块原样追加写入段文件(块本身带 crc 并且自描述),SQLite 只记录 (segment, offset, length)。 diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteStorageHealth.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteStorageHealth.cs similarity index 93% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteStorageHealth.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteStorageHealth.cs index 67dc4c7..083d294 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteStorageHealth.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteStorageHealth.cs @@ -1,7 +1,7 @@ using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteStorageHealth : IStorageHealth { diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteTestStore.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteTestStore.cs similarity index 98% rename from backend/src/Monitor.Backend.Storage.Sqlite/SqliteTestStore.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/SqliteTestStore.cs index fb59d69..b763b41 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/SqliteTestStore.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/SqliteTestStore.cs @@ -1,9 +1,9 @@ using Fleet.Core; using Microsoft.Data.Sqlite; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public sealed class SqliteTestStore : ITestStore { diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/StorageJson.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/StorageJson.cs similarity index 95% rename from backend/src/Monitor.Backend.Storage.Sqlite/StorageJson.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/StorageJson.cs index 3259e7a..51b9490 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/StorageJson.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/StorageJson.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] diff --git a/backend/src/Monitor.Backend.Storage.Sqlite/StorageServiceCollectionExtensions.cs b/backend/src/MoShiro.Backend.Storage.Sqlite/StorageServiceCollectionExtensions.cs similarity index 91% rename from backend/src/Monitor.Backend.Storage.Sqlite/StorageServiceCollectionExtensions.cs rename to backend/src/MoShiro.Backend.Storage.Sqlite/StorageServiceCollectionExtensions.cs index ee7402a..290fda9 100644 --- a/backend/src/Monitor.Backend.Storage.Sqlite/StorageServiceCollectionExtensions.cs +++ b/backend/src/MoShiro.Backend.Storage.Sqlite/StorageServiceCollectionExtensions.cs @@ -1,13 +1,13 @@ using Fleet.Server; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend.Storage.Sqlite; +namespace MoShiro.Backend.Storage.Sqlite; public static class StorageServiceCollectionExtensions { /// 注册 SQLite + 段文件存储:实现 Application 的全部存储端口,同时实现 Fleet 的 。 - public static IServiceCollection AddMonitorSqlite(this IServiceCollection services, string dataDirectory) + public static IServiceCollection AddMoShiroSqlite(this IServiceCollection services, string dataDirectory) { services.AddSingleton(_ => new SqliteDatabase(dataDirectory)); services.AddSingleton(); diff --git a/backend/src/Monitor.Backend/BackendHost.cs b/backend/src/MoShiro.Backend/BackendHost.cs similarity index 79% rename from backend/src/Monitor.Backend/BackendHost.cs rename to backend/src/MoShiro.Backend/BackendHost.cs index dd14010..3b42a1a 100644 --- a/backend/src/Monitor.Backend/BackendHost.cs +++ b/backend/src/MoShiro.Backend/BackendHost.cs @@ -3,20 +3,20 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Monitor.Backend.Api; -using Monitor.Backend.Application; -using Monitor.Backend.Gateway; -using Monitor.Backend.Storage.Sqlite; +using MoShiro.Backend.Api; +using MoShiro.Backend.Application; +using MoShiro.Backend.Gateway; +using MoShiro.Backend.Storage.Sqlite; -namespace Monitor.Backend; +namespace MoShiro.Backend; /// -/// 组合根:在这里显式注册所有层。配置来自 appsettings.json、环境变量(Monitor__DataDirectory 等)和命令行。 +/// 组合根:在这里显式注册所有层。配置来自 appsettings.json、环境变量(MoShiro__DataDirectory 等)和命令行。 /// 测试用 在真实端口上启动整个后端。 /// public static class BackendHost { - public const string ConfigurationSection = "Monitor"; + public const string ConfigurationSection = "MoShiro"; public const int MaxRequestBodyBytes = 64 * 1024; public static WebApplication Build(string[] args, Action? configureOptions = null, TimeProvider? time = null) @@ -37,7 +37,7 @@ public static class BackendHost IServiceCollection services = builder.Services; services.AddSingleton(time ?? TimeProvider.System); - services.AddMonitorSqlite(options.DataDirectory); + services.AddMoShiroSqlite(options.DataDirectory); // 国家和运营商:Backend 用节点的来源地址查一次(Probe、Agent 不访问任何外部服务);地址留空就不查 if (string.IsNullOrWhiteSpace(options.GeoLookupUrl)) { @@ -48,16 +48,16 @@ public static class BackendHost services.AddSingleton(new HttpGeoLookup(options.GeoLookupUrl)); } - services.AddMonitorApplication(options); - services.AddMonitorGateway(); - services.AddMonitorApi(); + services.AddMoShiroApplication(options); + services.AddMoShiroGateway(); + services.AddMoShiroApi(); WebApplication app = builder.Build(); // 静默死亡的 Agent(断电、断网)不会关闭连接:15 秒一个 Ping,20 秒没有 Pong 就关闭,这样面板上的在线状态才可信 app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(15), KeepAliveTimeout = TimeSpan.FromSeconds(20) }); - app.UseMonitorCors(options.CorsOrigins); - app.UseMonitorApi(); - app.MapMonitorApi(); + app.UseMoShiroCors(options.CorsOrigins); + app.UseMoShiroApi(); + app.MapMoShiroApi(); app.MapFleetEnrollment("/api/agent/v1/enroll"); app.MapFleetLink("/api/agent/v1/rpc"); return app; diff --git a/backend/src/Monitor.Backend/HttpGeoLookup.cs b/backend/src/MoShiro.Backend/HttpGeoLookup.cs similarity index 93% rename from backend/src/Monitor.Backend/HttpGeoLookup.cs rename to backend/src/MoShiro.Backend/HttpGeoLookup.cs index d8b50e2..234b699 100644 --- a/backend/src/Monitor.Backend/HttpGeoLookup.cs +++ b/backend/src/MoShiro.Backend/HttpGeoLookup.cs @@ -1,7 +1,7 @@ using System.Text.Json; -using Monitor.Backend.Application; +using MoShiro.Backend.Application; -namespace Monitor.Backend; +namespace MoShiro.Backend; /// /// 用 IP 归属查询服务(GET {地址}/lookup?ip=…)查国家和运营商。 @@ -14,7 +14,7 @@ public sealed class HttpGeoLookup : IGeoLookup, IDisposable public HttpGeoLookup(string baseUrl) { _http = new HttpClient { BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/"), Timeout = TimeSpan.FromSeconds(6) }; - _http.DefaultRequestHeaders.UserAgent.ParseAdd("MyMonitor"); + _http.DefaultRequestHeaders.UserAgent.ParseAdd("MoShiro"); } public async ValueTask LookupAsync(string host, CancellationToken ct) @@ -66,7 +66,7 @@ public sealed class HttpGeoLookup : IGeoLookup, IDisposable public void Dispose() => _http.Dispose(); } -/// 不查(Monitor__GeoLookupUrl 留空)。 +/// 不查(MoShiro__GeoLookupUrl 留空)。 public sealed class NoGeoLookup : IGeoLookup { public ValueTask LookupAsync(string host, CancellationToken ct) => ValueTask.FromResult(null); diff --git a/backend/src/Monitor.Backend/Monitor.Backend.csproj b/backend/src/MoShiro.Backend/MoShiro.Backend.csproj similarity index 85% rename from backend/src/Monitor.Backend/Monitor.Backend.csproj rename to backend/src/MoShiro.Backend/MoShiro.Backend.csproj index 93afdac..5492665 100644 --- a/backend/src/Monitor.Backend/Monitor.Backend.csproj +++ b/backend/src/MoShiro.Backend/MoShiro.Backend.csproj @@ -3,8 +3,8 @@ Exe - monitor-backend - Monitor.Backend + moshiro-backend + MoShiro.Backend true true Size @@ -28,9 +28,9 @@ - - - + + + diff --git a/backend/src/Monitor.Backend/Program.cs b/backend/src/MoShiro.Backend/Program.cs similarity index 65% rename from backend/src/Monitor.Backend/Program.cs rename to backend/src/MoShiro.Backend/Program.cs index 2a3a592..2d22be8 100644 --- a/backend/src/Monitor.Backend/Program.cs +++ b/backend/src/MoShiro.Backend/Program.cs @@ -1,3 +1,3 @@ -using Monitor.Backend; +using MoShiro.Backend; await BackendHost.Build(args).RunAsync(); diff --git a/backend/src/Monitor.Backend/StaticSqlite.targets b/backend/src/MoShiro.Backend/StaticSqlite.targets similarity index 90% rename from backend/src/Monitor.Backend/StaticSqlite.targets rename to backend/src/MoShiro.Backend/StaticSqlite.targets index e1c17d8..1a6f75a 100644 --- a/backend/src/Monitor.Backend/StaticSqlite.targets +++ b/backend/src/MoShiro.Backend/StaticSqlite.targets @@ -14,20 +14,20 @@ '$(RuntimeIdentifier)' != 'win-x64' and '$(RuntimeIdentifier)' != 'win-arm64'" Text="Static SQLite is configured for the eight supported x64/arm64 Linux, musl, macOS and Windows targets. Unsupported RID: $(RuntimeIdentifier)." /> - <_SqliteCache>$([System.IO.Path]::GetFullPath('$(BaseIntermediateOutputPath)sqlite/$(MonitorSqliteSourceVersion)/')) - <_SqliteArchive>$(_SqliteCache)sqlite-amalgamation-$(MonitorSqliteSourceVersion).zip - <_SqliteSource>$(_SqliteCache)sqlite-amalgamation-$(MonitorSqliteSourceVersion)/sqlite3.c + <_SqliteCache>$([System.IO.Path]::GetFullPath('$(BaseIntermediateOutputPath)sqlite/$(MoShiroSqliteSourceVersion)/')) + <_SqliteArchive>$(_SqliteCache)sqlite-amalgamation-$(MoShiroSqliteSourceVersion).zip + <_SqliteSource>$(_SqliteCache)sqlite-amalgamation-$(MoShiroSqliteSourceVersion)/sqlite3.c <_SqliteObjectDirectory>$(_SqliteCache)$(RuntimeIdentifier)/$(UseAotAnywhere)/ <_SqliteObject>$(_SqliteObjectDirectory)e_sqlite3.o <_SqliteObject Condition="$(RuntimeIdentifier.StartsWith('win'))">$(_SqliteObjectDirectory)e_sqlite3.obj - - @@ -45,7 +45,7 @@ <_SqliteCompiler Condition="'$(UseAotAnywhere)' == 'true'">"$(_AotAnywhereZigExe)" cc -target $(TargetTriple) <_SqliteCompiler Condition="'$(_SqliteUseMsvc)' == 'true'">cl - + - - + + diff --git a/backend/tests/Monitor.Backend.Analysis.Tests/ModelAndTestEvaluatorTests.cs b/backend/tests/MoShiro.Backend.Analysis.Tests/ModelAndTestEvaluatorTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Analysis.Tests/ModelAndTestEvaluatorTests.cs rename to backend/tests/MoShiro.Backend.Analysis.Tests/ModelAndTestEvaluatorTests.cs index 535351e..487311f 100644 --- a/backend/tests/Monitor.Backend.Analysis.Tests/ModelAndTestEvaluatorTests.cs +++ b/backend/tests/MoShiro.Backend.Analysis.Tests/ModelAndTestEvaluatorTests.cs @@ -1,8 +1,8 @@ using Fleet.Core; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Analysis.Tests; +namespace MoShiro.Backend.Analysis.Tests; public sealed class ModelAndTestEvaluatorTests { diff --git a/backend/tests/Monitor.Backend.Analysis.Tests/OpTests.cs b/backend/tests/MoShiro.Backend.Analysis.Tests/OpTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Analysis.Tests/OpTests.cs rename to backend/tests/MoShiro.Backend.Analysis.Tests/OpTests.cs index 5fc1c18..850e2d1 100644 --- a/backend/tests/Monitor.Backend.Analysis.Tests/OpTests.cs +++ b/backend/tests/MoShiro.Backend.Analysis.Tests/OpTests.cs @@ -1,8 +1,8 @@ using Fleet.Core; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Analysis.Tests; +namespace MoShiro.Backend.Analysis.Tests; public sealed class OpTests { diff --git a/backend/tests/Monitor.Backend.Tests/AgentEndToEndTests.cs b/backend/tests/MoShiro.Backend.Tests/AgentEndToEndTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/AgentEndToEndTests.cs rename to backend/tests/MoShiro.Backend.Tests/AgentEndToEndTests.cs index 423762d..7f2e0ec 100644 --- a/backend/tests/Monitor.Backend.Tests/AgentEndToEndTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/AgentEndToEndTests.cs @@ -1,9 +1,9 @@ using System.Text.Json.Nodes; -using Monitor.Measurement; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Measurement; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 真实的后端 + 真实的 Agent(进程内)+ 假 Probe:覆盖 Agent 与 Backend 的链路、Probe 与 Agent 的点对点会话和数据面。 [Collection("backend")] diff --git a/backend/tests/Monitor.Backend.Tests/AgentHarness.cs b/backend/tests/MoShiro.Backend.Tests/AgentHarness.cs similarity index 96% rename from backend/tests/Monitor.Backend.Tests/AgentHarness.cs rename to backend/tests/MoShiro.Backend.Tests/AgentHarness.cs index 0fff698..e83a6d8 100644 --- a/backend/tests/Monitor.Backend.Tests/AgentHarness.cs +++ b/backend/tests/MoShiro.Backend.Tests/AgentHarness.cs @@ -3,11 +3,11 @@ using Fleet.Core; using Fleet.Enrollment.Client; using Fleet.Hosting; using Fleet.Identity; -using Monitor.Agent; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Agent; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// /// 进程内的真实 Agent:用 注册(和 fleet-enroll 同一份代码)、写出身份目录、 @@ -43,7 +43,7 @@ public sealed class AgentHarness : IAsyncDisposable public static async Task StartAsync(BackendFixture backend, Action? configure = null, string? name = null) { - string directory = Path.Combine(Path.GetTempPath(), "monitor-agent-" + Guid.NewGuid().ToString("N")); + string directory = Path.Combine(Path.GetTempPath(), "moshiro-agent-" + Guid.NewGuid().ToString("N")); string token = await backend.CreateJoinTokenAsync(); using var http = new HttpClient(); Result enrolled = await EnrollmentClient.EnrollAsync(http, backend.BaseAddress, token, name ?? backend.UniqueName("agent"), directory, AgentApp.Version, null, TimeProvider.System, CancellationToken.None); diff --git a/backend/tests/Monitor.Backend.Tests/AotSmokeTests.cs b/backend/tests/MoShiro.Backend.Tests/AotSmokeTests.cs similarity index 96% rename from backend/tests/Monitor.Backend.Tests/AotSmokeTests.cs rename to backend/tests/MoShiro.Backend.Tests/AotSmokeTests.cs index 5c434d4..86204ec 100644 --- a/backend/tests/Monitor.Backend.Tests/AotSmokeTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/AotSmokeTests.cs @@ -1,24 +1,24 @@ using System.Net.Http.Headers; using System.Text.Json.Nodes; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// /// 对一个正在运行的 NativeAOT 发布产物跑一遍真实的 Agent 流程(注册、WebSocket、状态同步、二进制收件箱、查询、SSE、按需测试)。 -/// 只有设置了 MONITOR_AOT_URL 才会运行: -/// dotnet publish backend/src/Monitor.Backend -r win-x64 -p:PublishAot=true,启动产物,再设置该环境变量后运行本测试。 +/// 只有设置了 MOSHIRO_AOT_URL 才会运行: +/// dotnet publish backend/src/MoShiro.Backend -r win-x64 -p:PublishAot=true,启动产物,再设置该环境变量后运行本测试。 /// 管理员凭据与测试夹具相同(用户 admin,口令见 )。 /// public sealed class AotSmokeTests { - private static string? Url => Environment.GetEnvironmentVariable("MONITOR_AOT_URL"); + private static string? Url => Environment.GetEnvironmentVariable("MOSHIRO_AOT_URL"); [Fact] public async Task TheNativeBinary_ServesTheWholeAgentWorkflow() { - Assert.SkipUnless(Url is not null, "set MONITOR_AOT_URL to the address of a running NativeAOT build to run this test"); + Assert.SkipUnless(Url is not null, "set MOSHIRO_AOT_URL to the address of a running NativeAOT build to run this test"); var baseAddress = new Uri(Url!); using var http = new HttpClient { BaseAddress = baseAddress }; diff --git a/backend/tests/Monitor.Backend.Tests/ApiBasicsTests.cs b/backend/tests/MoShiro.Backend.Tests/ApiBasicsTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Tests/ApiBasicsTests.cs rename to backend/tests/MoShiro.Backend.Tests/ApiBasicsTests.cs index 06dbe59..6774085 100644 --- a/backend/tests/Monitor.Backend.Tests/ApiBasicsTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/ApiBasicsTests.cs @@ -2,7 +2,7 @@ using System.Net; using System.Net.Http.Headers; using System.Text.Json.Nodes; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("backend")] public sealed class ApiBasicsTests @@ -217,8 +217,8 @@ public sealed class ApiBasicsTests using HttpResponseMessage allowed = await _backend.Admin.GetAsync("/metrics"); string text = await allowed.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, allowed.StatusCode); - Assert.Contains("monitor_agents_online", text); - Assert.Contains("monitor_analysis_queue_length", text); + Assert.Contains("moshiro_agents_online", text); + Assert.Contains("moshiro_analysis_queue_length", text); } [Fact] diff --git a/backend/tests/Monitor.Backend.Tests/ArchitectureTests.cs b/backend/tests/MoShiro.Backend.Tests/ArchitectureTests.cs similarity index 81% rename from backend/tests/Monitor.Backend.Tests/ArchitectureTests.cs rename to backend/tests/MoShiro.Backend.Tests/ArchitectureTests.cs index cf81a17..83096a4 100644 --- a/backend/tests/Monitor.Backend.Tests/ArchitectureTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/ArchitectureTests.cs @@ -1,6 +1,6 @@ using System.Xml.Linq; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// /// 依赖规则(docs/project-layout.md 第 3 节):由测试在构建期强制,等价于文档里的 eng/check-deps。 @@ -17,7 +17,8 @@ public sealed class ArchitectureTests private static string FindRoot() { string? dir = AppContext.BaseDirectory; - while (dir is not null && !File.Exists(Path.Combine(dir, "global.json"))) + // backend/ 目录的标志:BannedSymbols.txt 只在这里(global.json 和 NuGet 版本表已经挪到仓库根目录) + while (dir is not null && !File.Exists(Path.Combine(dir, "BannedSymbols.txt"))) { dir = Path.GetDirectoryName(dir); } @@ -48,17 +49,17 @@ public sealed class ArchitectureTests private static bool IsFleet(Project p) => p.Name.StartsWith("Fleet.", StringComparison.Ordinal) && !IsTest(p); [Fact] - public void Fleet_NeverReferencesMonitor_InProjectsOrCode() + public void Fleet_NeverReferencesMoShiro_InProjectsOrCode() { foreach (Project project in LoadProjects().Where(IsFleet)) { - Assert.DoesNotContain(project.ProjectReferences, r => r.StartsWith("Monitor.", StringComparison.Ordinal)); + Assert.DoesNotContain(project.ProjectReferences, r => r.StartsWith("MoShiro.", StringComparison.Ordinal)); foreach (string file in Directory.EnumerateFiles(project.Directory, "*.cs", SearchOption.AllDirectories).Where(f => !f.Contains(Path.DirectorySeparatorChar + "obj" + Path.DirectorySeparatorChar, StringComparison.Ordinal))) { string text = File.ReadAllText(file); - Assert.False(text.Contains("using Monitor.", StringComparison.Ordinal) || text.Contains("namespace Monitor.", StringComparison.Ordinal), $"{file} refers to Monitor.*"); - Assert.False(text.Contains("monitor.", StringComparison.Ordinal) && text.Contains("\"monitor.", StringComparison.Ordinal), $"{file} hard-codes a Monitor method or channel name"); - Assert.False(text.Contains("7443", StringComparison.Ordinal), $"{file} hard-codes a Monitor port"); + Assert.False(text.Contains("using MoShiro.", StringComparison.Ordinal) || text.Contains("namespace MoShiro.", StringComparison.Ordinal), $"{file} refers to MoShiro.*"); + Assert.False(text.Contains("moshiro.", StringComparison.Ordinal) && text.Contains("\"moshiro.", StringComparison.Ordinal), $"{file} hard-codes a MoShiro method or channel name"); + Assert.False(text.Contains("7443", StringComparison.Ordinal), $"{file} hard-codes a MoShiro port"); } } } @@ -66,7 +67,7 @@ public sealed class ArchitectureTests [Fact] public void NodeSideLibraries_HaveNoRuntimePackages_AndUseTheNodeBanList() { - string[] nodeSide = ["Fleet.Core", "Fleet.Identity", "Fleet.Rpc", "Monitor.Protocol", "Monitor.Backend.Analysis"]; + string[] nodeSide = ["Fleet.Core", "Fleet.Identity", "Fleet.Rpc", "MoShiro.Protocol", "MoShiro.Backend.Analysis"]; foreach (Project project in LoadProjects().Where(p => nodeSide.Contains(p.Name))) { Assert.All(project.Packages, package => Assert.Contains(package, s_allowedAnalyzers)); @@ -83,8 +84,8 @@ public sealed class ArchitectureTests public void Protocol_DependsOnlyOnFleetCoreAndRpc_AndAnalysisOnlyOnProtocol() { List projects = LoadProjects(); - Assert.Equal(["Fleet.Core", "Fleet.Rpc"], projects.Single(p => p.Name == "Monitor.Protocol").ProjectReferences.Order().ToArray()); - Assert.Equal(["Monitor.Protocol"], projects.Single(p => p.Name == "Monitor.Backend.Analysis").ProjectReferences.ToArray()); + Assert.Equal(["Fleet.Core", "Fleet.Rpc"], projects.Single(p => p.Name == "MoShiro.Protocol").ProjectReferences.Order().ToArray()); + Assert.Equal(["MoShiro.Protocol"], projects.Single(p => p.Name == "MoShiro.Backend.Analysis").ProjectReferences.ToArray()); } [Fact] @@ -94,19 +95,19 @@ public sealed class ArchitectureTests Project Get(string name) => projects.Single(p => p.Name == name); // Agent 是纯采集进程:不引用后端的任何层,也不引用 Fleet 的服务端库 - Project agent = Get("Monitor.Agent"); - Assert.DoesNotContain(agent.ProjectReferences, r => r.StartsWith("Monitor.Backend", StringComparison.Ordinal) || r.StartsWith("Fleet.Server", StringComparison.Ordinal) || r.StartsWith("Fleet.Enrollment", StringComparison.Ordinal)); - Assert.Equal(["Fleet.Hosting", "Fleet.Link", "Monitor.Measurement"], agent.ProjectReferences.Order().ToArray()); + Project agent = Get("MoShiro.Agent"); + Assert.DoesNotContain(agent.ProjectReferences, r => r.StartsWith("MoShiro.Backend", StringComparison.Ordinal) || r.StartsWith("Fleet.Server", StringComparison.Ordinal) || r.StartsWith("Fleet.Enrollment", StringComparison.Ordinal)); + Assert.Equal(["Fleet.Hosting", "Fleet.Link", "MoShiro.Measurement"], agent.ProjectReferences.Order().ToArray()); // Probe 同理,并且不认识 Backend 和 Agent - Project probe = Get("Monitor.Probe"); - Assert.Equal(["Fleet.Hosting", "Fleet.Link", "Monitor.Measurement"], probe.ProjectReferences.Order().ToArray()); + Project probe = Get("MoShiro.Probe"); + Assert.Equal(["Fleet.Hosting", "Fleet.Link", "MoShiro.Measurement"], probe.ProjectReferences.Order().ToArray()); // 两个节点的共享库只依赖协议 - Assert.Equal(["Monitor.Protocol"], Get("Monitor.Measurement").ProjectReferences.ToArray()); + Assert.Equal(["MoShiro.Protocol"], Get("MoShiro.Measurement").ProjectReferences.ToArray()); // 节点一侧的库没有运行时依赖包,并使用节点禁用列表(不能用 HttpClient、Process、写文件;唯一的写文件豁免在 ConfigStore) - foreach (string name in new[] { "Fleet.Link", "Fleet.Hosting", "Monitor.Measurement", "Monitor.Agent", "Monitor.Probe" }) + foreach (string name in new[] { "Fleet.Link", "Fleet.Hosting", "MoShiro.Measurement", "MoShiro.Agent", "MoShiro.Probe" }) { Project project = Get(name); Assert.All(project.Packages, package => Assert.Contains(package, s_allowedAnalyzers)); @@ -126,7 +127,7 @@ public sealed class ArchitectureTests { // 采集层(协议、积木、Agent、Probe、Fleet)只认识机制(传输、节奏、方向、时间戳)和原始列; // REALITY、Hysteria2、Minecraft 这些业务概念以及由原语编排成的测试方案只存在于 Backend。 - string[] collectionRoots = ["shared/Monitor.Protocol", "shared/Monitor.Measurement", "agent/src", "probe/src", "fleet/src"]; + string[] collectionRoots = ["shared/MoShiro.Protocol", "shared/MoShiro.Measurement", "agent/src", "probe/src", "fleet/src"]; string[] forbidden = ["reality", "hysteria", "hy2", "minecraft", "brutal", "PlanSpec", "PhaseSpec"]; foreach (string root in collectionRoots) { @@ -142,7 +143,7 @@ public sealed class ArchitectureTests } // 派生量(丢包率、RTT、抖动)由 Backend 的分析层算:采集层的积木不引用分析层 - Assert.DoesNotContain(LoadProjects().Single(p => p.Name == "Monitor.Measurement").ProjectReferences, r => r.Contains("Analysis", StringComparison.Ordinal)); + Assert.DoesNotContain(LoadProjects().Single(p => p.Name == "MoShiro.Measurement").ProjectReferences, r => r.Contains("Analysis", StringComparison.Ordinal)); } [Fact] @@ -152,22 +153,22 @@ public sealed class ArchitectureTests Project Get(string name) => projects.Single(p => p.Name == name); // Api 不能引用 Storage(也不引用 Gateway):只调用 Application - Assert.DoesNotContain(Get("Monitor.Backend.Api").ProjectReferences, r => r.Contains("Storage", StringComparison.Ordinal) || r.Contains("Gateway", StringComparison.Ordinal)); - Assert.Equal(["Monitor.Backend.Application"], Get("Monitor.Backend.Api").ProjectReferences.ToArray()); + Assert.DoesNotContain(Get("MoShiro.Backend.Api").ProjectReferences, r => r.Contains("Storage", StringComparison.Ordinal) || r.Contains("Gateway", StringComparison.Ordinal)); + Assert.Equal(["MoShiro.Backend.Application"], Get("MoShiro.Backend.Api").ProjectReferences.ToArray()); // Application 不依赖任何下游层 - Assert.DoesNotContain(Get("Monitor.Backend.Application").ProjectReferences, r => r.Contains("Storage", StringComparison.Ordinal) || r.Contains("Api", StringComparison.Ordinal) || r.Contains("Gateway", StringComparison.Ordinal)); + Assert.DoesNotContain(Get("MoShiro.Backend.Application").ProjectReferences, r => r.Contains("Storage", StringComparison.Ordinal) || r.Contains("Api", StringComparison.Ordinal) || r.Contains("Gateway", StringComparison.Ordinal)); // Gateway 只通过 Application 与 Fleet.Server 工作 - Assert.DoesNotContain(Get("Monitor.Backend.Gateway").ProjectReferences, r => r.Contains("Storage", StringComparison.Ordinal) || r.Contains("Backend.Api", StringComparison.Ordinal)); + Assert.DoesNotContain(Get("MoShiro.Backend.Gateway").ProjectReferences, r => r.Contains("Storage", StringComparison.Ordinal) || r.Contains("Backend.Api", StringComparison.Ordinal)); // Storage 实现 Application 的端口,不认识 Api 和 Gateway - Assert.DoesNotContain(Get("Monitor.Backend.Storage.Sqlite").ProjectReferences, r => r.Contains("Backend.Api", StringComparison.Ordinal) || r.Contains("Gateway", StringComparison.Ordinal)); + Assert.DoesNotContain(Get("MoShiro.Backend.Storage.Sqlite").ProjectReferences, r => r.Contains("Backend.Api", StringComparison.Ordinal) || r.Contains("Gateway", StringComparison.Ordinal)); // 只有宿主(组合根)认识所有层 Assert.Equivalent( - new[] { "Monitor.Backend.Api", "Monitor.Backend.Gateway", "Monitor.Backend.Storage.Sqlite" }, - Get("Monitor.Backend").ProjectReferences.ToArray()); + new[] { "MoShiro.Backend.Api", "MoShiro.Backend.Gateway", "MoShiro.Backend.Storage.Sqlite" }, + Get("MoShiro.Backend").ProjectReferences.ToArray()); } [Fact] @@ -175,7 +176,7 @@ public sealed class ArchitectureTests { string[] allowed = [ - "Microsoft.CodeAnalysis.BannedApiAnalyzers", "Microsoft.Data.Sqlite", "Microsoft.AspNetCore.OpenApi", + "Microsoft.CodeAnalysis.BannedApiAnalyzers", "Microsoft.Data.Sqlite", "SQLitePCLRaw.lib.e_sqlite3", "Microsoft.AspNetCore.OpenApi", "Microsoft.Extensions.ApiDescription.Server", "xunit.v3", "Microsoft.Extensions.TimeProvider.Testing", ]; foreach (Project project in LoadProjects()) @@ -187,7 +188,7 @@ public sealed class ArchitectureTests [Fact] public void ApplicationAndApi_NeverTouchSqliteOrTheFileSystemDirectly() { - foreach (string layer in new[] { "Monitor.Backend.Application", "Monitor.Backend.Api", "Monitor.Backend.Gateway", "Monitor.Backend.Analysis" }) + foreach (string layer in new[] { "MoShiro.Backend.Application", "MoShiro.Backend.Api", "MoShiro.Backend.Gateway", "MoShiro.Backend.Analysis" }) { string dir = Path.Combine(s_root, "src", layer); foreach (string file in Directory.EnumerateFiles(dir, "*.cs", SearchOption.AllDirectories).Where(f => !f.Contains(Path.DirectorySeparatorChar + "obj" + Path.DirectorySeparatorChar, StringComparison.Ordinal))) @@ -204,7 +205,7 @@ public sealed class ArchitectureTests public void NoProductionCode_BuildsSqlByConcatenatingValues() { // 手写的参数化 SQL:只有表名可以来自常量;值一律通过参数 - string dir = Path.Combine(s_root, "src", "Monitor.Backend.Storage.Sqlite"); + string dir = Path.Combine(s_root, "src", "MoShiro.Backend.Storage.Sqlite"); foreach (string file in Directory.EnumerateFiles(dir, "*.cs", SearchOption.AllDirectories).Where(f => !f.Contains(Path.DirectorySeparatorChar + "obj" + Path.DirectorySeparatorChar, StringComparison.Ordinal))) { string text = File.ReadAllText(file); diff --git a/backend/tests/Monitor.Backend.Tests/BackendFixture.cs b/backend/tests/MoShiro.Backend.Tests/BackendFixture.cs similarity index 96% rename from backend/tests/Monitor.Backend.Tests/BackendFixture.cs rename to backend/tests/MoShiro.Backend.Tests/BackendFixture.cs index 86c2970..47120ab 100644 --- a/backend/tests/Monitor.Backend.Tests/BackendFixture.cs +++ b/backend/tests/MoShiro.Backend.Tests/BackendFixture.cs @@ -5,10 +5,10 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.DependencyInjection; -using Monitor.Backend.Application; -using Monitor.TestSupport; +using MoShiro.Backend.Application; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// /// 在真实的 Kestrel 端口上启动整个后端(真实的 SQLite 与段文件,只是用临时目录和较快的轮询), @@ -58,7 +58,7 @@ public class BackendFixture : IAsyncLifetime public async ValueTask InitializeAsync() { - DataDirectory = Path.Combine(Path.GetTempPath(), "monitor-tests-" + Guid.NewGuid().ToString("N")); + DataDirectory = Path.Combine(Path.GetTempPath(), "moshiro-tests-" + Guid.NewGuid().ToString("N")); await StartAsync(); _adminToken = await CreateAdminTokenAsync(); CreateClients(); @@ -100,7 +100,7 @@ public class BackendFixture : IAsyncLifetime public async Task ReconnectAsync(AgentSimulator previous) { AgentSimulator agent = AgentSimulator.Restore(previous.ExportedKey, previous.NodeId); - agent.ProbeForChunks = new Monitor.Protocol.ProbeId(DefaultProbeId); + agent.ProbeForChunks = new MoShiro.Protocol.ProbeId(DefaultProbeId); _agents.Add(agent); await agent.ConnectAsync(BaseAddress); return agent; @@ -129,7 +129,7 @@ public class BackendFixture : IAsyncLifetime { string token = await CreateJoinTokenAsync(autoApprove); AgentSimulator agent = await AgentSimulator.EnrollAsync(Anonymous, token, name ?? UniqueName("agent"), kinds); - agent.ProbeForChunks = new Monitor.Protocol.ProbeId(DefaultProbeId); + agent.ProbeForChunks = new MoShiro.Protocol.ProbeId(DefaultProbeId); _agents.Add(agent); await agent.ConnectAsync(BaseAddress, hello, kinds); return agent; diff --git a/backend/tests/Monitor.Backend.Tests/EnrollmentAndTaskSetTests.cs b/backend/tests/MoShiro.Backend.Tests/EnrollmentAndTaskSetTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/EnrollmentAndTaskSetTests.cs rename to backend/tests/MoShiro.Backend.Tests/EnrollmentAndTaskSetTests.cs index 6bb7d7e..b97414e 100644 --- a/backend/tests/Monitor.Backend.Tests/EnrollmentAndTaskSetTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/EnrollmentAndTaskSetTests.cs @@ -1,9 +1,9 @@ using System.Net; using System.Text.Json.Nodes; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("backend")] public sealed class EnrollmentAndTaskSetTests @@ -150,7 +150,7 @@ public sealed class EnrollmentAndTaskSetTests RawChunk chunk = ChunkFactory.UdpEchoClean(agent.NodeId, TimeProvider.System.GetUtcNow().ToUnixTimeMilliseconds() - 30_000); chunk.Header.AgentId = agent.NodeId; - await agent.SendBinaryAsync(Fleet.Rpc.OutboxFrame.Build(MonitorChannels.RawChunk, chunk.Header.ChunkId.Value, RawChunkCodec.Encode(chunk))); + await agent.SendBinaryAsync(Fleet.Rpc.OutboxFrame.Build(MoShiroChannels.RawChunk, chunk.Header.ChunkId.Value, RawChunkCodec.Encode(chunk))); await Task.Delay(300); JsonNode chunks = await _backend.GetJsonAsync(_backend.Admin, "/api/admin/raw/chunks?agent_id=" + agent.NodeId.Value); Assert.Empty(chunks["items"]!.AsArray()); diff --git a/backend/tests/Monitor.Backend.Tests/HostTests.cs b/backend/tests/MoShiro.Backend.Tests/HostTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Tests/HostTests.cs rename to backend/tests/MoShiro.Backend.Tests/HostTests.cs index d6ba7ce..ca4ea9b 100644 --- a/backend/tests/Monitor.Backend.Tests/HostTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/HostTests.cs @@ -1,9 +1,9 @@ using System.Text.Json.Nodes; -using Monitor.Agent; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Agent; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; public sealed class HostSamplerTests { diff --git a/backend/tests/Monitor.Backend.Tests/IngestAndQueryTests.cs b/backend/tests/MoShiro.Backend.Tests/IngestAndQueryTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/IngestAndQueryTests.cs rename to backend/tests/MoShiro.Backend.Tests/IngestAndQueryTests.cs index 6b6292b..3a4b065 100644 --- a/backend/tests/Monitor.Backend.Tests/IngestAndQueryTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/IngestAndQueryTests.cs @@ -2,10 +2,10 @@ using System.Net; using System.Net.Http.Headers; using System.Text.Json.Nodes; using Fleet.Core; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("backend")] public sealed class IngestAndQueryTests @@ -120,11 +120,11 @@ public sealed class IngestAndQueryTests var ack = new TaskCompletionSource(); byte[] corrupt = RawChunkCodec.Encode(ChunkFactory.UdpEchoClean(agent.NodeId, start)); corrupt[^1] ^= 0xFF; - await agent.SendBinaryAsync(Fleet.Rpc.OutboxFrame.Build(MonitorChannels.RawChunk, garbage.Value, corrupt)); + await agent.SendBinaryAsync(Fleet.Rpc.OutboxFrame.Build(MoShiroChannels.RawChunk, garbage.Value, corrupt)); // 冒名的块:agent_id 与认证的节点不一致 RawChunk spoofed = ChunkFactory.UdpEchoClean(new NodeId("agt_someone_else"), start); - await agent.SendBinaryAsync(Fleet.Rpc.OutboxFrame.Build(MonitorChannels.RawChunk, spoofed.Header.ChunkId.Value, RawChunkCodec.Encode(spoofed))); + await agent.SendBinaryAsync(Fleet.Rpc.OutboxFrame.Build(MoShiroChannels.RawChunk, spoofed.Header.ChunkId.Value, RawChunkCodec.Encode(spoofed))); await BackendFixture.EventuallyAsync(async () => { diff --git a/backend/tests/Monitor.Backend.Tests/LinkTests.cs b/backend/tests/MoShiro.Backend.Tests/LinkTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Tests/LinkTests.cs rename to backend/tests/MoShiro.Backend.Tests/LinkTests.cs index 5877c34..15b2020 100644 --- a/backend/tests/Monitor.Backend.Tests/LinkTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/LinkTests.cs @@ -1,8 +1,8 @@ using System.Net; using System.Text.Json.Nodes; -using Monitor.TestSupport; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("probe-e2e")] public sealed class LinkTests diff --git a/backend/tests/Monitor.Backend.Tests/LiveTests.cs b/backend/tests/MoShiro.Backend.Tests/LiveTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Tests/LiveTests.cs rename to backend/tests/MoShiro.Backend.Tests/LiveTests.cs index f29d959..c875df6 100644 --- a/backend/tests/Monitor.Backend.Tests/LiveTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/LiveTests.cs @@ -1,10 +1,10 @@ using System.Net; using System.Text.Json.Nodes; using Fleet.Core; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("backend")] public sealed class LiveTests diff --git a/backend/tests/Monitor.Backend.Tests/LoginRateLimitTests.cs b/backend/tests/MoShiro.Backend.Tests/LoginRateLimitTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Tests/LoginRateLimitTests.cs rename to backend/tests/MoShiro.Backend.Tests/LoginRateLimitTests.cs index ef1dee7..ddd5a91 100644 --- a/backend/tests/Monitor.Backend.Tests/LoginRateLimitTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/LoginRateLimitTests.cs @@ -1,7 +1,7 @@ using System.Net; using System.Text.Json.Nodes; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 登录限流按来源地址计,会影响同一地址上的其他登录,所以用独立的后端实例。 public sealed class LoginRateLimitTests : IClassFixture diff --git a/backend/tests/Monitor.Backend.Tests/Monitor.Backend.Tests.csproj b/backend/tests/MoShiro.Backend.Tests/MoShiro.Backend.Tests.csproj similarity index 67% rename from backend/tests/Monitor.Backend.Tests/Monitor.Backend.Tests.csproj rename to backend/tests/MoShiro.Backend.Tests/MoShiro.Backend.Tests.csproj index d36d4b8..8409a4f 100644 --- a/backend/tests/Monitor.Backend.Tests/Monitor.Backend.Tests.csproj +++ b/backend/tests/MoShiro.Backend.Tests/MoShiro.Backend.Tests.csproj @@ -8,11 +8,11 @@ - - - + + + - + diff --git a/backend/tests/Monitor.Backend.Tests/NodeInfoTests.cs b/backend/tests/MoShiro.Backend.Tests/NodeInfoTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/NodeInfoTests.cs rename to backend/tests/MoShiro.Backend.Tests/NodeInfoTests.cs index b9ecaf2..0bb40dc 100644 --- a/backend/tests/Monitor.Backend.Tests/NodeInfoTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/NodeInfoTests.cs @@ -2,10 +2,10 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using Microsoft.Extensions.Time.Testing; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; public sealed partial class NodeInfoTests { diff --git a/backend/tests/Monitor.Backend.Tests/OnDemandTestsTests.cs b/backend/tests/MoShiro.Backend.Tests/OnDemandTestsTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/OnDemandTestsTests.cs rename to backend/tests/MoShiro.Backend.Tests/OnDemandTestsTests.cs index f344ee8..66f200a 100644 --- a/backend/tests/Monitor.Backend.Tests/OnDemandTestsTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/OnDemandTestsTests.cs @@ -1,10 +1,10 @@ using System.Net; using System.Text.Json.Nodes; using Fleet.Core; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("backend")] public sealed class OnDemandTestsTests @@ -233,7 +233,7 @@ public sealed class OnDemandTestsTests Assert.Equal("line:" + _backend.DefaultProbeId, item["series"]!.GetValue()); using HttpResponseMessage download = await _backend.Admin.GetAsync("/api/admin/raw/chunks/" + chunk.Header.ChunkId); - Assert.Equal("application/vnd.monitor.rawchunk", download.Content.Headers.ContentType!.MediaType); + Assert.Equal("application/vnd.moshiro.rawchunk", download.Content.Headers.ContentType!.MediaType); RawChunk decoded = RawChunkCodec.Decode(await download.Content.ReadAsByteArrayAsync()).Value; Assert.Equal(chunk.Header.ChunkId, decoded.Header.ChunkId); Assert.Equal(100, decoded.Find("msg")!.Count); diff --git a/backend/tests/Monitor.Backend.Tests/OpenApiConformanceTests.cs b/backend/tests/MoShiro.Backend.Tests/OpenApiConformanceTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/OpenApiConformanceTests.cs rename to backend/tests/MoShiro.Backend.Tests/OpenApiConformanceTests.cs index 955c80d..7d9fdc0 100644 --- a/backend/tests/Monitor.Backend.Tests/OpenApiConformanceTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/OpenApiConformanceTests.cs @@ -1,7 +1,7 @@ using System.Text.Json.Nodes; using System.Text.RegularExpressions; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// /// 对照 docs/api-guidelines.md 检查运行时生成的 OpenAPI 文档:operationId、路径风格、错误响应格式、字段命名。 diff --git a/backend/tests/Monitor.Backend.Tests/OperationsAndSecurityTests.cs b/backend/tests/MoShiro.Backend.Tests/OperationsAndSecurityTests.cs similarity index 98% rename from backend/tests/Monitor.Backend.Tests/OperationsAndSecurityTests.cs rename to backend/tests/MoShiro.Backend.Tests/OperationsAndSecurityTests.cs index 4485688..c7b6215 100644 --- a/backend/tests/Monitor.Backend.Tests/OperationsAndSecurityTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/OperationsAndSecurityTests.cs @@ -5,12 +5,12 @@ using Fleet.Core; using Fleet.Rpc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Monitor.Backend.Analysis; -using Monitor.Backend.Application; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Backend.Analysis; +using MoShiro.Backend.Application; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("backend")] public sealed class OperationsAndSecurityTests @@ -169,7 +169,7 @@ public sealed class OperationsAndSecurityTests await agent.SendBinaryAsync(OutboxFrame.Build(99, Guid.NewGuid(), [1])); // 没有注册的通道 // 节点不能调用任何“执行”类的方法:Fleet 根本没有这样的路由 - foreach (string method in new[] { "fleet.exec", "fleet.shell", "monitor.exec", "monitor.write_file", "fleet.update" }) + foreach (string method in new[] { "fleet.exec", "fleet.shell", "moshiro.exec", "moshiro.write_file", "fleet.update" }) { RpcMessage reply = await agent.RequestRawAsync(method); Assert.Equal("fleet_unsupported", reply.Error!.Data!.Code); diff --git a/backend/tests/Monitor.Backend.Tests/PersistenceTests.cs b/backend/tests/MoShiro.Backend.Tests/PersistenceTests.cs similarity index 97% rename from backend/tests/Monitor.Backend.Tests/PersistenceTests.cs rename to backend/tests/MoShiro.Backend.Tests/PersistenceTests.cs index 6a8c000..9c60385 100644 --- a/backend/tests/Monitor.Backend.Tests/PersistenceTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/PersistenceTests.cs @@ -1,8 +1,8 @@ using System.Text.Json.Nodes; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 重启后端:数据、身份和令牌都必须还在;热层从原始块回填;段文件尾部没有索引的数据被截断。 public sealed class PersistenceTests : IAsyncLifetime diff --git a/backend/tests/Monitor.Backend.Tests/PlanTests.cs b/backend/tests/MoShiro.Backend.Tests/PlanTests.cs similarity index 86% rename from backend/tests/Monitor.Backend.Tests/PlanTests.cs rename to backend/tests/MoShiro.Backend.Tests/PlanTests.cs index 352345a..4aeb1f9 100644 --- a/backend/tests/Monitor.Backend.Tests/PlanTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/PlanTests.cs @@ -1,8 +1,8 @@ using System.Text.Json; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; public sealed class PlanTests { diff --git a/backend/tests/Monitor.Backend.Tests/ProbeEndToEndTests.cs b/backend/tests/MoShiro.Backend.Tests/ProbeEndToEndTests.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/ProbeEndToEndTests.cs rename to backend/tests/MoShiro.Backend.Tests/ProbeEndToEndTests.cs index 21562d1..2d34f5a 100644 --- a/backend/tests/Monitor.Backend.Tests/ProbeEndToEndTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/ProbeEndToEndTests.cs @@ -1,6 +1,6 @@ using System.Text.Json.Nodes; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 真实的后端 + 真实的 Agent + 真实的 Probe(都在进程内,回环网络)。 [Collection("probe-e2e")] diff --git a/backend/tests/Monitor.Backend.Tests/ProbeHarness.cs b/backend/tests/MoShiro.Backend.Tests/ProbeHarness.cs similarity index 93% rename from backend/tests/Monitor.Backend.Tests/ProbeHarness.cs rename to backend/tests/MoShiro.Backend.Tests/ProbeHarness.cs index 95b354e..e10cbcb 100644 --- a/backend/tests/Monitor.Backend.Tests/ProbeHarness.cs +++ b/backend/tests/MoShiro.Backend.Tests/ProbeHarness.cs @@ -1,9 +1,9 @@ using System.Text; using System.Text.Json.Nodes; using Fleet.Core; -using Monitor.Probe; +using MoShiro.Probe; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 进程内的真实 Probe:用后端生成的身份文件(和面板下载的 probe.json 同一份)启动 。 public sealed class ProbeHarness : IAsyncDisposable @@ -31,7 +31,7 @@ public sealed class ProbeHarness : IAsyncDisposable JsonNode created = await backend.CreateProbeAsync(); string json = created.ToJsonString(); - string directory = Path.Combine(Path.GetTempPath(), "monitor-probe-" + Guid.NewGuid().ToString("N")); + string directory = Path.Combine(Path.GetTempPath(), "moshiro-probe-" + Guid.NewGuid().ToString("N")); System.IO.Directory.CreateDirectory(directory); string path = Path.Combine(directory, "probe.json"); await File.WriteAllTextAsync(path, json, Encoding.UTF8); diff --git a/backend/tests/Monitor.Backend.Tests/QueryPlanningTests.cs b/backend/tests/MoShiro.Backend.Tests/QueryPlanningTests.cs similarity index 92% rename from backend/tests/Monitor.Backend.Tests/QueryPlanningTests.cs rename to backend/tests/MoShiro.Backend.Tests/QueryPlanningTests.cs index 8c9d41e..259aa3d 100644 --- a/backend/tests/Monitor.Backend.Tests/QueryPlanningTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/QueryPlanningTests.cs @@ -1,10 +1,10 @@ using Fleet.Core; using Microsoft.Extensions.Time.Testing; -using Monitor.Backend.Analysis; -using Monitor.Backend.Application; -using Monitor.Protocol; +using MoShiro.Backend.Analysis; +using MoShiro.Backend.Application; +using MoShiro.Protocol; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 时间段、步长与数据源的规划:与 docs/query.md 里的表格逐行对应。 public sealed class QueryPlanningTests @@ -127,18 +127,18 @@ public sealed class QueryPlanningTests { Result result = Resolver.Resolve(new RangeRequest(range, null, null, null, null), s_now); Assert.False(result.IsSuccess); - Assert.Contains(result.Error.Code, new[] { MonitorErrors.InvalidRange, MonitorErrors.RangeTooLarge }); + Assert.Contains(result.Error.Code, new[] { MoShiroErrors.InvalidRange, MoShiroErrors.RangeTooLarge }); } [Fact] public void RangeResolver_EnforcesThePointLimit_AndSuggestsAStep() { Result result = Resolver.Resolve(new RangeRequest("30d", null, null, "1m", null), s_now); - Assert.Equal(MonitorErrors.RangeTooLarge, result.Error.Code); + Assert.Equal(MoShiroErrors.RangeTooLarge, result.Error.Code); Assert.Contains("step=", result.Error.Message); - Assert.Equal(MonitorErrors.InvalidStep, Resolver.Resolve(new RangeRequest("1h", null, null, "7s", null), s_now).Error.Code); - Assert.Equal(MonitorErrors.InvalidRange, Resolver.Resolve(new RangeRequest(null, DateTimeOffset.FromUnixTimeMilliseconds(s_now), DateTimeOffset.FromUnixTimeMilliseconds(s_now), null, null), s_now).Error.Code); - Assert.Equal(MonitorErrors.ValidationFailed, Resolver.Resolve(new RangeRequest("1h", DateTimeOffset.FromUnixTimeMilliseconds(s_now - Hour), null, null, null), s_now).Error.Code); + Assert.Equal(MoShiroErrors.InvalidStep, Resolver.Resolve(new RangeRequest("1h", null, null, "7s", null), s_now).Error.Code); + Assert.Equal(MoShiroErrors.InvalidRange, Resolver.Resolve(new RangeRequest(null, DateTimeOffset.FromUnixTimeMilliseconds(s_now), DateTimeOffset.FromUnixTimeMilliseconds(s_now), null, null), s_now).Error.Code); + Assert.Equal(MoShiroErrors.ValidationFailed, Resolver.Resolve(new RangeRequest("1h", DateTimeOffset.FromUnixTimeMilliseconds(s_now - Hour), null, null, null), s_now).Error.Code); } [Fact] @@ -147,7 +147,7 @@ public sealed class QueryPlanningTests Assert.True(Resolver.Resolve(new RangeRequest("1h", null, null, null, "UTC"), s_now).IsSuccess); Assert.True(Resolver.Resolve(new RangeRequest("1h", null, null, null, null), s_now).IsSuccess); Result shanghai = Resolver.Resolve(new RangeRequest("7d", null, null, "1d", "Asia/Shanghai"), s_now); - Assert.Equal(MonitorErrors.ValidationFailed, shanghai.Error.Code); + Assert.Equal(MoShiroErrors.ValidationFailed, shanghai.Error.Code); Assert.IsType(shanghai.Error); } @@ -209,10 +209,10 @@ public sealed class QueryPlanningTests Assert.Equal(selection.ExportIds.Count, selection.ExportIds.Distinct().Count()); Assert.True(selection.WantsScores); - Assert.Equal(MonitorErrors.UnknownMetric, MetricSelection.Resolve("nope", null, 100).Error.Code); - Assert.Equal(MonitorErrors.UnknownMetric, MetricSelection.Resolve("score.nope", null, 100).Error.Code); - Assert.Equal(MonitorErrors.UnknownPreset, MetricSelection.Resolve(null, "nope", 100).Error.Code); - Assert.Equal(MonitorErrors.TooManyMetrics, MetricSelection.Resolve(null, "udp", 3).Error.Code); + Assert.Equal(MoShiroErrors.UnknownMetric, MetricSelection.Resolve("nope", null, 100).Error.Code); + Assert.Equal(MoShiroErrors.UnknownMetric, MetricSelection.Resolve("score.nope", null, 100).Error.Code); + Assert.Equal(MoShiroErrors.UnknownPreset, MetricSelection.Resolve(null, "nope", 100).Error.Code); + Assert.Equal(MoShiroErrors.TooManyMetrics, MetricSelection.Resolve(null, "udp", 3).Error.Code); } [Fact] diff --git a/backend/tests/Monitor.Backend.Tests/SseClient.cs b/backend/tests/MoShiro.Backend.Tests/SseClient.cs similarity index 99% rename from backend/tests/Monitor.Backend.Tests/SseClient.cs rename to backend/tests/MoShiro.Backend.Tests/SseClient.cs index f20f160..6d6b70f 100644 --- a/backend/tests/Monitor.Backend.Tests/SseClient.cs +++ b/backend/tests/MoShiro.Backend.Tests/SseClient.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Text.Json.Nodes; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; public sealed record SseEvent(string Name, JsonNode? Data); diff --git a/backend/tests/Monitor.Backend.Tests/StorageTests.cs b/backend/tests/MoShiro.Backend.Tests/StorageTests.cs similarity index 97% rename from backend/tests/Monitor.Backend.Tests/StorageTests.cs rename to backend/tests/MoShiro.Backend.Tests/StorageTests.cs index e0dff17..ec98bcf 100644 --- a/backend/tests/Monitor.Backend.Tests/StorageTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/StorageTests.cs @@ -2,20 +2,20 @@ using System.Text; using Fleet.Core; using Fleet.Server; using Microsoft.Data.Sqlite; -using Monitor.Backend.Analysis; -using Monitor.Backend.Application; -using Monitor.Backend.Storage.Sqlite; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Backend.Analysis; +using MoShiro.Backend.Application; +using MoShiro.Backend.Storage.Sqlite; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; /// 存储层:段文件与索引、指标窗口、队列、节点与令牌。直接使用真实的 SQLite 和临时目录。 public sealed class StorageTests : IDisposable { private static readonly NodeId s_agent = new("agt_storage"); - private readonly string _dir = Path.Combine(Path.GetTempPath(), "monitor-storage-" + Guid.NewGuid().ToString("N")); + private readonly string _dir = Path.Combine(Path.GetTempPath(), "moshiro-storage-" + Guid.NewGuid().ToString("N")); private readonly SqliteDatabase _db; public StorageTests() @@ -269,7 +269,7 @@ public sealed class StorageTests : IDisposable Assert.True((await ((IFleetStore)store).CreateNodeAsync(Node("agt_4", "hk", NodeStatus.Pending), default)).IsSuccess); Result approve = await store.SetStatusAsync(new NodeId("agt_3"), NodeStatus.Active, default); - Assert.Equal(MonitorErrors.NameConflict, approve.Error.Code); + Assert.Equal(MoShiroErrors.NameConflict, approve.Error.Code); // 接管:保留旧 ID 与历史,换成新公钥,删除待审批记录 Result takeover = await store.TakeoverAsync(new NodeId("agt_3"), default); @@ -277,7 +277,7 @@ public sealed class StorageTests : IDisposable Assert.Equal(new byte[] { 1, 2, 3 }, takeover.Value.PublicKey); Assert.Null(await store.GetAsync(new NodeId("agt_3"), default)); Assert.Equal(NodeStatus.Pending, (await store.GetAsync(new NodeId("agt_4"), default))!.Status); - Assert.Equal(MonitorErrors.AgentNotFound, (await store.TakeoverAsync(new NodeId("agt_1"), default)).Error.Code); // 只有 pending 能接管别人 + Assert.Equal(MoShiroErrors.AgentNotFound, (await store.TakeoverAsync(new NodeId("agt_1"), default)).Error.Code); // 只有 pending 能接管别人 } [Fact] @@ -288,12 +288,12 @@ public sealed class StorageTests : IDisposable AgentRecord current = (await store.GetAsync(new NodeId("agt_1"), default))!; Result stale = await store.PatchAsync(current.Id, new AgentPatch { Labels = ["x"] }, current.RowVersion + 5, default); - Assert.Equal(MonitorErrors.PreconditionFailed, stale.Error.Code); + Assert.Equal(MoShiroErrors.PreconditionFailed, stale.Error.Code); Result ok = await store.PatchAsync(current.Id, new AgentPatch { Labels = ["x"] }, current.RowVersion, default); Assert.Equal(current.RowVersion + 1, ok.Value.RowVersion); Assert.Equal(["x"], ok.Value.Labels); Assert.Equal("hk", ok.Value.Name); // 没写的字段不动 - Assert.Equal(MonitorErrors.AgentNotFound, (await store.PatchAsync(new NodeId("agt_x"), new AgentPatch(), null, default)).Error.Code); + Assert.Equal(MoShiroErrors.AgentNotFound, (await store.PatchAsync(new NodeId("agt_x"), new AgentPatch(), null, default)).Error.Code); } [Fact] @@ -368,11 +368,11 @@ public sealed class StorageTests : IDisposable [Fact] public void Schema_RefusesADatabaseFromANewerBuild() { - string dir = Path.Combine(Path.GetTempPath(), "monitor-schema-" + Guid.NewGuid().ToString("N")); + string dir = Path.Combine(Path.GetTempPath(), "moshiro-schema-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(dir); try { - using (var connection = new SqliteConnection($"Data Source={Path.Combine(dir, "monitor.db")}")) + using (var connection = new SqliteConnection($"Data Source={Path.Combine(dir, "moshiro.db")}")) { connection.Open(); connection.Exec("PRAGMA user_version = 99"); diff --git a/backend/tests/Monitor.Backend.Tests/UpdateTests.cs b/backend/tests/MoShiro.Backend.Tests/UpdateTests.cs similarity index 93% rename from backend/tests/Monitor.Backend.Tests/UpdateTests.cs rename to backend/tests/MoShiro.Backend.Tests/UpdateTests.cs index caf8443..75c438b 100644 --- a/backend/tests/Monitor.Backend.Tests/UpdateTests.cs +++ b/backend/tests/MoShiro.Backend.Tests/UpdateTests.cs @@ -1,15 +1,20 @@ using System.Net; using System.Text.Json.Nodes; -using Monitor.Protocol; -using Monitor.TestSupport; +using MoShiro.Protocol; +using MoShiro.TestSupport; -namespace Monitor.Backend.Tests; +namespace MoShiro.Backend.Tests; [Collection("probe-e2e")] public sealed class UpdateTests { private const string Digest = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + // 每个用例取自己的 seq(单调递增):共用的后端只接受比已登记的更大的 seq,用例的先后顺序不能影响结果 + private static long s_seq = TimeProvider.System.GetUtcNow().ToUnixTimeSeconds() * 1000; + + private static long NextSeq() => Interlocked.Add(ref s_seq, 100); + private readonly BackendFixture _backend; public UpdateTests(BackendFixture backend) @@ -19,7 +24,7 @@ public sealed class UpdateTests // 面板不验签(节点宿主机上的更新器验),所以测试里签名随便写一个格式对的 private static string Manifest(string component, long seq, string digest = Digest) => - $$"""{"schema":1,"component":"{{component}}","channel":"dev","image":"registry.example/monitor-dev/monitor-{{component}}","digest":"{{digest}}","tag":"dev-{{seq}}","version":"0.1.1","commit":"abc","built_at":"2026-10-06T00:00:00Z","seq":{{seq}}}"""; + $$"""{"schema":1,"component":"{{component}}","channel":"dev","image":"registry.example/moshiro-dev/moshiro-{{component}}","digest":"{{digest}}","tag":"dev-{{seq}}","version":"0.1.1","commit":"abc","built_at":"2026-10-06T00:00:00Z","seq":{{seq}}}"""; private async Task PublishAsync(string manifest, string signature = "MEUCIQDc") => await _backend.Admin.PostAsync("/api/admin/releases", BackendFixture.Json(new JsonObject { ["manifest"] = manifest, ["signature"] = signature })); @@ -27,7 +32,7 @@ public sealed class UpdateTests [Fact] public async Task Release_IsForwardedByteForByte_ToOnlineNodesOfThatComponent_AndToNodesThatConnectLater() { - long seq = TimeProvider.System.GetUtcNow().ToUnixTimeSeconds() * 1000; + long seq = NextSeq(); await using AgentSimulator online = await _backend.ConnectAgentAsync(); string manifest = Manifest("agent", seq); @@ -53,7 +58,7 @@ public sealed class UpdateTests [Fact] public async Task Release_Registration_RefusesBadManifests_StaleSeq_AndNeedsAnAdmin() { - long seq = TimeProvider.System.GetUtcNow().ToUnixTimeSeconds() * 1000 + 5; + long seq = NextSeq(); Assert.Equal(HttpStatusCode.BadRequest, (await PublishAsync("{not json")).StatusCode); Assert.Equal(HttpStatusCode.BadRequest, (await PublishAsync(Manifest("agent", seq, digest: "sha256:short"))).StatusCode); Assert.Equal(HttpStatusCode.BadRequest, (await PublishAsync(Manifest("backend", seq))).StatusCode); @@ -84,7 +89,7 @@ public sealed class UpdateTests return client; } - long seq = TimeProvider.System.GetUtcNow().ToUnixTimeSeconds() * 1000 + 50; + long seq = NextSeq(); using HttpClient release = Client(await NewTokenAsync("release")); using HttpClient read = Client(await NewTokenAsync("read")); var body = BackendFixture.Json(new JsonObject { ["manifest"] = Manifest("probe", seq), ["signature"] = "MEUC" }); diff --git a/backend/tests/Monitor.TestSupport/AgentSimulator.cs b/backend/tests/MoShiro.TestSupport/AgentSimulator.cs similarity index 92% rename from backend/tests/Monitor.TestSupport/AgentSimulator.cs rename to backend/tests/MoShiro.TestSupport/AgentSimulator.cs index 4e7ca32..15b0d30 100644 --- a/backend/tests/Monitor.TestSupport/AgentSimulator.cs +++ b/backend/tests/MoShiro.TestSupport/AgentSimulator.cs @@ -7,9 +7,9 @@ using System.Text.Json.Nodes; using Fleet.Core; using Fleet.Identity; using Fleet.Rpc; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.TestSupport; +namespace MoShiro.TestSupport; /// /// 一个会说 Fleet 协议的假 Agent:用加入令牌注册 → 连接 → 签名认证 → hello → 接收期望状态 → 上传原始块并等待 ACK。 @@ -48,7 +48,7 @@ public sealed class AgentSimulator : IAsyncDisposable public ConcurrentQueue TestRuns { get; } = new(); - /// 面板发来的发布清单(monitor.update.offer)和更新请求(monitor.update.apply)。 + /// 面板发来的发布清单(moshiro.update.offer)和更新请求(moshiro.update.apply)。 public ConcurrentQueue UpdateOffers { get; } = new(); private int _updateApplies; @@ -63,7 +63,7 @@ public sealed class AgentSimulator : IAsyncDisposable public string? CloseReason { get; private set; } - /// 收到 monitor.test.run 时的应答:默认接受。 + /// 收到 moshiro.test.run 时的应答:默认接受。 public Func TestRunHandler { get; set; } = _ => (true, null); public byte[] PublicKey => _identity.PublicKeySpki; @@ -144,11 +144,11 @@ public sealed class AgentSimulator : IAsyncDisposable ProbeUdpPorts = [7443], LocalLimits = new LocalLimits { MaxTotalMbps = 200, MaxTotalPps = 20_000, MaxConcurrentTests = 1 }, }, - MonitorJsonContext.Default.AgentHelloMeta); + MoShiroJsonContext.Default.AgentHelloMeta); var hello = new HelloParams { NodeVersion = "0.0.1-sim", - Capabilities = [MonitorCapabilities.Base, MonitorCapabilities.KindsPrefix + string.Join(',', kinds ?? ["udp_echo", "tcp_echo", "udp_train", "connect", "hold", "tcp_bulk", "icmp_echo"])], + Capabilities = [MoShiroCapabilities.Base, MoShiroCapabilities.KindsPrefix + string.Join(',', kinds ?? ["udp_echo", "tcp_echo", "udp_train", "connect", "hold", "tcp_bulk", "icmp_echo"])], NowUtcMs = TimeProvider.System.GetUtcNow().ToUnixTimeMilliseconds(), Meta = meta, }; @@ -170,7 +170,7 @@ public sealed class AgentSimulator : IAsyncDisposable Guid id = chunk.Header.ChunkId.Value; var ack = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _acks[id] = ack; - await SendBinaryAsync(OutboxFrame.Build(MonitorChannels.RawChunk, id, bytes)); + await SendBinaryAsync(OutboxFrame.Build(MoShiroChannels.RawChunk, id, bytes)); return await ack.Task.WaitAsync(timeout ?? TimeSpan.FromSeconds(10)); } @@ -182,24 +182,24 @@ public sealed class AgentSimulator : IAsyncDisposable public Task SendEventAsync(string kind, string? detail = null) => SendAsync(RpcMessage.Notification( - MonitorMethods.Event, + MoShiroMethods.Event, new AgentEventParams { Kind = kind, AtUtcMs = TimeProvider.System.GetUtcNow().ToUnixTimeMilliseconds(), Detail = detail }, - MonitorJsonContext.Default.AgentEventParams)); + MoShiroJsonContext.Default.AgentEventParams)); public Task SendHostAsync(HostSnapshot snapshot) => - SendAsync(RpcMessage.Notification(MonitorMethods.Host, snapshot, MonitorJsonContext.Default.HostSnapshot)); + SendAsync(RpcMessage.Notification(MoShiroMethods.Host, snapshot, MoShiroJsonContext.Default.HostSnapshot)); public Task SendTestStatusAsync(TestId test, string status, int? phase = null, string? errorCode = null) => SendAsync(RpcMessage.Notification( - MonitorMethods.TestStatus, + MoShiroMethods.TestStatus, new TestStatusParams { TestId = test, Status = status, Phase = phase, ErrorCode = errorCode }, - MonitorJsonContext.Default.TestStatusParams)); + MoShiroJsonContext.Default.TestStatusParams)); /// 像节点的更新器报告的那样,随心跳上报更新状态。 public Task SendUpdateStatusAsync(UpdateStatus status) => SendAsync(RpcMessage.Notification( FleetMethods.Heartbeat, - new HeartbeatParams { Extra = JsonSerializer.SerializeToElement(new AgentHeartbeatExtra { Update = status }, MonitorJsonContext.Default.AgentHeartbeatExtra) }, + new HeartbeatParams { Extra = JsonSerializer.SerializeToElement(new AgentHeartbeatExtra { Update = status }, MoShiroJsonContext.Default.AgentHeartbeatExtra) }, FleetRpcContext.Default.HeartbeatParams)); public async Task RequestRawAsync(string method, JsonElement? parameters = null) => await RequestAsync(method, parameters); @@ -383,7 +383,7 @@ public sealed class AgentSimulator : IAsyncDisposable StateSyncParams sync = message.Params!.Value.Deserialize(FleetRpcContext.Default.StateSyncParams)!; if (sync.Type == TaskSet.StateType) { - TaskSets.Enqueue(sync.Doc.Deserialize(MonitorJsonContext.Default.TaskSet)!); + TaskSets.Enqueue(sync.Doc.Deserialize(MoShiroJsonContext.Default.TaskSet)!); } var result = new StateSyncResult { AppliedVersion = sync.Version, Rejected = [.. RejectNextState] }; @@ -403,19 +403,19 @@ public sealed class AgentSimulator : IAsyncDisposable case FleetMethods.Revoked: Revoked = true; break; - case MonitorMethods.UpdateOffer: - UpdateOffers.Enqueue(message.Params!.Value.Deserialize(MonitorJsonContext.Default.UpdateOfferParams)!); + case MoShiroMethods.UpdateOffer: + UpdateOffers.Enqueue(message.Params!.Value.Deserialize(MoShiroJsonContext.Default.UpdateOfferParams)!); break; - case MonitorMethods.UpdateApply: + case MoShiroMethods.UpdateApply: Interlocked.Increment(ref _updateApplies); break; - case MonitorMethods.TestRun: - TestRunParams run = message.Params!.Value.Deserialize(MonitorJsonContext.Default.TestRunParams)!; + case MoShiroMethods.TestRun: + TestRunParams run = message.Params!.Value.Deserialize(MoShiroJsonContext.Default.TestRunParams)!; TestRuns.Enqueue(run); (bool accepted, string? errorCode) = TestRunHandler(run); if (accepted) { - await SendAsync(RpcMessage.Success(message.Id!.Value, new TestRunResult { Accepted = true }, MonitorJsonContext.Default.TestRunResult)); + await SendAsync(RpcMessage.Success(message.Id!.Value, new TestRunResult { Accepted = true }, MoShiroJsonContext.Default.TestRunResult)); } else { @@ -423,10 +423,10 @@ public sealed class AgentSimulator : IAsyncDisposable } break; - case MonitorMethods.TestCancel: - TestCancelParams cancel = message.Params!.Value.Deserialize(MonitorJsonContext.Default.TestCancelParams)!; + case MoShiroMethods.TestCancel: + TestCancelParams cancel = message.Params!.Value.Deserialize(MoShiroJsonContext.Default.TestCancelParams)!; TestCancels.Enqueue(cancel.TestId); - await SendAsync(RpcMessage.Success(message.Id!.Value, new TestRunResult { Accepted = true }, MonitorJsonContext.Default.TestRunResult)); + await SendAsync(RpcMessage.Success(message.Id!.Value, new TestRunResult { Accepted = true }, MoShiroJsonContext.Default.TestRunResult)); break; } } diff --git a/backend/tests/Monitor.TestSupport/AgentWire.cs b/backend/tests/MoShiro.TestSupport/AgentWire.cs similarity index 98% rename from backend/tests/Monitor.TestSupport/AgentWire.cs rename to backend/tests/MoShiro.TestSupport/AgentWire.cs index 685f307..a3fbf6e 100644 --- a/backend/tests/Monitor.TestSupport/AgentWire.cs +++ b/backend/tests/MoShiro.TestSupport/AgentWire.cs @@ -3,7 +3,7 @@ using System.Text.Json; using Fleet.Core; using Fleet.Rpc; -namespace Monitor.TestSupport; +namespace MoShiro.TestSupport; public sealed record AuthAttempt(bool Ok, string? Status, string? ErrorCode, string? CloseReason); diff --git a/backend/tests/Monitor.TestSupport/ChunkFactory.cs b/backend/tests/MoShiro.TestSupport/ChunkFactory.cs similarity index 99% rename from backend/tests/Monitor.TestSupport/ChunkFactory.cs rename to backend/tests/MoShiro.TestSupport/ChunkFactory.cs index 2a01dab..3f51c50 100644 --- a/backend/tests/Monitor.TestSupport/ChunkFactory.cs +++ b/backend/tests/MoShiro.TestSupport/ChunkFactory.cs @@ -1,8 +1,8 @@ using System.Text.Json; using Fleet.Core; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.TestSupport; +namespace MoShiro.TestSupport; /// 每一个回显包的设定:上行或下行丢失,以及两个方向的单向时延(微秒)。 public readonly record struct EchoPacket(bool LostUp, bool LostDown, long OwdUpUs, long OwdDownUs, long ProcessUs = 50); diff --git a/backend/tests/Monitor.TestSupport/Monitor.TestSupport.csproj b/backend/tests/MoShiro.TestSupport/MoShiro.TestSupport.csproj similarity index 77% rename from backend/tests/Monitor.TestSupport/Monitor.TestSupport.csproj rename to backend/tests/MoShiro.TestSupport/MoShiro.TestSupport.csproj index ae60e1e..56e8167 100644 --- a/backend/tests/Monitor.TestSupport/Monitor.TestSupport.csproj +++ b/backend/tests/MoShiro.TestSupport/MoShiro.TestSupport.csproj @@ -3,7 +3,7 @@ false - + diff --git a/backend/tests/Monitor.TestSupport/ProbeSimulator.cs b/backend/tests/MoShiro.TestSupport/ProbeSimulator.cs similarity index 96% rename from backend/tests/Monitor.TestSupport/ProbeSimulator.cs rename to backend/tests/MoShiro.TestSupport/ProbeSimulator.cs index d2734a1..972bc58 100644 --- a/backend/tests/Monitor.TestSupport/ProbeSimulator.cs +++ b/backend/tests/MoShiro.TestSupport/ProbeSimulator.cs @@ -8,9 +8,9 @@ using Fleet.Core; using Fleet.Identity; using Fleet.Link; using Fleet.Rpc; -using Monitor.Protocol; +using MoShiro.Protocol; -namespace Monitor.TestSupport; +namespace MoShiro.TestSupport; /// /// 假 Probe:通过真实的点对点握手(TLS 固定证书 + 签名 + ECDH)连接真实的 Agent,接收 line_task_set, @@ -104,7 +104,7 @@ public sealed class ProbeSimulator : IAsyncDisposable for (int i = 0; i < rounds; i++) { long t0 = NowUs(); - Result reply = await _rpc!.RequestAsync(MonitorMethods.Clock, new ClockParams { ProbeMonoUs = t0 }, MonitorJsonContext.Default.ClockParams, MonitorJsonContext.Default.ClockResult, CancellationToken.None); + Result reply = await _rpc!.RequestAsync(MoShiroMethods.Clock, new ClockParams { ProbeMonoUs = t0 }, MoShiroJsonContext.Default.ClockParams, MoShiroJsonContext.Default.ClockResult, CancellationToken.None); long t1 = NowUs(); long roundTrip = t1 - t0; if (reply.IsSuccess && roundTrip < bestRoundTrip) @@ -214,7 +214,7 @@ public sealed class ProbeSimulator : IAsyncDisposable Guid id = fragment.Header.ChunkId.Value; var ack = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _acks[id] = ack; - await _rpc!.SendBinaryAsync(OutboxFrame.Build(MonitorChannels.ColumnBlock, id, RawChunkCodec.Encode(fragment)), ct); + await _rpc!.SendBinaryAsync(OutboxFrame.Build(MoShiroChannels.ColumnBlock, id, RawChunkCodec.Encode(fragment)), ct); if (waitForAck) { await ack.Task.WaitAsync(TimeSpan.FromSeconds(10), ct); @@ -322,24 +322,24 @@ public sealed class ProbeSimulator : IAsyncDisposable { if (sync.Type == LineTaskSet.StateType) { - probe._taskSets.Enqueue(sync.Doc.Deserialize(MonitorJsonContext.Default.LineTaskSet)!); + probe._taskSets.Enqueue(sync.Doc.Deserialize(MoShiroJsonContext.Default.LineTaskSet)!); } return ValueTask.FromResult(Result.Ok(new StateSyncResult { AppliedVersion = sync.Version })); }) .On( - MonitorMethods.TestPhase, - MonitorJsonContext.Default.TestPhaseStartParams, - MonitorJsonContext.Default.TestPhaseStartResult, + MoShiroMethods.TestPhase, + MoShiroJsonContext.Default.TestPhaseStartParams, + MoShiroJsonContext.Default.TestPhaseStartResult, async (probe, start, ct) => Result.Ok(new TestPhaseStartResult { Accepted = await probe.StartPhaseAsync(start, ct) })) .On( - MonitorMethods.TestPhaseEnd, - MonitorJsonContext.Default.TestPhaseEndParams, - MonitorJsonContext.Default.TestPhaseEndResult, + MoShiroMethods.TestPhaseEnd, + MoShiroJsonContext.Default.TestPhaseEndParams, + MoShiroJsonContext.Default.TestPhaseEndResult, async (probe, end, ct) => Result.Ok(new TestPhaseEndResult { Fragments = await probe.EndPhaseAsync(end, ct) })) .OnNotify( - MonitorMethods.TestAbort, - MonitorJsonContext.Default.TestAbortParams, + MoShiroMethods.TestAbort, + MoShiroJsonContext.Default.TestAbortParams, (probe, abort, _) => { probe.Aborts.Enqueue(abort.TestId); diff --git a/ci/Monitor.Tar/Monitor.Tar.csproj b/ci/MoShiro.Tar/MoShiro.Tar.csproj similarity index 100% rename from ci/Monitor.Tar/Monitor.Tar.csproj rename to ci/MoShiro.Tar/MoShiro.Tar.csproj diff --git a/ci/Monitor.Tar/Program.cs b/ci/MoShiro.Tar/Program.cs similarity index 96% rename from ci/Monitor.Tar/Program.cs rename to ci/MoShiro.Tar/Program.cs index a66e340..65587dc 100644 --- a/ci/Monitor.Tar/Program.cs +++ b/ci/MoShiro.Tar/Program.cs @@ -3,7 +3,7 @@ using System.IO.Compression; if (args.Length < 3) { - Console.Error.WriteLine("usage: Monitor.Tar ..."); + Console.Error.WriteLine("usage: MoShiro.Tar ..."); return 2; } diff --git a/ci/build-main-amd64.sh b/ci/build-main-amd64.sh index 68b1b1d..649dc5d 100644 --- a/ci/build-main-amd64.sh +++ b/ci/build-main-amd64.sh @@ -23,7 +23,7 @@ esac root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" registry="${ZOT_REGISTRY:-registry.oeo.one}" -repository="$registry/monitor-dev/monitor-$component" +repository="$registry/moshiro-dev/moshiro-$component" work="$root/.release/main-amd64-build" base_home="/home/justme/woodpecker/buildah-home/main-amd64-build-$component" short_sha="${CI_COMMIT_SHA:0:12}" @@ -44,12 +44,12 @@ export HOME="$base_home" export BUILDAH_HOME="$base_home" export REGISTRY_AUTH_FILE="$work/$component-auth.json" context="$work/$component" -local_image="localhost/monitor-$component:main-amd64-check" +local_image="localhost/moshiro-$component:main-amd64-check" python3 "$root/ci/stage-node-image.py" "$component" "$context" buildah bud --layers --network host --tls-verify=true --arch amd64 \ --build-arg APP_VERSION=main \ - --build-arg "MONITOR_TAG=$dev_tag" --build-arg "MONITOR_COMMIT=${CI_COMMIT_SHA:0:10}" --build-arg "MONITOR_BUILT_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --build-arg "MOSHIRO_TAG=$dev_tag" --build-arg "MOSHIRO_COMMIT=${CI_COMMIT_SHA:0:10}" --build-arg "MOSHIRO_BUILT_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ -f "$context/Containerfile" \ -t "$local_image" "$context" diff --git a/ci/index-backend-image.sh b/ci/index-backend-image.sh index aff521b..793d15b 100644 --- a/ci/index-backend-image.sh +++ b/ci/index-backend-image.sh @@ -11,7 +11,7 @@ version="${CI_COMMIT_TAG#v}" registry="${ZOT_REGISTRY:-registry.oeo.one}" scheme="${ZOT_SCHEME:-https}" -image="monitor/monitor-backend" +image="moshiro/moshiro-backend" auth_file="$(mktemp)" chmod 600 "$auth_file" trap 'rm -f "$auth_file"' EXIT diff --git a/ci/package-cross-platform.ps1 b/ci/package-cross-platform.ps1 index 32c02be..71327a4 100644 --- a/ci/package-cross-platform.ps1 +++ b/ci/package-cross-platform.ps1 @@ -15,14 +15,14 @@ if ($version -ne $projectVersion) { throw "Tag version $version does not match project version $projectVersion." } -$tarProject = Join-Path $PSScriptRoot 'Monitor.Tar\Monitor.Tar.csproj' -$tarTool = Join-Path $PSScriptRoot 'Monitor.Tar\bin\Release\net10.0\Monitor.Tar.dll' +$tarProject = Join-Path $PSScriptRoot 'MoShiro.Tar\MoShiro.Tar.csproj' +$tarTool = Join-Path $PSScriptRoot 'MoShiro.Tar\bin\Release\net10.0\MoShiro.Tar.dll' & dotnet build $tarProject -c Release --nologo if ($LASTEXITCODE -ne 0) { throw 'Could not build the cross-platform tar helper.' } $giteaBase = if ($env:GITEA_BASE) { $env:GITEA_BASE.TrimEnd('/') } else { 'http://localhost:3100' } -$netrc = Join-Path $env:TEMP ("monitor-gitea-$([guid]::NewGuid().ToString('N')).netrc") +$netrc = Join-Path $env:TEMP ("moshiro-gitea-$([guid]::NewGuid().ToString('N')).netrc") $giteaHost = ([Uri]$giteaBase).Host Set-Content -LiteralPath $netrc -Value "machine $giteaHost login justme password $($env:GITEA_TOKEN)" -Encoding ascii -NoNewline @@ -61,11 +61,11 @@ try { $stage = Join-Path $root ".release\stage\$($target.Rid)\$role" $null = New-Item -ItemType Directory -Force -Path $stage $extension = if ($target.Windows) { '.exe' } else { '' } - Copy-Item -LiteralPath (Join-Path (Join-Path $root ".release\publish\$($target.Rid)\$role") "monitor-$role$extension") -Destination $stage + Copy-Item -LiteralPath (Join-Path (Join-Path $root ".release\publish\$($target.Rid)\$role") "moshiro-$role$extension") -Destination $stage Copy-Item -LiteralPath (Join-Path $root 'LICENSE') -Destination $stage Copy-Item -LiteralPath (Join-Path $root "backend\$role\README.md") -Destination $stage - $executables = @("monitor-$role$extension") + $executables = @("moshiro-$role$extension") if ($role -eq 'agent') { $enrollName = if ($target.Windows) { 'fleet-enroll.exe' } else { 'fleet-enroll' } Copy-Item -LiteralPath (Join-Path $enrollDirectory $enrollName) -Destination $stage @@ -76,12 +76,12 @@ try { $executables += 'install.sh' $systemd = Join-Path $stage 'systemd' $null = New-Item -ItemType Directory -Force -Path $systemd - Copy-Item -LiteralPath (Join-Path $root "packaging/systemd/monitor-$role.service") -Destination $systemd + Copy-Item -LiteralPath (Join-Path $root "packaging/systemd/moshiro-$role.service") -Destination $systemd if ($role -eq 'probe') { $openwrt = Join-Path $stage 'openwrt' $null = New-Item -ItemType Directory -Force -Path $openwrt - Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/monitor-probe.init') -Destination $openwrt - Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/monitor.keep') -Destination $openwrt + Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/moshiro-probe.init') -Destination $openwrt + Copy-Item -LiteralPath (Join-Path $root 'packaging/openwrt/moshiro.keep') -Destination $openwrt } } elseif ($target.Os -eq 'macos') { @@ -94,7 +94,7 @@ consumer install. '@ | Set-Content -LiteralPath (Join-Path $stage 'MACOS-SECURITY.txt') -Encoding utf8 } - $archiveName = if ($target.Windows) { "monitor-$role-v$version-windows-$($target.Arch).zip" } else { "monitor-$role-v$version-$($target.Os)-$($target.Arch).tar.gz" } + $archiveName = if ($target.Windows) { "moshiro-$role-v$version-windows-$($target.Arch).zip" } else { "moshiro-$role-v$version-$($target.Os)-$($target.Arch).tar.gz" } $archive = Join-Path $packageRoot $archiveName if ($target.Windows) { Compress-Archive -Path (Join-Path $stage '*') -DestinationPath $archive -CompressionLevel Optimal -Force @@ -115,7 +115,7 @@ consumer install. param([string]$File, [string]$Base, [string]$PackageVersion, [string]$Netrc) $ErrorActionPreference = 'Stop' $name = [IO.Path]::GetFileName($File) -$url = "$Base/api/packages/justme/generic/monitor-node/$PackageVersion/$name" +$url = "$Base/api/packages/justme/generic/moshiro-node/$PackageVersion/$name" $status = & curl.exe --silent --show-error --retry 3 --netrc-file $Netrc --upload-file $File --output NUL --write-out '%{http_code}' $url if ($LASTEXITCODE -ne 0) { throw "Gitea package upload failed for $name (curl exit $LASTEXITCODE)." diff --git a/ci/publish-gitea-release.sh b/ci/publish-gitea-release.sh index 80dabea..55cc612 100644 --- a/ci/publish-gitea-release.sh +++ b/ci/publish-gitea-release.sh @@ -16,7 +16,7 @@ gitea_host="${gitea_base#https://}" gitea_host="${gitea_host#http://}" gitea_host="${gitea_host%%/*}" gitea_host="${gitea_host%%:*}" -api="$gitea_base/api/v1/repos/justme/MyMonitor" +api="$gitea_base/api/v1/repos/justme/MoShiro" tmp="$(mktemp -d)" netrc="$tmp/netrc" release_json="$tmp/release.json" @@ -26,8 +26,8 @@ printf 'machine %s login justme password %s\n' "$gitea_host" "$GITEA_TOKEN" > "$ chmod 600 "$netrc" trap 'rm -rf "$tmp"' EXIT -release_body="Agent and Probe builds: Linux glibc and musl (x64, arm64), macOS (x64, arm64), and Windows (x64, arm64). Linux glibc Agent and Probe OCI images: registry.oeo.one/monitor/monitor-agent:$version and registry.oeo.one/monitor/monitor-probe:$version (linux/amd64 and linux/arm64). Backend OCI image: registry.oeo.one/monitor/monitor-backend:$version (linux/amd64 and linux/arm64). macOS binaries are not Developer ID signed or notarized." -payload="$(printf '{"tag_name":"%s","target_commitish":"%s","name":"MyMonitor %s","body":"%s","draft":false,"prerelease":false}' \ +release_body="Agent and Probe builds: Linux glibc and musl (x64, arm64), macOS (x64, arm64), and Windows (x64, arm64). Linux glibc Agent and Probe OCI images: registry.oeo.one/moshiro/moshiro-agent:$version and registry.oeo.one/moshiro/moshiro-probe:$version (linux/amd64 and linux/arm64). Backend OCI image: registry.oeo.one/moshiro/moshiro-backend:$version (linux/amd64 and linux/arm64). macOS binaries are not Developer ID signed or notarized." +payload="$(printf '{"tag_name":"%s","target_commitish":"%s","name":"MoShiro %s","body":"%s","draft":false,"prerelease":false}' \ "$tag" "${CI_COMMIT_SHA:-}" "$tag" "$release_body")" release_status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \ --output "$release_json" --write-out '%{http_code}' "$api/releases/tags/$tag")" @@ -60,28 +60,28 @@ curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \ "$api/releases/$release_id/assets" -o "$assets_json" assets=( - "monitor-agent-v$version-linux-amd64.tar.gz" - "monitor-agent-v$version-linux-arm64.tar.gz" - "monitor-probe-v$version-linux-amd64.tar.gz" - "monitor-probe-v$version-linux-arm64.tar.gz" - "monitor-agent-v$version-linux-musl-amd64.tar.gz" - "monitor-probe-v$version-linux-musl-amd64.tar.gz" - "monitor-agent-v$version-linux-musl-arm64.tar.gz" - "monitor-probe-v$version-linux-musl-arm64.tar.gz" - "monitor-agent-v$version-macos-x64.tar.gz" - "monitor-probe-v$version-macos-x64.tar.gz" - "monitor-agent-v$version-macos-arm64.tar.gz" - "monitor-probe-v$version-macos-arm64.tar.gz" - "monitor-agent-v$version-windows-amd64.zip" - "monitor-agent-v$version-windows-arm64.zip" - "monitor-probe-v$version-windows-amd64.zip" - "monitor-probe-v$version-windows-arm64.zip" + "moshiro-agent-v$version-linux-amd64.tar.gz" + "moshiro-agent-v$version-linux-arm64.tar.gz" + "moshiro-probe-v$version-linux-amd64.tar.gz" + "moshiro-probe-v$version-linux-arm64.tar.gz" + "moshiro-agent-v$version-linux-musl-amd64.tar.gz" + "moshiro-probe-v$version-linux-musl-amd64.tar.gz" + "moshiro-agent-v$version-linux-musl-arm64.tar.gz" + "moshiro-probe-v$version-linux-musl-arm64.tar.gz" + "moshiro-agent-v$version-macos-x64.tar.gz" + "moshiro-probe-v$version-macos-x64.tar.gz" + "moshiro-agent-v$version-macos-arm64.tar.gz" + "moshiro-probe-v$version-macos-arm64.tar.gz" + "moshiro-agent-v$version-windows-amd64.zip" + "moshiro-agent-v$version-windows-arm64.zip" + "moshiro-probe-v$version-windows-amd64.zip" + "moshiro-probe-v$version-windows-arm64.zip" ) upload_release_asset() { local name="$1" file="$tmp/$1" upload_response="$tmp/$1.upload.json" status curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \ - "$gitea_base/api/packages/justme/generic/monitor-node/$version/$name" -o "$file" + "$gitea_base/api/packages/justme/generic/moshiro-node/$version/$name" -o "$file" status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \ --request POST --header 'Content-Type: application/octet-stream' --data-binary "@$file" \ --output "$upload_response" --write-out '%{http_code}' \ @@ -111,4 +111,4 @@ for pid in "${pids[@]}"; do wait "$pid" done -echo "published Gitea Release $tag: https://git.oeo.one/justme/MyMonitor/releases/tag/$tag" +echo "published Gitea Release $tag: https://git.oeo.one/justme/MoShiro/releases/tag/$tag" diff --git a/ci/release-cross-platform.ps1 b/ci/release-cross-platform.ps1 index d048fd0..2d3d60b 100644 --- a/ci/release-cross-platform.ps1 +++ b/ci/release-cross-platform.ps1 @@ -43,8 +43,8 @@ if ($RuntimeIdentifier -in @('win-x64', 'win-arm64')) { } $project = switch ($Component) { - 'agent' { 'agent/src/Monitor.Agent' } - 'probe' { 'probe/src/Monitor.Probe' } + 'agent' { 'agent/src/MoShiro.Agent' } + 'probe' { 'probe/src/MoShiro.Probe' } 'fleet-enroll' { 'fleet/tools/fleet-enroll' } } $output = Join-Path $root ".release\publish\$RuntimeIdentifier\$Component" @@ -52,9 +52,9 @@ $buildRoot = Join-Path $root ".release\build\$RuntimeIdentifier-$Component" $null = New-Item -ItemType Directory -Force -Path $output # 构建信息:程序里显示的 tag、提交、构建时间 -$env:MONITOR_TAG = if ($env:CI_COMMIT_TAG) { $env:CI_COMMIT_TAG } else { "main-$($env:CI_COMMIT_SHA.Substring(0, 10))" } -$env:MONITOR_COMMIT = $env:CI_COMMIT_SHA.Substring(0, 10) -$env:MONITOR_BUILT_AT = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') +$env:MOSHIRO_TAG = if ($env:CI_COMMIT_TAG) { $env:CI_COMMIT_TAG } else { "main-$($env:CI_COMMIT_SHA.Substring(0, 10))" } +$env:MOSHIRO_COMMIT = $env:CI_COMMIT_SHA.Substring(0, 10) +$env:MOSHIRO_BUILT_AT = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') Push-Location $backend try { diff --git a/ci/release-linux.sh b/ci/release-linux.sh index f7e34f8..e801990 100644 --- a/ci/release-linux.sh +++ b/ci/release-linux.sh @@ -40,25 +40,25 @@ if [[ -n "$tag" && "$version" != "$project_version" ]]; then fi registry="${ZOT_REGISTRY:-registry.oeo.one}" -image="${ZOT_IMAGE_REPOSITORY:-$registry/monitor/monitor-backend}" +image="${ZOT_IMAGE_REPOSITORY:-$registry/moshiro/moshiro-backend}" output="$root/.release/linux-$arch/backend" build_root="$root/.release/build/linux-$arch-backend" container_context="$root/.release/container/linux-$arch" mkdir -p "$output" "$build_root" "$container_context/app" "$container_context/data" # 构建信息:tag 发布用 tag,main 构建用 main-<提交> -export MONITOR_TAG="${MONITOR_TAG:-${tag:-main-${CI_COMMIT_SHA:0:10}}}" -export MONITOR_COMMIT="${MONITOR_COMMIT:-${CI_COMMIT_SHA:0:10}}" -export MONITOR_BUILT_AT="${MONITOR_BUILT_AT:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" +export MOSHIRO_TAG="${MOSHIRO_TAG:-${tag:-main-${CI_COMMIT_SHA:0:10}}}" +export MOSHIRO_COMMIT="${MOSHIRO_COMMIT:-${CI_COMMIT_SHA:0:10}}" +export MOSHIRO_BUILT_AT="${MOSHIRO_BUILT_AT:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" -(cd "$root/backend" && dotnet publish src/Monitor.Backend \ +(cd "$root/backend" && dotnet publish src/MoShiro.Backend \ -m:3 -c Release -r "$rid" --self-contained true \ -p:DebugType=none -p:DebugSymbols=false \ -p:PublishAot=true -p:UseAotAnywhere=true -p:SqliteStaticLink=true -p:OpenApiGenerateDocuments=false \ --artifacts-path "$build_root" -o "$output") -[[ -x "$output/monitor-backend" ]] || { echo "NativeAOT Backend executable is missing" >&2; exit 1; } -[[ "$(wc -c < "$output/monitor-backend")" -gt 2000000 ]] || { echo "Backend output is an apphost, not a NativeAOT executable" >&2; exit 1; } -install -m 0755 "$output/monitor-backend" "$container_context/app/monitor-backend" +[[ -x "$output/moshiro-backend" ]] || { echo "NativeAOT Backend executable is missing" >&2; exit 1; } +[[ "$(wc -c < "$output/moshiro-backend")" -gt 2000000 ]] || { echo "Backend output is an apphost, not a NativeAOT executable" >&2; exit 1; } +install -m 0755 "$output/moshiro-backend" "$container_context/app/moshiro-backend" : > "$container_context/data/.keep" # Woodpecker's local backend isolates HOME per workflow. Keep Buildah's image @@ -68,7 +68,7 @@ buildah_home="${BUILDAH_HOME:-/home/justme/woodpecker/buildah-home/$arch}" mkdir -p "$buildah_home" export HOME="$buildah_home" -local_image="localhost/monitor-backend:$version-$arch" +local_image="localhost/moshiro-backend:$version-$arch" buildah bud --tls-verify=true --arch "$arch" \ --build-arg "APP_VERSION=$version" \ -f "$root/packaging/container/Backend.Containerfile" \ diff --git a/ci/release-node-images.sh b/ci/release-node-images.sh index 95eaa7e..e6c1cd9 100644 --- a/ci/release-node-images.sh +++ b/ci/release-node-images.sh @@ -28,7 +28,7 @@ gitea_host="${gitea_host#http://}" gitea_host="${gitea_host%%/*}" gitea_host="${gitea_host%%:*}" registry="${ZOT_REGISTRY:-registry.oeo.one}" -image_root="$registry/monitor" +image_root="$registry/moshiro" work="$root/.release/node-images" netrc="$work/netrc" buildah_home="/home/justme/woodpecker/buildah-home/node-images" @@ -56,23 +56,23 @@ done build_arch_image() { local component="$1" arch="$2" suffix="$3" - local filename="monitor-$component-v$version-linux-$suffix.tar.gz" + local filename="moshiro-$component-v$version-linux-$suffix.tar.gz" local context="$work/$component-$arch" local archive="$work/$filename" local containerfile="$root/packaging/container/${component^}.Containerfile" - local image="$image_root/monitor-$component" - local local_image="localhost/monitor-$component:$version-$arch" + local image="$image_root/moshiro-$component" + local local_image="localhost/moshiro-$component:$version-$arch" curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \ - "$gitea_base/api/packages/justme/generic/monitor-node/$version/$filename" -o "$archive" + "$gitea_base/api/packages/justme/generic/moshiro-node/$version/$filename" -o "$archive" mkdir -p "$context/app" "$context/config" if [[ "$component" == agent ]]; then - tar -xzf "$archive" -C "$work" monitor-agent fleet-enroll + tar -xzf "$archive" -C "$work" moshiro-agent fleet-enroll install -m 0755 "$work/fleet-enroll" "$context/app/fleet-enroll" else - tar -xzf "$archive" -C "$work" "monitor-$component" + tar -xzf "$archive" -C "$work" "moshiro-$component" fi - install -m 0755 "$work/monitor-$component" "$context/app/monitor-$component" + install -m 0755 "$work/moshiro-$component" "$context/app/moshiro-$component" : > "$context/config/.keep" buildah bud --tls-verify=true --arch "$arch" \ @@ -95,8 +95,8 @@ build_arch_image() { } publish_index() { - local component="$1" image="$image_root/monitor-$component" - local manifest="localhost/monitor-$component:$version-multiarch" + local component="$1" image="$image_root/moshiro-$component" + local manifest="localhost/moshiro-$component:$version-multiarch" buildah manifest rm "$manifest" >/dev/null 2>&1 || true buildah manifest create "$manifest" >/dev/null buildah manifest add "$manifest" "docker://$image:$version-amd64" >/dev/null diff --git a/compose.yaml b/compose.yaml index 1b83feb..a6471c3 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,18 +1,18 @@ services: backend: - image: registry.oeo.one/monitor/monitor-backend:${MONITOR_VERSION:-0.1.1} - container_name: monitor-backend + image: registry.oeo.one/moshiro/moshiro-backend:${MOSHIRO_VERSION:-0.1.1} + container_name: moshiro-backend restart: unless-stopped init: true ports: - "127.0.0.1:8080:8080" environment: ASPNETCORE_HTTP_PORTS: "8080" - Monitor__DataDirectory: /data - Monitor__InitialAdminUser: ${MONITOR_ADMIN_USER:-admin} - Monitor__InitialAdminPassword: "${MONITOR_ADMIN_PASSWORD:?Set MONITOR_ADMIN_PASSWORD in the environment}" + MoShiro__DataDirectory: /data + MoShiro__InitialAdminUser: ${MOSHIRO_ADMIN_USER:-admin} + MoShiro__InitialAdminPassword: "${MOSHIRO_ADMIN_PASSWORD:?Set MOSHIRO_ADMIN_PASSWORD in the environment}" volumes: - - monitor-data:/data + - moshiro-data:/data read_only: true tmpfs: - /tmp:rw,noexec,nosuid,size=64m,mode=1777 @@ -23,4 +23,4 @@ services: stop_grace_period: 30s volumes: - monitor-data: + moshiro-data: diff --git a/docs/analysis.md b/docs/analysis.md index aa2545c..9785a89 100644 --- a/docs/analysis.md +++ b/docs/analysis.md @@ -135,7 +135,7 @@ chunk 入库 ─┬─> 流式路径:按 1s 桶计算累加器 → 热层环 ## 9. 项目结构 ```text -backend/src/Monitor.Backend.Analysis/ 依赖:Monitor.Protocol、Fleet.Core(纯计算,没有 I/O) +backend/src/MoShiro.Backend.Analysis/ 依赖:MoShiro.Protocol、Fleet.Core(纯计算,没有 I/O) ├─ Accumulators/ Counter Ratio Sum Min Max Histogram RunLength Series + AccumulatorCodec ├─ Ops/ Rtt Loss OwdVar LossRuns Reorder Gap KernelDelta Rate PhaseMs Outcome ChangePoint ├─ Metrics/ LinkMetrics TcpMetrics UdpTrainMetrics ConnectMetrics …(只包含声明) @@ -144,4 +144,4 @@ backend/src/Monitor.Backend.Analysis/ 依赖:Monitor.Protocol、Fleet.Core └─ Clock/ ClockDriftFit(估计两端的时钟偏移和漂移) ``` -分析库没有 I/O,可以直接用于离线 CLI `tools/monitor-analyze`,对下载的原始块做实验。 +分析库没有 I/O,可以直接用于离线 CLI `tools/moshiro-analyze`,对下载的原始块做实验。 diff --git a/docs/api-guidelines.md b/docs/api-guidelines.md index 266736d..cac57ce 100644 --- a/docs/api-guidelines.md +++ b/docs/api-guidelines.md @@ -16,7 +16,7 @@ web:openapi-typescript 生成类型,调用方的类型错误会直接暴露 - 端点**必须**声明精确的返回类型,例如 `Results, NotFound>`,不能返回 `IResult` 或匿名对象,否则 OpenAPI 文档会不准确。 - 每个端点都必须设置 `WithName("agents_list")`(即 operationId,snake_case、`资源_动作`)、`WithSummary`、`WithTags`。 -- 请求和响应 DTO 放在 `Monitor.Backend.Api/Contracts/`,并注册到 Backend 的 JSON 源生成上下文。**不要直接暴露 `Monitor.Protocol` 中的线协议类型**,因为 REST 与线协议的演进节奏不同。 +- 请求和响应 DTO 放在 `MoShiro.Backend.Api/Contracts/`,并注册到 Backend 的 JSON 源生成上下文。**不要直接暴露 `MoShiro.Protocol` 中的线协议类型**,因为 REST 与线协议的演进节奏不同。 - **结构强类型,指标弱类型**:资源结构(Agent、任务、测试等)必须是强类型 DTO;而**指标值和评分**统一使用字典 `metrics: { "": number }`、`scores: { "": number }`,键的含义、单位、版本由 `/api/public/catalog/*` 给出。这样新增指标或模型不需要改 OpenAPI,也不需要改前端类型。 ## 2. 版本与兼容性 diff --git a/docs/api.md b/docs/api.md index 3c21298..1a1c182 100644 --- a/docs/api.md +++ b/docs/api.md @@ -95,7 +95,7 @@ `GET /api/public/about`:`{show_build, backend}`。`show_build` 关着时 `backend` 是 `null`;打开后 `backend` 是 `{version, tag, built_at}`(后端自己的版本、tag、构建时间,不含提交号)。 - 系统和架构是节点自报的:Agent 和 Probe 都在各自连面板时的 `fleet.hello` 里自报。只有粗粒度的名字,没有主机名、内核版本、CPU 型号。 -- 国家和运营商由 **Backend** 用节点的来源地址向 IP 归属服务查一次(`Monitor__GeoLookupUrl`,缺省 `https://ip.007995.xyz`,留空就不查):Probe 用 Agent 看到的来源 IP,Agent 用它的公开地址。**Probe 和 Agent 不访问任何外部服务。** 只查公网地址(回环、内网、CGNAT、链路本地一律不发出去);同一个地址一周内只查一次,失败 10 分钟后再试;查到的只留国家代码和运营商,地址只存在 Backend 内部的 `node_meta.geo_host`,不对外。 +- 国家和运营商由 **Backend** 用节点的来源地址向 IP 归属服务查一次(`MoShiro__GeoLookupUrl`,缺省 `https://ip.007995.xyz`,留空就不查):Probe 用 Agent 看到的来源 IP,Agent 用它的公开地址。**Probe 和 Agent 不访问任何外部服务。** 只查公网地址(回环、内网、CGNAT、链路本地一律不发出去);同一个地址一周内只查一次,失败 10 分钟后再试;查到的只留国家代码和运营商,地址只存在 Backend 内部的 `node_meta.geo_host`,不对外。 ### 2.4 测试结果与目录 @@ -105,7 +105,7 @@ ### 2.5 跨域访问 -默认前端和 Backend 同源(Cookie 会话)。前端在别的来源时(比如本机跑前端、连线上的 Backend),Backend 设 `Monitor__CorsOrigins`(逗号分隔的来源,或 `*`;默认空 = 不允许)。只对 `/api/*` 生效,响应带 `Access-Control-Allow-Origin`,预检请求(`OPTIONS`)返回 204,允许的头是 `Authorization, Content-Type, X-CSRF-Token`。**不带凭据**,Cookie 不会跨域,所以跨域的调用只能用 `Authorization: Bearer `:展示端用只读令牌(站点私有时需要),管理端用 admin 范围的令牌(`/api/admin/auth/me` 对令牌同样可用)。SSE 在跨域时前端用 fetch 读流,因为 `EventSource` 带不了请求头。 +默认前端和 Backend 同源(Cookie 会话)。前端在别的来源时(比如本机跑前端、连线上的 Backend),Backend 设 `MoShiro__CorsOrigins`(逗号分隔的来源,或 `*`;默认空 = 不允许)。只对 `/api/*` 生效,响应带 `Access-Control-Allow-Origin`,预检请求(`OPTIONS`)返回 204,允许的头是 `Authorization, Content-Type, X-CSRF-Token`。**不带凭据**,Cookie 不会跨域,所以跨域的调用只能用 `Authorization: Bearer `:展示端用只读令牌(站点私有时需要),管理端用 admin 范围的令牌(`/api/admin/auth/me` 对令牌同样可用)。SSE 在跨域时前端用 fetch 读流,因为 `EventSource` 带不了请求头。 ## 3. Admin API @@ -130,7 +130,7 @@ | 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/tests/{id}/cancel` | | | GET | `/api/admin/raw/chunks?agent_id=&kind=&from=&to=&test_id=` | 原始块元数据 | -| GET | `/api/admin/raw/chunks/{chunk_id}` | 下载原始块(`application/vnd.monitor.rawchunk`);过期返回 410 `raw_expired` | +| GET | `/api/admin/raw/chunks/{chunk_id}` | 下载原始块(`application/vnd.moshiro.rawchunk`);过期返回 410 `raw_expired` | | GET | `/api/admin/events?agent_id=&kind=&from=&to=` | 事件 | | GET | `/api/admin/analysis/recompute` | 指标重算进度 | | GET/POST/DELETE | `/api/admin/api-tokens` | 作用域为 `read`、`admin` 或 `release`(只能登记发布清单,给 CI 用,什么都读不了);明文只显示一次 | @@ -157,7 +157,7 @@ | 方法 | 路径 | 说明 | | --- | --- | --- | | POST | `/api/agent/v1/enroll` | 由 `MapFleetEnrollment` 提供(使用加入令牌认证),见 [fleet/link.md](fleet/link.md) 第 3 节 | -| GET | `/api/agent/v1/rpc` | 由 `MapFleetLink` 提供,升级为 WebSocket;Monitor 的方法见 [protocol.md](protocol.md) | +| GET | `/api/agent/v1/rpc` | 由 `MapFleetLink` 提供,升级为 WebSocket;MoShiro 的方法见 [protocol.md](protocol.md) | | GET | `/fleet/releases/{app}/{channel}/latest.json`、`/fleet/releases/{app}/{version}/…` | 由 `MapFleetReleases` 提供的公开发布源(清单和制品),供 `fleet-updater` 轮询;也可以改用 GitHub Releases | ## 5. 运维端点 diff --git a/docs/architecture.md b/docs/architecture.md index b503a87..7c5d6ba 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -53,13 +53,13 @@ | 主题 | 要点 | 文档 | | --- | --- | --- | -| 节点框架 | 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 **Fleet** 提供;Monitor 只写业务,其他产品也能复用 | [fleet/overview.md](fleet/overview.md) | +| 节点框架 | 注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新都由与业务无关的 **Fleet** 提供;MoShiro 只写业务,其他产品也能复用 | [fleet/overview.md](fleet/overview.md) | | 注册与身份 | 使用 `fleet-enroll` 和加入令牌自动注册,之后用签名认证;Probe 用自己的身份直连面板,Agent 列表由面板下发 | [enrollment.md](enrollment.md) | -| 更新 | 只接受发布密钥签名的清单,更新策略由节点本地决定,失败自动回滚。容器节点(Agent、Probe)已实现:CI 对镜像 digest 签名,宿主机上的 `monitor-update` 验签、按 digest 拉取、重启;纯二进制的 `fleet-updater` 还只是设计 | [deployment.md](deployment.md)、[fleet/update.md](fleet/update.md) | +| 更新 | 只接受发布密钥签名的清单,更新策略由节点本地决定,失败自动回滚。容器节点(Agent、Probe)已实现:CI 对镜像 digest 签名,宿主机上的 `moshiro-update` 验签、按 digest 拉取、重启;纯二进制的 `fleet-updater` 还只是设计 | [deployment.md](deployment.md)、[fleet/update.md](fleet/update.md) | | 任务模型 | `Task = {kind, target, params, schedule}`;持续采集、按需测试、目标任务统一建模;下发“期望任务集”,由节点自行调和 | [measurement.md](measurement.md) | | 采集扩展 | Kind 由积木组合声明;采集密度靠参数调整;原始块自描述 | [measurement.md](measurement.md) | | 统计扩展 | 指标注册制:少数几种累加器 + 统计算子;一行 `MetricDef` 就是一个新指标 | [analysis.md](analysis.md) | -| 协议 | 控制链路使用 Fleet(JSON-RPC 2.0 + 二进制通道);Monitor 只定义 `monitor.*` 方法、`TaskSet` 状态文档和 UDP/TCP 数据面 | [protocol.md](protocol.md) | +| 协议 | 控制链路使用 Fleet(JSON-RPC 2.0 + 二进制通道);MoShiro 只定义 `moshiro.*` 方法、`TaskSet` 状态文档和 UDP/TCP 数据面 | [protocol.md](protocol.md) | | 存储 | 原始块追加写入段文件,SQLite 记录偏移量;指标窗口通用存储并降采样 | [storage.md](storage.md) | | 展示查询 | 任意时间段(相对或绝对);自动步长(每条曲线最多约 720 个点);1s 内存热层(1 小时)加上 10s/1m/5m/1h 各层;累加器现场合并,汇总精确;列式响应;SSE 实时跟随 | [query.md](query.md) | | API | `/api/public`(只读,可设为公开)、`/api/admin`(登录)、`/api/agent`(注册与 RPC);实时数据用 SSE | [api.md](api.md) | @@ -70,7 +70,7 @@ CI ──签名的发布清单(镜像 digest)──▶ Backend ──原样转发(offer)──▶ Agent / Probe 进程 │ 只写进数据目录的 update/ ▼ -宿主机 monitor-update(root):用本机公钥验签 → 核对组件、仓库和 seq → podman pull 仓库@digest → 重启 → 不健康就回滚 +宿主机 moshiro-update(root):用本机公钥验签 → 核对组件、仓库和 seq → podman pull 仓库@digest → 重启 → 不健康就回滚 │ 状态写进只读的 update-status/ ▼ 节点随心跳上报更新状态 ──▶ Backend ──▶ 管理端(有新版本时点「更新」) @@ -97,13 +97,13 @@ Agent / Probe 能做的 执行编译在二进制中的 Kind;监听和访问 - **本地硬上限**写在节点本机的配置文件中,Backend 下发的任务如果超出,节点直接拒绝:允许访问的目标网段(默认为空,即不开放目标任务)、总码率和包速率上限、允许监听的端口。 - **构建期强制**:常驻节点进程使用 `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`)、更新是不同的进程。容器节点里,采集进程在容器内以 UID 1654 运行(只读文件系统、不带额外能力),更新由宿主机上 root 运行的 `monitor-update` 完成:它只读取节点放进 `update/` 的普通文件、复制到自己的临时目录后验签,状态写在节点只读的 `update-status/`;采集进程改写不了自己的二进制,更新器也读不到节点身份私钥。纯二进制安装的 `fleet-updater` 还只是设计。 +- **权限分离**:注册(`fleet-enroll`,一次性运行)、采集(`moshiro-agent` / `moshiro-probe`)、更新是不同的进程。容器节点里,采集进程在容器内以 UID 1654 运行(只读文件系统、不带额外能力),更新由宿主机上 root 运行的 `moshiro-update` 完成:它只读取节点放进 `update/` 的普通文件、复制到自己的临时目录后验签,状态写在节点只读的 `update-status/`;采集进程改写不了自己的二进制,更新器也读不到节点身份私钥。纯二进制安装的 `fleet-updater` 还只是设计。 - **容器运行**:Agent、Probe 的镜像基于 Alpine(musl,NativeAOT),宿主机用 rootful podman 加 systemd 运行:UID/GID 1654、只读文件系统、`--cap-drop all`、`no-new-privileges`、`/tmp` 用 tmpfs;配置和身份放在挂载的数据目录里。 - **最小权限运行**:非 root 用户;只在需要时授予 `CAP_NET_RAW`(ICMP);默认端口都高于 1024,不需要 `CAP_NET_BIND_SERVICE`,只有在 Agent 本地显式开启 443 时才授予。 - **身份**:节点使用本地生成的 ECDSA P-256 密钥对签名认证(Probe 的密钥由面板生成、只返回一次,同一把用来连面板和连 Agent),Backend 只存公钥,数据库泄露也无法冒充节点。 - **防放大**:未认证的包静默丢弃;回复长度与请求相等;每个流按声明速率限速;使用重放窗口。 - **容器里的主机指标**:容器里的 Agent 通过只读挂载宿主机的 `/proc`、`/sys`、根目录和 `/etc/os-release`(环境变量 `HOST_PROC`、`HOST_SYS`、`HOST_ROOT`)读取宿主机的资源占用和系统名;读不到的项返回空值,不会退回去读容器自己的数据。Probe 只需要挂载 `/etc/os-release`。 -- **自身状态**:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 `monitor.host` 上报一份主机资源占用的汇总数字(可以在 `agent.json` 里用 `reporting.host_stats = false` 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,`retention.host_days`)。 +- **自身状态**:节点上报进程内的计数(会话数、包速率、队列、丢弃数);Agent 另外每隔几秒用 `moshiro.host` 上报一份主机资源占用的汇总数字(可以在 `agent.json` 里用 `reporting.host_stats = false` 整个关掉),最新值实时推给展示页,Backend 每分钟存一个样本(默认保留 7 天,`retention.host_days`)。 ## 6. 可扩展性目标 @@ -119,7 +119,7 @@ Kind 目录编译在二进制中,节点不接受远程下发代码(安全边 ## 7. 可靠性与资源 -- Probe:每个 Agent 未交付的原始列 ≤ 8 MB;Agent:上传队列 ≤ 64 MB,满时丢弃最旧的块,并通过 `monitor.event(gap)` 上报缺口。 +- Probe:每个 Agent 未交付的原始列 ≤ 8 MB;Agent:上传队列 ≤ 64 MB,满时丢弃最旧的块,并通过 `moshiro.event(gap)` 上报缺口。 - 至少一次投递:Backend 持久化之后才回复 ACK,按 `chunk_id` 去重。 - 节点重启会丢失未上传的数据,并体现为数据缺口。 - 窗口时间以 Agent 时钟为准;时钟偏差超过 5 秒时记录告警事件。 @@ -144,7 +144,7 @@ Kind 目录编译在二进制中,节点不接受远程下发代码(安全边 | D13 | Probe/Agent 运行时只依赖 BCL;.NET 10 NativeAOT | 轻量 | | D14 | 节点机制抽成与业务无关的 Fleet 框架,放在同一仓库的 `backend/fleet/` 目录,暂不独立(由用户确认;不设拆分时间表,需要时再评估;不使用 PublicApiAnalyzers) | 可复用;`samples/EchoNode` 作为第二个使用者,防止过度设计 | | D15 | 注册独立为 `fleet-enroll` 工具;常驻进程不包含注册代码 | 常驻进程去掉 HttpClient,边界更纯 | -| D16 | 更新与业务进程分离:发布密钥签名、本地策略、健康确认与回滚。容器节点已实现(宿主机 `monitor-update`,清单里是镜像 digest);纯二进制的 `fleet-updater` 待做 | 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品 | +| D16 | 更新与业务进程分离:发布密钥签名、本地策略、健康确认与回滚。容器节点已实现(宿主机 `moshiro-update`,清单里是镜像 digest);纯二进制的 `fleet-updater` 待做 | 面板被攻破时也无法推送恶意二进制;可以同时管理多个产品 | | 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 上的真实服务冲突;结果会记录实际端口 | | D19 | 展示查询最细 1s:最近 1 小时来自内存热层,更早的数据来自 10s/1m/5m/1h 各层,现场合并;时间段内的汇总由累加器精确合并得出 | 由用户确认;5 分钟、15 分钟这类视图足够精细且快 | diff --git a/docs/coding-style.md b/docs/coding-style.md index d3a95c2..37e7049 100644 --- a/docs/coding-style.md +++ b/docs/coding-style.md @@ -5,7 +5,7 @@ ## 1. 格式与命名(`.editorconfig` 强制) - 使用 Allman 大括号(左大括号独占一行),四空格缩进,不使用 Tab;`if`/`for` 等语句必须加大括号。 -- 使用文件作用域 namespace;namespace 与目录一致(`Monitor.Agent.Sessions` 对应 `Sessions/`)。 +- 使用文件作用域 namespace;namespace 与目录一致(`MoShiro.Agent.Sessions` 对应 `Sessions/`)。 - `using` 放在文件顶部、namespace 之外,`System.*` 排在最前面,其余按字母排序。 - 始终显式写出可见性,并且放在修饰符的第一位;`static readonly` 的顺序固定。 @@ -82,8 +82,8 @@ ## 9.1 Fleet 的通用性 -- `backend/fleet/**` 不能引用任何 `Monitor.*`,也不能出现产品相关的概念、命名或默认值(例如端口 7443、`monitor` 字样),由 `scripts/check-deps` 和代码评审把关。 -- Fleet 的每个公开 API 都要在 `samples/EchoNode` 或 `EchoServer` 中有用法。新增的公开 API 如果只有 Monitor 使用,应先放在 Monitor 中,等出现第二个使用者后再下沉到 Fleet。 +- `backend/fleet/**` 不能引用任何 `MoShiro.*`,也不能出现产品相关的概念、命名或默认值(例如端口 7443、`moshiro` 字样),由 `scripts/check-deps` 和代码评审把关。 +- Fleet 的每个公开 API 都要在 `samples/EchoNode` 或 `EchoServer` 中有用法。新增的公开 API 如果只有 MoShiro 使用,应先放在 MoShiro 中,等出现第二个使用者后再下沉到 Fleet。 - Fleet 永远不提供 exec、shell、文件读写或远程下发代码的方法,这条规则不接受例外。 - 公开 API 的变更记录在 `PublicAPI.Unshipped.txt` 中;PR 描述需要说明是否属于破坏性变更。 @@ -107,5 +107,5 @@ ## 12. 提交与评审 - 提交信息使用 `<范围>: <摘要>` 格式,范围取值为 `probe`、`agent`、`backend`、`shared`、`web`、`docs`、`scripts`。 -- 修改 `backend/shared/Monitor.Protocol` 的 PR 必须说明兼容性影响(新增字段、行为变化、需要升级版本)。 +- 修改 `backend/shared/MoShiro.Protocol` 的 PR 必须说明兼容性影响(新增字段、行为变化、需要升级版本)。 - 修改 `backend/openapi/v1.json` 的 PR 必须按照 [api-guidelines.md](api-guidelines.md) 第 2 节判断是否属于破坏性变更。 diff --git a/docs/deployment.md b/docs/deployment.md index c66a03d..cbc1fb2 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -6,10 +6,10 @@ ```text scripts/publish.sh → backend/artifacts/publish/// -scripts/package.sh → backend/artifacts/packages/monitor---.tar.gz(附 .sha256) +scripts/package.sh → backend/artifacts/packages/moshiro---.tar.gz(附 .sha256) ``` -构建信息(程序里显示的 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`)。 +构建信息(程序里显示的 tag、提交、构建时间):`publish.sh` 会用 `git describe --tags --always --dirty`、`git rev-parse` 和当前时间自动设置环境变量 `MOSHIRO_TAG`、`MOSHIRO_COMMIT`、`MOSHIRO_BUILT_AT`,直接用 `dotnet publish` 或 CI 构建时自己设置它们(容器镜像的 `*.NativeBuild.Containerfile` 用同名的 `--build-arg`,CI 脚本会传);不设置就是空,本地构建照常可重复。版本号取自 `backend/Directory.Build.props` 的 `Version`。前端的信息由 `web/build-info.mjs` 在构建时写入(版本取自 `web/package.json`)。 | rid | 目标 | 构建方式 | | --- | --- | --- | @@ -24,15 +24,15 @@ macOS 的 Apple Silicon 产物带临时 ad-hoc 签名,Intel 产物未签名。 ## Backend 容器 -Backend 使用 OCI 标准镜像,通过 `registry.oeo.one/monitor/monitor-backend:` 分发,支持 `linux/amd64` 和 `linux/arm64`。容器使用 musl NativeAOT 单文件程序,基底为 `mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine`,以非 root 用户运行,在 `8080/tcp` 提供 HTTP 服务;数据库和原始数据统一写入 `/data`。程序启用 InvariantGlobalization,无需 .NET 运行时或 ICU;时间查询使用 UTC。SQLite 从固定版本的官方源码编译并静态链接,不再随镜像携带 `libe_sqlite3.so`。将 `/data` 挂载为持久卷,并在反向代理处配置 HTTPS。 +Backend 使用 OCI 标准镜像,通过 `registry.oeo.one/moshiro/moshiro-backend:` 分发,支持 `linux/amd64` 和 `linux/arm64`。容器使用 musl NativeAOT 单文件程序,基底为 `mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine`,以非 root 用户运行,在 `8080/tcp` 提供 HTTP 服务;数据库和原始数据统一写入 `/data`。程序启用 InvariantGlobalization,无需 .NET 运行时或 ICU;时间查询使用 UTC。SQLite 从固定版本的官方源码编译并静态链接,不再随镜像携带 `libe_sqlite3.so`。将 `/data` 挂载为持久卷,并在反向代理处配置 HTTPS。 -在部署目录保存此仓库的 `compose.yaml`,先通过部署平台的环境变量或密钥管理设置 `MONITOR_ADMIN_PASSWORD`(可选设置 `MONITOR_ADMIN_USER`、`MONITOR_VERSION`),然后启动: +在部署目录保存此仓库的 `compose.yaml`,先通过部署平台的环境变量或密钥管理设置 `MOSHIRO_ADMIN_PASSWORD`(可选设置 `MOSHIRO_ADMIN_USER`、`MOSHIRO_VERSION`),然后启动: ```sh docker compose up -d ``` -Compose 将容器端口只绑定到宿主机 `127.0.0.1:8080`,适合由同机 HTTPS 反向代理转发;数据保存在 Docker 命名卷 `monitor-data`。首次启动成功创建管理员后,改密应在管理端完成。 +Compose 将容器端口只绑定到宿主机 `127.0.0.1:8080`,适合由同机 HTTPS 反向代理转发;数据保存在 Docker 命名卷 `moshiro-data`。首次启动成功创建管理员后,改密应在管理端完成。 Backend 的 NativeAOT 发布默认启用 `SqliteStaticLink`,支持 Linux glibc/musl、macOS、Windows 的 x64/arm64 RID;跨平台构建使用 `-p:UseAotAnywhere=true`。SQLite 源码版本、下载年份和 SHA-256 统一配置在根目录 `Directory.Packages.props`,构建会校验下载文件并按 RID 缓存原生对象。普通 JIT 开发构建仍使用 NuGet 原生库。发布 Backend 时设置 `-p:OpenApiGenerateDocuments=false`,API 文档在普通开发构建中生成。 @@ -50,30 +50,30 @@ Pull request 和 `dev` push 会运行 Backend tests;通过后,独立的 `web ### main 开发镜像 -`main` 上只有提交信息包含 `[CI]` 的 push 才构建镜像。合并 PR 时,请把 `[CI]` 放入 PR 标题或合并提交标题。Backend、Agent、Probe 的 Linux/amd64 镜像会并行构建,并推送到同一个 Zot Registry 下独立的 `monitor-dev/` 仓库: +`main` 上只有提交信息包含 `[CI]` 的 push 才构建镜像。合并 PR 时,请把 `[CI]` 放入 PR 标题或合并提交标题。Backend、Agent、Probe 的 Linux/amd64 镜像会并行构建,并推送到同一个 Zot Registry 下独立的 `moshiro-dev/` 仓库: | 镜像 | 滚动标签 | 固定提交标签 | | --- | --- | --- | -| Backend | `registry.oeo.one/monitor-dev/monitor-backend:latest` | `registry.oeo.one/monitor-dev/monitor-backend:dev-<12位SHA>` | -| Agent | `registry.oeo.one/monitor-dev/monitor-agent:latest` | `registry.oeo.one/monitor-dev/monitor-agent:dev-<12位SHA>` | -| Probe | `registry.oeo.one/monitor-dev/monitor-probe:latest` | `registry.oeo.one/monitor-dev/monitor-probe:dev-<12位SHA>` | +| Backend | `registry.oeo.one/moshiro-dev/moshiro-backend:latest` | `registry.oeo.one/moshiro-dev/moshiro-backend:dev-<12位SHA>` | +| Agent | `registry.oeo.one/moshiro-dev/moshiro-agent:latest` | `registry.oeo.one/moshiro-dev/moshiro-agent:dev-<12位SHA>` | +| Probe | `registry.oeo.one/moshiro-dev/moshiro-probe:latest` | `registry.oeo.one/moshiro-dev/moshiro-probe:dev-<12位SHA>` | -Dev 镜像目前仅支持 amd64;Agent/Probe 在 `sdk:10.0-alpine-aot` 中构建 `linux-musl-x64` 产物,运行基底为 `runtime-deps:10.0-alpine`。它们不创建 Gitea Release,也不修改正式版 `monitor/` 仓库中的标签。普通 main push 不会构建镜像。 +Dev 镜像目前仅支持 amd64;Agent/Probe 在 `sdk:10.0-alpine-aot` 中构建 `linux-musl-x64` 产物,运行基底为 `runtime-deps:10.0-alpine`。它们不创建 Gitea Release,也不修改正式版 `moshiro/` 仓库中的标签。普通 main push 不会构建镜像。 ### 正式版发布 -推送匹配项目版本的 `vMAJOR.MINOR.PATCH` tag 会触发完整跨平台构建、正式 OCI 镜像推送和 Gitea Release。正式镜像使用 `registry.oeo.one/monitor/` 路径;Agent、Probe 提供 amd64 与 arm64 多架构标签,稳定版本更新正式仓库自己的 `latest`。这与 `monitor-dev/` 的 dev `latest` 相互独立。 +推送匹配项目版本的 `vMAJOR.MINOR.PATCH` tag 会触发完整跨平台构建、正式 OCI 镜像推送和 Gitea Release。正式镜像使用 `registry.oeo.one/moshiro/` 路径;Agent、Probe 提供 amd64 与 arm64 多架构标签,稳定版本更新正式仓库自己的 `latest`。这与 `moshiro-dev/` 的 dev `latest` 相互独立。 -Agent 和 Probe 镜像分别发布为 `registry.oeo.one/monitor/monitor-agent:` 与 `registry.oeo.one/monitor/monitor-probe:`,包含 amd64、arm64 多架构索引;也可指定 `:-amd64` 或 `:-arm64`。稳定版本同时更新 `latest`。镜像以非 root 用户(UID/GID 1654)运行,默认读取 `/etc/monitor/agent.json` 或 `/etc/monitor/probe.json`。运行容器时挂载对应配置目录;Probe 还会在该目录写入 `agents.json`,因此目录需可由 1654 用户写入。Agent、Probe 需使用宿主网络以保持节点端口和测量流量行为,并按需添加 `NET_RAW` 能力以执行 ICMP 测量: +Agent 和 Probe 镜像分别发布为 `registry.oeo.one/moshiro/moshiro-agent:` 与 `registry.oeo.one/moshiro/moshiro-probe:`,包含 amd64、arm64 多架构索引;也可指定 `:-amd64` 或 `:-arm64`。稳定版本同时更新 `latest`。镜像以非 root 用户(UID/GID 1654)运行,默认读取 `/etc/moshiro/agent.json` 或 `/etc/moshiro/probe.json`。运行容器时挂载对应配置目录;Probe 还会在该目录写入 `agents.json`,因此目录需可由 1654 用户写入。Agent、Probe 需使用宿主网络以保持节点端口和测量流量行为,并按需添加 `NET_RAW` 能力以执行 ICMP 测量: ```sh -docker run -d --name monitor-agent --restart unless-stopped --network host \ - --cap-add NET_RAW -v "$PWD/agent-config:/etc/monitor" \ - registry.oeo.one/monitor/monitor-agent: +docker run -d --name moshiro-agent --restart unless-stopped --network host \ + --cap-add NET_RAW -v "$PWD/agent-config:/etc/moshiro" \ + registry.oeo.one/moshiro/moshiro-agent: -docker run -d --name monitor-probe --restart unless-stopped --network host \ - --cap-add NET_RAW -v "$PWD/probe-config:/etc/monitor" \ - registry.oeo.one/monitor/monitor-probe: +docker run -d --name moshiro-probe --restart unless-stopped --network host \ + --cap-add NET_RAW -v "$PWD/probe-config:/etc/moshiro" \ + registry.oeo.one/moshiro/moshiro-probe: ``` 宿主机配置目录应允许 UID/GID 1654 读写;Probe 需要写权限来更新 `agents.json`。也可通过 Docker Compose 配置相同的网络、卷和能力。 @@ -86,8 +86,8 @@ Agent 和 Probe 的默认发布配置:NativeAOT、完整裁剪、`Optimization | 程序 | linux-musl-x64 | linux-x64 | win-x64 | | --- | --- | --- | --- | -| monitor-probe | 7.7 MB | 7.7 MB | 5.6 MB | -| monitor-agent | 8.4 MB | 8.4 MB | 7.3 MB | +| moshiro-probe | 7.7 MB | 7.7 MB | 5.6 MB | +| moshiro-agent | 8.4 MB | 8.4 MB | 7.3 MB | | fleet-enroll | 6.4 MB | 6.4 MB | — | Linux 上比 Windows 大,是因为 .NET 的加密、压缩、网络本机库被静态链接进来。tar.gz 压缩后 Probe 约 3.6 MB。 @@ -108,31 +108,31 @@ Linux 上比 Windows 大,是因为 .NET 的加密、压缩、网络本机库 | 位置 | 内容 | 所有者 | | --- | --- | --- | -| `/usr/lib/monitor/` | 二进制 | root,0755:采集进程不能改写自己的可执行文件 | -| `/etc/monitor/` | `agent.json` 或 `probe.json`、身份(`node.json`、`node.key`、`tls.pfx`)、`agents.json` | `monitor` 用户,0750 | +| `/usr/lib/moshiro/` | 二进制 | root,0755:采集进程不能改写自己的可执行文件 | +| `/etc/moshiro/` | `agent.json` 或 `probe.json`、身份(`node.json`、`node.key`、`tls.pfx`)、`agents.json` | `moshiro` 用户,0750 | -安装脚本会:检查 OpenSSL、创建 `monitor` 用户(`useradd`、busybox `adduser`,OpenWrt 没有这些时直接写 `/etc/passwd`)、安装二进制和服务文件;缺少配置时只安装、不启动,并打印下一步。 +安装脚本会:检查 OpenSSL、创建 `moshiro` 用户(`useradd`、busybox `adduser`,OpenWrt 没有这些时直接写 `/etc/passwd`)、安装二进制和服务文件;缺少配置时只安装、不启动,并打印下一步。 ### systemd(`packaging/systemd/*.service`) -以 `monitor` 用户运行,沙箱:`NoNewPrivileges`、`ProtectSystem=strict`、`ProtectHome`、`PrivateTmp/Devices`、`ProtectKernel*`、`RestrictAddressFamilies=AF_INET AF_INET6`、`RestrictNamespaces`、`MemoryDenyWriteExecute`(NativeAOT 不 JIT)、`SystemCallArchitectures=native`,`MemoryMax`(Agent 128 MB、Probe 64 MB)。 +以 `moshiro` 用户运行,沙箱:`NoNewPrivileges`、`ProtectSystem=strict`、`ProtectHome`、`PrivateTmp/Devices`、`ProtectKernel*`、`RestrictAddressFamilies=AF_INET AF_INET6`、`RestrictNamespaces`、`MemoryDenyWriteExecute`(NativeAOT 不 JIT)、`SystemCallArchitectures=native`,`MemoryMax`(Agent 128 MB、Probe 64 MB)。 -- **Agent** 只保留 `CAP_NET_RAW`(ICMP 目标任务用),`/etc/monitor` 只读;Probe 没有任何 capability,只对 `agents.json` 所在的目录可写。 +- **Agent** 只保留 `CAP_NET_RAW`(ICMP 目标任务用),`/etc/moshiro` 只读;Probe 没有任何 capability,只对 `agents.json` 所在的目录可写。 - 监听 1024 以下的端口(例如把数据面放到 443)需要在 drop-in 里加 `AmbientCapabilities=CAP_NET_BIND_SERVICE` 和对应的 `CapabilityBoundingSet`。默认端口是 7443/tcp+udp(Probe 控制通道和 UDP 数据面)、7444/tcp。 ### OpenWrt(`packaging/openwrt/`) -只支持 Probe(Agent 面向服务器)。`install.sh probe` 安装 `/etc/init.d/monitor-probe`(procd,崩溃自动重启,日志进 logd)并写入 `/lib/upgrade/keep.d/monitor`,sysupgrade 时保留 `/etc/monitor/`。 +只支持 Probe(Agent 面向服务器)。`install.sh probe` 安装 `/etc/init.d/moshiro-probe`(procd,崩溃自动重启,日志进 logd)并写入 `/lib/upgrade/keep.d/moshiro`,sysupgrade 时保留 `/etc/moshiro/`。 OpenWrt 默认不带 OpenSSL:`opkg install libopenssl3`(较新的快照和 24.10 之后用 `apk add libopenssl3`)。闪存很小时注意:二进制 7.7 MB,配置只在 Agent 列表变化时才写。路由器上建议把 `probe.json` 的 `limits.max_test_mbps` 调低。 ## 3. 验证 -`scripts/smoke-linux.sh` 在 Linux 容器里用发布的二进制跑完整链路(后端 → 注册 → Agent → Probe → 数据落库),`MODE=install` 用打包产物和 `install.sh` 安装、以 `monitor` 用户运行。`scripts/smoke-systemd.sh` 在 systemd 容器里验证服务单元。已经验证过: +`scripts/smoke-linux.sh` 在 Linux 容器里用发布的二进制跑完整链路(后端 → 注册 → Agent → Probe → 数据落库),`MODE=install` 用打包产物和 `install.sh` 安装、以 `moshiro` 用户运行。`scripts/smoke-systemd.sh` 在 systemd 容器里验证服务单元。已经验证过: - Alpine 3.22(musl)、Debian 12(glibc):直接运行和 `install.sh` 安装都通; - OpenWrt(官方 rootfs,busybox,无 `install`/`useradd`/`su`):安装、建用户、卸载、二进制启动和 OpenSSL 自检; -- systemd(Debian 12):两个服务 active,Agent 只有 `CAP_NET_RAW`、Probe 没有 capability,`NoNewPrivs`、seccomp 生效,Agent 无法写 `/etc/monitor`,没有重启和错误日志。 +- systemd(Debian 12):两个服务 active,Agent 只有 `CAP_NET_RAW`、Probe 没有 capability,`NoNewPrivs`、seccomp 生效,Agent 无法写 `/etc/moshiro`,没有重启和错误日志。 没有验证的:arm64 构建、真正的 OpenWrt 设备(有 procd 和真实的内存/闪存限制)、跨公网的真实线路。 @@ -172,20 +172,20 @@ OpenWrt 默认不带 OpenSSL:`opkg install libopenssl3`(较新的快照和 2 - **Backend 通过隧道暴露**:隧道把一个公网域名转到家宽机器的 `29181`;Backend 监听 `http://0.0.0.0:29181`(只写一个监听地址,同时写 `0.0.0.0` 和 `[::]` 会报地址已被占用),家宽路由器的防火墙不放行这个端口,公网只能经隧道进来。Agent 用 `fleet-enroll --server https://<公网域名>` 注册,之后用 WSS 连回 Backend。 - **经隧道注册之后,检查 Agent 的 `public_host`**:Backend 看到的来源地址是隧道的出口,不是 VPS 的地址,要改成 VPS 的真实地址,Probe 才连得上。 -- **Probe 直连面板**:`probe.json` 里的 `server` 就是面板地址(取自 `Monitor__PublicUrl`,没配就用创建它时的请求地址),Probe 用它连面板,Agent 列表由面板下发;先注册还是先创建 Probe 都可以。 +- **Probe 直连面板**:`probe.json` 里的 `server` 就是面板地址(取自 `MoShiro__PublicUrl`,没配就用创建它时的请求地址),Probe 用它连面板,Agent 列表由面板下发;先注册还是先创建 Probe 都可以。 - **小 VPS 把 `limits.max_total_mbps` 调低**(试跑时用 100);Agent 常驻约 23 MB,VPS 上原有的服务不受影响,结束后删除 `/opt/…`、停掉进程即可,没有留下任何东西。 - 家宽的 IPv6 前缀会在路由器重新拨号(比如停电)后变化,SSH 地址要跟着换;面板走隧道的域名,不受影响。 - 本地电脑没有直连 VPS 的路径时,用家宽机器当跳板(`SSH_ASKPASS` + `SSH_ASKPASS_REQUIRE=force`,密码放环境变量,不写进文件)。 **常驻(开机自启)**: -- 家宽机器(没有 root 时):`~/.config/systemd/user/monitor-backend.service` 和 `monitor-probe.service`,`EnvironmentFile` 指向放配置的 `backend.env`,`Restart=always`,`systemctl --user enable --now …`,再用 `sudo loginctl enable-linger <用户>` 让它们不登录也能起来。Probe 的命令行是 `monitor-probe --config `(默认路径是 `/etc/monitor/probe.json`)。 -- VPS:`/etc/systemd/system/monitor-agent.service`,`Restart=always`,加 `NoNewPrivileges`、`ProtectSystem=strict`、`ReadWritePaths=/opt/monitor`、`ProtectHome`、`PrivateTmp`。这是不走安装脚本的手动方式;正式安装用上面的 `packaging/systemd`。 -- 日志进 journal:`journalctl -u monitor-agent`、`journalctl --user -u monitor-probe`。重启 Backend 用 `systemctl` 而不是手动再起一个进程,否则会端口冲突。 +- 家宽机器(没有 root 时):`~/.config/systemd/user/moshiro-backend.service` 和 `moshiro-probe.service`,`EnvironmentFile` 指向放配置的 `backend.env`,`Restart=always`,`systemctl --user enable --now …`,再用 `sudo loginctl enable-linger <用户>` 让它们不登录也能起来。Probe 的命令行是 `moshiro-probe --config `(默认路径是 `/etc/moshiro/probe.json`)。 +- VPS:`/etc/systemd/system/moshiro-agent.service`,`Restart=always`,加 `NoNewPrivileges`、`ProtectSystem=strict`、`ReadWritePaths=/opt/moshiro`、`ProtectHome`、`PrivateTmp`。这是不走安装脚本的手动方式;正式安装用上面的 `packaging/systemd`。 +- 日志进 journal:`journalctl -u moshiro-agent`、`journalctl --user -u moshiro-probe`。重启 Backend 用 `systemctl` 而不是手动再起一个进程,否则会端口冲突。 -**国家和运营商**:Backend 用 Probe、Agent 的来源地址向 IP 归属服务查一次,展示在国旗和运营商图标上(`Monitor__GeoLookupUrl`,缺省 `https://ip.007995.xyz`;留空就不查;这个服务只供公益使用,署名和使用条款由部署的人自己遵守)。Probe、Agent 自己不访问外部服务,所以客户的防火墙审计里不会多出出站连接;Backend 发出去的只有公网 IP(内网地址不发)。 +**国家和运营商**:Backend 用 Probe、Agent 的来源地址向 IP 归属服务查一次,展示在国旗和运营商图标上(`MoShiro__GeoLookupUrl`,缺省 `https://ip.007995.xyz`;留空就不查;这个服务只供公益使用,署名和使用条款由部署的人自己遵守)。Probe、Agent 自己不访问外部服务,所以客户的防火墙审计里不会多出出站连接;Backend 发出去的只有公网 IP(内网地址不发)。 -**从别处看这个面板**:本机跑前端,在页面里把数据源指向这个域名,用 API 令牌访问,Backend 设 `Monitor__CorsOrigins` 放行前端的来源(见 [web/README.md](../web/README.md))。 +**从别处看这个面板**:本机跑前端,在页面里把数据源指向这个域名,用 API 令牌访问,Backend 设 `MoShiro__CorsOrigins` 放行前端的来源(见 [web/README.md](../web/README.md))。 实测(Probe 在广东移动家宽,Agent 在日本 IIJ): @@ -203,24 +203,24 @@ OpenWrt 默认不带 OpenSSL:`opkg install libopenssl3`(较新的快照和 2 ## 容器部署(Docker / Podman 共用命名 volume) -镜像和 Compose 配置共用。普通用户执行 Podman 即为 rootless,使用命名 volume 不需要 `keep-id` 或手动 `chown`。Agent 的配置、原有身份和缓存保存在 `monitor-agent-config`,Probe 保存在 `monitor-probe-config`,更新状态使用独立的 `monitor--status` volume,在业务容器内只读。不会重新生成已有身份。更新/重建容器不删除 volume;不要使用 `compose down -v`,该参数会删除数据。 +镜像和 Compose 配置共用。普通用户执行 Podman 即为 rootless,使用命名 volume 不需要 `keep-id` 或手动 `chown`。Agent 的配置、原有身份和缓存保存在 `moshiro-agent-config`,Probe 保存在 `moshiro-probe-config`,更新状态使用独立的 `moshiro--status` volume,在业务容器内只读。不会重新生成已有身份。更新/重建容器不删除 volume;不要使用 `compose down -v`,该参数会删除数据。 在 `packaging/compose/agent` 下首次注册,并写配置(下列 `podman` 可替换为 `docker`): ```sh podman compose run --rm --entrypoint /usr/local/bin/fleet-enroll agent \ - --server https:// --join --name --identity-dir /etc/monitor/agent-id -podman compose run --rm -T --entrypoint /bin/sh agent -c 'cat > /etc/monitor/agent.json' <<'JSON' -{"fleet":{"server":"https://","identity_dir":"/etc/monitor/agent-id"},"listen":{"bind":"0.0.0.0"}} + --server https:// --join --name --identity-dir /etc/moshiro/agent-id +podman compose run --rm -T --entrypoint /bin/sh agent -c 'cat > /etc/moshiro/agent.json' <<'JSON' +{"fleet":{"server":"https://","identity_dir":"/etc/moshiro/agent-id"},"listen":{"bind":"0.0.0.0"}} JSON podman compose up -d ``` -Probe 在 `packaging/compose/probe` 下,将面板下载的身份文件通过标准输入导入 volume:`podman compose run --rm -T --entrypoint /bin/sh probe -c 'umask 077; cat > /etc/monitor/probe.json' < probe.json`,再 `podman compose up -d`。私钥权限为 600。已有部署迁移时,应把原配置与身份原样复制进 volume,不重新注册或轮换密钥。Docker 与 Podman 各自管理 volume,切换引擎不会自动共享数据。 +Probe 在 `packaging/compose/probe` 下,将面板下载的身份文件通过标准输入导入 volume:`podman compose run --rm -T --entrypoint /bin/sh probe -c 'umask 077; cat > /etc/moshiro/probe.json' < probe.json`,再 `podman compose up -d`。私钥权限为 600。已有部署迁移时,应把原配置与身份原样复制进 volume,不重新注册或轮换密钥。Docker 与 Podman 各自管理 volume,切换引擎不会自动共享数据。 -纯 Compose 使用 `compose pull && compose up -d` 更新镜像;面板自动更新使用下述 systemd 单元和更新器。Rootless Podman 使用 `monitor--rootless.service`,安装到 `~/.config/systemd/user/`,镜像文件为 `~/.config/monitor/.image`;rootful Podman 使用 `monitor--container.service`,Docker 使用 `monitor--docker.service`,安装到 `/etc/systemd/system/`,镜像文件为 `/etc/monitor-update/.image`。所有单元均使用相同的配置和状态 volume 名称。自动启动分别使用 `systemctl --user enable --now ` 或 `sudo systemctl enable --now `。Rootless 在退出登录后继续运行需管理员执行一次 `loginctl enable-linger <用户名>`。 +纯 Compose 使用 `compose pull && compose up -d` 更新镜像;面板自动更新使用下述 systemd 单元和更新器。Rootless Podman 使用 `moshiro--rootless.service`,安装到 `~/.config/systemd/user/`,镜像文件为 `~/.config/moshiro/.image`;rootful Podman 使用 `moshiro--container.service`,Docker 使用 `moshiro--docker.service`,安装到 `/etc/systemd/system/`,镜像文件为 `/etc/moshiro-update/.image`。所有单元均使用相同的配置和状态 volume 名称。自动启动分别使用 `systemctl --user enable --now ` 或 `sudo systemctl enable --now `。Rootless 在退出登录后继续运行需管理员执行一次 `loginctl enable-linger <用户名>`。 -Backend 的 `packaging/compose/backend/compose.yaml` 使用 `monitor-backend-data` 保存数据库和原始数据,设置 `MONITOR_INITIAL_ADMIN_PASSWORD` 后即可启动。首次安装 Podman 与用户 subuid/subgid 映射需要管理员权限,之后普通用户运行无需 sudo。 +Backend 的 `packaging/compose/backend/compose.yaml` 使用 `moshiro-backend-data` 保存数据库和原始数据,设置 `MOSHIRO_INITIAL_ADMIN_PASSWORD` 后即可启动。首次安装 Podman 与用户 subuid/subgid 映射需要管理员权限,之后普通用户运行无需 sudo。 ### Agent 的系统指标挂载 @@ -247,16 +247,16 @@ Rootless Podman 普通用户安装示例(先将对应 rootless 服务单元复 ```sh sh packaging/update/install-updater.sh agent --user \ - --volume monitor-agent-config --status-volume monitor-agent-status \ - --helper-image registry.oeo.one/monitor/monitor-agent: \ - --image-repo registry.oeo.one/monitor/monitor-agent \ - --unit monitor-agent-rootless.service --policy auto + --volume moshiro-agent-config --status-volume moshiro-agent-status \ + --helper-image registry.oeo.one/moshiro/moshiro-agent: \ + --image-repo registry.oeo.one/moshiro/moshiro-agent \ + --unit moshiro-agent-rootless.service --policy auto ``` -使用 Docker 时以管理员运行相同安装命令,去掉 `--user`,加 `--engine docker`,单元选择 `monitor-agent-docker.service`。rootful Podman 去掉 `--user`,单元选择 `monitor-agent-container.service`。Probe 替换角色名。初始化对应 `.image` 为 `IMAGE=<当前镜像引用>` 后启动容器单元。辅助镜像可用本机已有的 digest 引用,避免标签变化。Docker rootless 更新尚未适配。 +使用 Docker 时以管理员运行相同安装命令,去掉 `--user`,加 `--engine docker`,单元选择 `moshiro-agent-docker.service`。rootful Podman 去掉 `--user`,单元选择 `moshiro-agent-container.service`。Probe 替换角色名。初始化对应 `.image` 为 `IMAGE=<当前镜像引用>` 后启动容器单元。辅助镜像可用本机已有的 digest 引用,避免标签变化。Docker rootless 更新尚未适配。 命名 volume 的更新服务由 systemd timer 每 30 秒检查一次。`--policy auto` 验签后自动应用,默认 `manual` 等待面板写入应用请求。清单必须通过 ECDSA P-256 签名校验,组件、镜像仓库和递增 seq 都必须匹配本机配置;镜像按签名清单中的 digest 拉取。更新后观察服务 20 秒,启动失败或崩溃循环会恢复旧镜像引用并重启。无效签名、重放清单、拉取失败不会切换当前镜像。同一组件的更新通过 flock 串行处理。 -更新器依赖 Linux、systemd、OpenSSL、util-linux/flock 和所选引擎。用户部署把配置、公钥和镜像引用放在 `~/.config/monitor/`、版本记录放在 `~/.local/state/monitor-update/`;系统部署使用 `/etc/monitor-update/` 和 `/var/lib/monitor-update/`。私有仓库的 `--authfile` 对 Podman 使用其认证文件,对 Docker 使用标准 `config.json`,不改写全局 Docker 配置。旧 `--data-dir` 安装方式保留兼容,但新部署统一使用 `--volume`。 +更新器依赖 Linux、systemd、OpenSSL、util-linux/flock 和所选引擎。用户部署把配置、公钥和镜像引用放在 `~/.config/moshiro/`、版本记录放在 `~/.local/state/moshiro-update/`;系统部署使用 `/etc/moshiro-update/` 和 `/var/lib/moshiro-update/`。私有仓库的 `--authfile` 对 Podman 使用其认证文件,对 Docker 使用标准 `config.json`,不改写全局 Docker 配置。旧 `--data-dir` 安装方式保留兼容,但新部署统一使用 `--volume`。 发布私钥只放在 Woodpecker 的 `release_signing_key` secret;发布公钥位于 `packaging/update/release-key.pub.pem`。CI 的 `release_api_token` 只有发布登记权限,`RELEASE_API_URL` 指向面板。面板保存并转发签名清单,Agent/Probe 将它与更新请求写进配置 volume;更新器验签后才调用容器引擎和 systemd。管理端仍能看到可更新、更新中、已回滚和无效清单状态。纯二进制更新器尚未实现。 diff --git a/docs/enrollment.md b/docs/enrollment.md index 1338b2c..b03e4e6 100644 --- a/docs/enrollment.md +++ b/docs/enrollment.md @@ -1,6 +1,6 @@ -# Monitor 的注册与身份 +# MoShiro 的注册与身份 -通用的注册、身份、认证机制由 Fleet 提供,见 [fleet/link.md](fleet/link.md) 第 2~3 节。本文只描述 **Monitor 在 Fleet 之上的约定**:Agent 注册后如何自动接入测量、Probe 的身份以及 Agent 列表同步,以及节点本地的硬上限。 +通用的注册、身份、认证机制由 Fleet 提供,见 [fleet/link.md](fleet/link.md) 第 2~3 节。本文只描述 **MoShiro 在 Fleet 之上的约定**:Agent 注册后如何自动接入测量、Probe 的身份以及 Agent 列表同步,以及节点本地的硬上限。 ## 1. 身份一览 @@ -10,7 +10,7 @@ | Probe | Fleet 节点身份(同时用于连面板和连 Agent) | 面板生成 `probe.json`(可以有多个 Probe,每个独立吊销) | 公钥 | | 面板用户 | 账号 + 密码(PBKDF2-SHA256) | 面板 | 密码哈希 | | API 令牌 | 随机高熵令牌,作用域为 `read` 或 `admin` | 面板 | SHA-256 | -| 发布签名 | ECDSA P-256 发布密钥 | CI(Woodpecker 密钥 `release_signing_key`)或离线环境 | 只在节点宿主机的更新器里配置公钥(容器节点是 `monitor-update`,见 [deployment.md](deployment.md);纯二进制方案的 `fleet-updater` 还只是设计),**面板不保存私钥,也不验签** | +| 发布签名 | ECDSA P-256 发布密钥 | CI(Woodpecker 密钥 `release_signing_key`)或离线环境 | 只在节点宿主机的更新器里配置公钥(容器节点是 `moshiro-update`,见 [deployment.md](deployment.md);纯二进制方案的 `fleet-updater` 还只是设计),**面板不保存私钥,也不验签** | ## 2. Agent 接入流程 @@ -20,14 +20,14 @@ curl -fsSL https://panel/install.sh | sh -s -- --join jt_xxx --name jp-1 install.sh(只做安装,不包含任何远程控制功能;下面的更新器部分是纯二进制方案的设计,还没有实现,容器节点的做法见 deployment.md) - ├─ 下载 monitor-agent、fleet-enroll、fleet-updater,并验证发布签名 - ├─ 创建系统用户:monitor(运行 Agent)、fleet-update(运行更新器) - ├─ fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/monitor - │ └─ 注册元数据(Monitor 定义):{ probe_tcp_port, probe_udp_ports, public_host?, kinds[] } + ├─ 下载 moshiro-agent、fleet-enroll、fleet-updater,并验证发布签名 + ├─ 创建系统用户:moshiro(运行 Agent)、fleet-update(运行更新器) + ├─ fleet-enroll --server https://panel --join jt_xxx --name jp-1 --identity-dir /etc/moshiro + │ └─ 注册元数据(MoShiro 定义):{ probe_tcp_port, probe_udp_ports, public_host?, kinds[] } ├─ 写入 agent.json(本地硬上限)和 /etc/fleet/updater.json - └─ 启用 monitor-agent.service 和 fleet-updater.service + └─ 启用 moshiro-agent.service 和 fleet-updater.service -面板收到 OnEnrolled(Monitor 挂钩) +面板收到 OnEnrolled(MoShiro 挂钩) ├─ public_host 未提供时,取注册请求的来源 IP ├─ 按“线路任务模板”为该 Agent 计算有效任务集(TaskSet) └─ 面板把新的 Agent 列表作为期望状态推给所有 Probe → Probe 自动连接新 Agent @@ -58,11 +58,11 @@ Probe 启动 - **哪些 Probe 连哪些 Agent 由面板决定,始终是 Probe 主动拨出**:设置 `fleet.auto_link`(默认开)打开时,所有未吊销的 Probe 和所有 Agent 两两连;关闭时只连手动选中的。每一对还可以在管理端 Probe 页的「线路」矩阵里手动强制开或关(`PUT /api/admin/links/{probe_id}/{agent_id}`),手动设置优先于自动连接。结果同时用在 Probe 的 Agent 列表、Agent 的 `peers` 和线路目录(关掉的线路从总览里消失,历史数据还在)。 - 吊销 Probe:在面板上重新生成身份,旧公钥立即从所有 Agent 的期望状态中移除,旧会话随之断开。 -## 4. Agent 本地配置(`/etc/monitor/agent.json`,面板无法修改) +## 4. Agent 本地配置(`/etc/moshiro/agent.json`,面板无法修改) ```json { - "fleet": { "server": "https://panel.example.com", "identity_dir": "/etc/monitor" }, + "fleet": { "server": "https://panel.example.com", "identity_dir": "/etc/moshiro" }, "listen": { "control_port": 7443, "udp_ports": [7443], "tcp_ports": [7444] }, "limits": { "max_total_mbps": 200, @@ -70,7 +70,7 @@ Probe 启动 "allowed_target_cidrs": [], "max_concurrent_tests": 1 }, - "fleet_policy": { "allow_methods": ["monitor.test.run", "monitor.test.cancel"] } + "fleet_policy": { "allow_methods": ["moshiro.test.run", "moshiro.test.cancel"] } } ``` @@ -101,4 +101,4 @@ Probe 启动 { "reporting": { "host_stats": true, "host_interval_s": 2 } } ``` -`host_stats` 为 true(默认)时,Agent 每隔 `host_interval_s` 秒用 `monitor.host` 上报一份主机资源占用的汇总数字(CPU、内存、根分区、网卡累计流量与网速、运行时长;没有型号、主机名、IP);设为 false 就一个字也不上报。 +`host_stats` 为 true(默认)时,Agent 每隔 `host_interval_s` 秒用 `moshiro.host` 上报一份主机资源占用的汇总数字(CPU、内存、根分区、网卡累计流量与网速、运行时长;没有型号、主机名、IP);设为 false 就一个字也不上报。 diff --git a/docs/fleet/link.md b/docs/fleet/link.md index 6238f40..735cbf2 100644 --- a/docs/fleet/link.md +++ b/docs/fleet/link.md @@ -49,7 +49,7 @@ ## 4. RPC(Fleet.Rpc) -- 信封采用 JSON-RPC 2.0;方法名为 `领域.动作`。`fleet.*` 保留给框架,产品使用自己的前缀,例如 `monitor.*`。 +- 信封采用 JSON-RPC 2.0;方法名为 `领域.动作`。`fleet.*` 保留给框架,产品使用自己的前缀,例如 `moshiro.*`。 - 二进制帧:`u8 channel | 正文`。channel `0` 到 `15` 保留给框架,产品从 `16` 开始注册自己的二进制通道。 - 错误:`error.data.code` 为 snake_case 字符串,框架的错误码以 `fleet_` 开头,产品可以扩展。 - **与传输方式无关**:同一套路由既可以跑在 wss 上,也可以跑在 TLS 帧(`u32 长度 | u8 类型 | 正文`)上。 @@ -86,7 +86,7 @@ connect(wss) → fleet.challenge → fleet.auth → fleet.hello → 状态同步 - 投递语义为**至少一次**:每条消息都带有 `msg_id`(UUIDv7),服务端按此去重。 - 在途数据按字节限制(默认 4 MiB),保证服务端慢时不会把内存撑爆。 - 丢弃消息时会累计缺口计数,通过 `OnGap` 回调通知产品,由产品决定如何上报。 -- 默认只保存在内存中,不落盘。产品如果需要持久化,可以自行实现 `IOutboxStore`(Monitor 不使用)。 +- 默认只保存在内存中,不落盘。产品如果需要持久化,可以自行实现 `IOutboxStore`(MoShiro 不使用)。 ## 7. 期望状态同步(State Sync) @@ -134,4 +134,4 @@ public interface IFleetStore } ``` -产品可以使用 `Fleet.Server.Sqlite`,也可以把这些表放进自己的数据库中。Monitor 的做法是把它们放进自己的 `monitor.db`,并由同一个单写入者负责写入。 +产品可以使用 `Fleet.Server.Sqlite`,也可以把这些表放进自己的数据库中。MoShiro 的做法是把它们放进自己的 `moshiro.db`,并由同一个单写入者负责写入。 diff --git a/docs/fleet/overview.md b/docs/fleet/overview.md index ff8ab79..1580fed 100644 --- a/docs/fleet/overview.md +++ b/docs/fleet/overview.md @@ -1,8 +1,8 @@ # Fleet:通用节点框架 -> **实现状态**:`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计;容器节点的更新目前由宿主机上的 `monitor-update` 脚本和 Monitor 的 `ReleaseService` 完成(见 [deployment.md](../deployment.md))。其余组件已实现。 +> **实现状态**:`Fleet.Update`、`Fleet.Server.Releases`、`fleet-updater`、`fleet-release` 还只是设计;容器节点的更新目前由宿主机上的 `moshiro-update` 脚本和 MoShiro 的 `ReleaseService` 完成(见 [deployment.md](../deployment.md))。其余组件已实现。 -> 名称暂定。Fleet 与业务无关,Monitor 是它的第一个使用者。 +> 名称暂定。Fleet 与业务无关,MoShiro 是它的第一个使用者。 ## 1. 定位 @@ -10,7 +10,7 @@ ```text ┌──────────────────────────── 产品层(各产品自己写) ─────────────────────────────┐ -│ Monitor: Protocol(Kind/任务) · Measurement(积木) · Agent · Probe · Backend │ +│ MoShiro: Protocol(Kind/任务) · Measurement(积木) · Agent · Probe · Backend │ │ 其他产品: YourApp.Node · YourApp.Server │ └──────────────────────────────────┬─────────────────────────────────────────────┘ │ 只依赖 Fleet;Fleet 不认识任何产品 @@ -29,14 +29,14 @@ ## 2. 设计原则 -1. **与业务无关**:Fleet 中不出现任何产品概念。产品通过扩展点接入:元数据类型、RPC 方法、发件箱消息类型、期望状态文档类型。`scripts/check-deps` 强制规定 `backend/fleet/**` 不能引用 `Monitor.*`。 +1. **与业务无关**:Fleet 中不出现任何产品概念。产品通过扩展点接入:元数据类型、RPC 方法、发件箱消息类型、期望状态文档类型。`scripts/check-deps` 强制规定 `backend/fleet/**` 不能引用 `MoShiro.*`。 2. **安全默认值**: - Fleet **永远不提供** exec、shell、文件读写、远程下发代码这类方法; - 节点只响应本地注册过的方法,本地策略 `allow_methods` 还可以进一步收窄; - 更新必须通过离线密钥签名验证,面板即使被攻破也推不出恶意二进制。 3. **权限分离**:注册(`fleet-enroll`,一次性运行)、常驻业务进程、更新(`fleet-updater`)是三个独立的进程,使用不同的系统用户,各自只拥有最小权限。 4. **NativeAOT 优先**:节点侧只依赖 BCL;泛型扩展点通过产品传入的 `JsonTypeInfo` 实现序列化,不使用反射。 -5. **只抽取真正用到的**:每项能力都必须同时被 Monitor 和 `samples/EchoNode` 使用,防止过度设计。 +5. **只抽取真正用到的**:每项能力都必须同时被 MoShiro 和 `samples/EchoNode` 使用,防止过度设计。 ## 3. 组件 @@ -67,15 +67,15 @@ fleet/ 以后可以原样拆成独立仓库并发 │ ├─ Fleet.Enrollment.Client/ │ └─ Fleet.Server.Enrollment/ Fleet.Server.Link/ Fleet.Server.Releases/ Fleet.Server.Sqlite/ ├─ tools/ fleet-enroll/ fleet-updater/ fleet-release/ -├─ samples/ EchoNode/ EchoServer/ 最小示例:不依赖 Monitor 的第二个使用者 +├─ samples/ EchoNode/ EchoServer/ 最小示例:不依赖 MoShiro 的第二个使用者 ├─ tests/ └─ docs → 仓库 docs/fleet/ ``` ## 5. 演进策略 -- **现阶段**(已确认,暂不独立):放在 monitor 仓库的 `backend/fleet/` 目录中,通过项目引用使用,一处修改两边同时编译。 -- **拆分条件**:公开 API 连续两个里程碑没有破坏性变更,并且 `samples` 能覆盖全部能力。满足后拆成独立仓库,按 semver 发布 NuGet 包,Monitor 改为包引用。 +- **现阶段**(已确认,暂不独立):放在 MoShiro 仓库的 `backend/fleet/` 目录中,通过项目引用使用,一处修改两边同时编译。 +- **拆分条件**:公开 API 连续两个里程碑没有破坏性变更,并且 `samples` 能覆盖全部能力。满足后拆成独立仓库,按 semver 发布 NuGet 包,MoShiro 改为包引用。 - **公开 API 审查**:`backend/fleet/src/*` 的 public 类型使用 `PublicAPI.Shipped.txt` / `PublicAPI.Unshipped.txt` 跟踪(使用 `Microsoft.CodeAnalysis.PublicApiAnalyzers`,只在构建期生效),任何变更都会在 PR 中显示出来。 ## 6. 产品接入一览 diff --git a/docs/fleet/update.md b/docs/fleet/update.md index 58311b8..f379c5e 100644 --- a/docs/fleet/update.md +++ b/docs/fleet/update.md @@ -1,6 +1,6 @@ # Fleet:通用更新(Fleet.Update / fleet-updater) -> **实现状态**:容器节点(Agent、Probe)的更新已经实现,见 [deployment.md](../deployment.md) 的「容器节点的更新」:同样的信任模型(离线发布密钥签名、本机配置公钥和仓库、`seq` 防重放、面板只通知和请求),清单里是镜像 digest,更新器是宿主机上的 `monitor-update`。本文下面描述的纯二进制方案(`Fleet.Update`、`fleet-updater`、`fleet-release`、健康信标)还没有实现。 +> **实现状态**:容器节点(Agent、Probe)的更新已经实现,见 [deployment.md](../deployment.md) 的「容器节点的更新」:同样的信任模型(离线发布密钥签名、本机配置公钥和仓库、`seq` 防重放、面板只通知和请求),清单里是镜像 digest,更新器是宿主机上的 `moshiro-update`。本文下面描述的纯二进制方案(`Fleet.Update`、`fleet-updater`、`fleet-release`、健康信标)还没有实现。 ## 1. 原则 @@ -30,15 +30,15 @@ "check_interval_s": 3600, "apps": [ { - "id": "monitor-agent", - "sources": ["https://panel.example.com/fleet/releases", "https://github.com/org/monitor/releases/download"], + "id": "moshiro-agent", + "sources": ["https://panel.example.com/fleet/releases", "https://github.com/org/moshiro/releases/download"], "channel": "stable", "policy": "notify", "rid": "linux-x64", - "install_dir": "/opt/monitor-agent", + "install_dir": "/opt/moshiro-agent", "keep_versions": 2, - "service": { "kind": "systemd", "name": "monitor-agent" }, - "health": { "kind": "beacon", "path": "/run/fleet/monitor-agent.beacon", "timeout_s": 60 } + "service": { "kind": "systemd", "name": "moshiro-agent" }, + "health": { "kind": "beacon", "path": "/run/fleet/moshiro-agent.beacon", "timeout_s": 60 } } ] } @@ -53,16 +53,16 @@ ```json { "schema": 1, - "app": "monitor-agent", + "app": "moshiro-agent", "channel": "stable", "version": "0.4.2", "released_at": "2026-10-01T00:00:00Z", "min_from": "0.3.0", "artifacts": [ - { "rid": "linux-x64", "url": "monitor-agent-0.4.2-linux-x64.tar.gz", "sha256": "…", "size": 4120334 }, - { "rid": "linux-arm64", "url": "monitor-agent-0.4.2-linux-arm64.tar.gz", "sha256": "…", "size": 3987112 }, - { "rid": "linux-musl-arm64", "url": "monitor-agent-0.4.2-linux-musl-arm64.tar.gz", "sha256": "…", "size": 3990021 }, - { "rid": "win-x64", "url": "monitor-agent-0.4.2-win-x64.zip", "sha256": "…", "size": 4455010 } + { "rid": "linux-x64", "url": "moshiro-agent-0.4.2-linux-x64.tar.gz", "sha256": "…", "size": 4120334 }, + { "rid": "linux-arm64", "url": "moshiro-agent-0.4.2-linux-arm64.tar.gz", "sha256": "…", "size": 3987112 }, + { "rid": "linux-musl-arm64", "url": "moshiro-agent-0.4.2-linux-musl-arm64.tar.gz", "sha256": "…", "size": 3990021 }, + { "rid": "win-x64", "url": "moshiro-agent-0.4.2-win-x64.zip", "sha256": "…", "size": 4455010 } ], "notes_url": "https://…/CHANGELOG#0.4.2", "key_id": "rel-2026a", diff --git a/docs/measurement.md b/docs/measurement.md index 541d903..f8f7cd6 100644 --- a/docs/measurement.md +++ b/docs/measurement.md @@ -3,7 +3,7 @@ ## 1. 原则 - **只采集原始数据**:逐包、逐消息、逐事件,TCP 另加周期性的内核采样;不计算任何统计,解释全部放在 [analysis.md](analysis.md)。 -- **一处声明,处处生效**:采集方法(Kind)由积木组合**声明**而成。Probe 和 Agent 共用同一套积木(`backend/shared/Monitor.Measurement`)。 +- **一处声明,处处生效**:采集方法(Kind)由积木组合**声明**而成。Probe 和 Agent 共用同一套积木(`backend/shared/MoShiro.Measurement`)。 - **密度即参数**:频率、包大小、采样间隔全部是任务参数,由 API 下发,不需要改代码。 ## 2. 任务模型 @@ -26,7 +26,7 @@ schedule continuous 持续运行,按时间切块 ## 3. Kind 目录:积木组合 -### 3.1 积木(`Monitor.Measurement`,只有新增积木才需要写代码) +### 3.1 积木(`MoShiro.Measurement`,只有新增积木才需要写代码) | 类别 | 积木 | 参数示例 | | --- | --- | --- | @@ -39,7 +39,7 @@ schedule continuous 持续运行,按时间切块 **两种角色互相独立**:连接角色(Probe 永远是拨号方,适配 NAT)与数据角色(发送端、接收端、回显端,由 `direction` 决定)无关。每种积木只实现一次,Probe 和 Agent 共用。 -### 3.2 Kind 声明(`Monitor.Protocol/Catalog/Kinds`) +### 3.2 Kind 声明(`MoShiro.Protocol/Catalog/Kinds`) ```csharp public static readonly KindSpec TcpEcho = new( @@ -109,7 +109,7 @@ RawChunk - **列定义写在块头中**:新增列或新增 Kind 时,**不需要升级 `container_version`**。只有容器格式本身变化时才升级。Backend 可以通用地解码任何块,不认识的列会被保留,但会被分析层忽略。 - **切块规则**:持续任务每 **10 秒**切一块,或者记录数达到上限(默认 50 000)时提前切块。这样采集密度提高后,单块大小仍然有界,实时显示和持久化也共用同一条链路。 - 按需测试按阶段切块,阶段内同样受记录数上限约束。 -- 编解码器位于 `Monitor.Protocol/Raw`,Probe、Agent、Backend、离线工具使用同一份代码。 +- 编解码器位于 `MoShiro.Protocol/Raw`,Probe、Agent、Backend、离线工具使用同一份代码。 ## 5. 列的归属与合并 @@ -131,12 +131,12 @@ RawChunk 新增一种记录流只需要写声明,不需要写合并代码。Probe 的列在规定时间内没有送达时,目前 Agent 带着已有的数据继续(不上传只有 Agent 一侧的 `partial` 块,见已知缺口)。 -## 6. 代码结构(`backend/shared/Monitor.Measurement`) +## 6. 代码结构(`backend/shared/MoShiro.Measurement`) 同一种采集方法的两端是**一对角色**写在同一个文件里:拨号方(Probe 永远是拨号方,适配 NAT)和监听方(Agent)。两端共用包的构造和校验、时钟、行表和合并;Probe 和 Agent 只负责组装自己那一侧。 ```text -Monitor.Measurement/ 依赖:Monitor.Protocol(仅 BCL,零 NuGet) +MoShiro.Measurement/ 依赖:MoShiro.Protocol(仅 BCL,零 NuGet) ├─ Core/ │ ├─ Contracts.cs IBlock、BlockSpec、BlockEnv、DataSession、IAdmission、IFragmentSink…… │ ├─ RowStore.cs 本侧记录的行:只含本侧拥有的列,按键索引的环形缓冲,热路径不分配 diff --git a/docs/project-layout.md b/docs/project-layout.md index 4ba2efa..d7f1e34 100644 --- a/docs/project-layout.md +++ b/docs/project-layout.md @@ -3,8 +3,8 @@ ## 1. 目标 1. **三个独立项目加一个前端**:Probe、Agent、Backend 各自拥有解决方案,可以单独构建、测试和发布;Web 是独立的 Vite 项目(`web/`)。 -2. **通用能力只写一次**:TCP/UDP/TLS/ICMP 采集积木(类似 iperf3 的能力,但只记录原始数据)放在 `Monitor.Measurement` 中,Probe 和 Agent 共用;采集方法(Kind)由积木组合声明而成。 -3. **节点机制通用化**:注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新,由与业务无关的 **Fleet** 框架提供(见 [fleet/overview.md](fleet/overview.md))。Monitor 只写业务,其他产品也可以复用 Fleet。 +2. **通用能力只写一次**:TCP/UDP/TLS/ICMP 采集积木(类似 iperf3 的能力,但只记录原始数据)放在 `MoShiro.Measurement` 中,Probe 和 Agent 共用;采集方法(Kind)由积木组合声明而成。 +3. **节点机制通用化**:注册、身份、连接、RPC、可靠上报、状态同步、宿主、更新,由与业务无关的 **Fleet** 框架提供(见 [fleet/overview.md](fleet/overview.md))。MoShiro 只写业务,其他产品也可以复用 Fleet。 4. **改一处,整条链路受益**:协议、原始数据格式、测试方案、指标、错误码、版本号、编译设置、代码风格都只有**一个真源**。改动之后,下游要么自动更新,要么在编译期或 CI 中直接报错。 5. **模块化**:Probe/Agent 由可插拔的模块组成;Backend 按层分成多个项目,层之间的依赖在编译期强制。 6. **Probe/Agent 轻量**:运行时只依赖 BCL 和仓库内的库,不引用任何 NuGet 包。 @@ -12,45 +12,45 @@ ## 2. 仓库布局 ```text -monitor/ +moshiro/ ├─ README.md .gitignore ├─ docs/ docs/fleet/ ├─ scripts/ # publish / package / smoke / stability 等脚本(check-deps 等检查也放这里) ├─ packaging/ # install.sh、systemd 单元、OpenWrt 文件 ├─ web/ # 前端:Vite + React + TypeScript(自写),只依赖 backend/openapi/v1.json │ -├─ global.json Directory.Packages.props Monitor.slnx # SDK、NuGet 版本和全部 .NET 项目入口 +├─ global.json Directory.Packages.props MoShiro.slnx # SDK、NuGet 版本和全部 .NET 项目入口 ├─ .editorconfig BannedSymbols.txt # 风格与禁用 API(构建期强制) ├─ BannedSymbols.Node.txt # 仅用于常驻节点进程:纯采集边界 │ -├─ ci/Monitor.Tar/ # CI 归档工具,也加入根解决方案 +├─ ci/MoShiro.Tar/ # CI 归档工具,也加入根解决方案 └─ backend/ # 服务和节点 C# 代码(构建产物在 backend/artifacts/) - ├─ Directory.Build.props Monitor.slnx # 后端编译设置和后端目录解决方案 - ├─ fleet/ # 通用节点框架(不认识 Monitor,以后可以拆成独立仓库) + ├─ Directory.Build.props MoShiro.slnx # 后端编译设置和后端目录解决方案 + ├─ fleet/ # 通用节点框架(不认识 MoShiro,以后可以拆成独立仓库) │ ├─ src/ Fleet.Core Fleet.Identity Fleet.Rpc Fleet.Link Fleet.Hosting Fleet.Update │ │ Fleet.Enrollment.Client Fleet.Server.{Enrollment,Link,Releases,Sqlite} │ ├─ tools/ fleet-enroll fleet-updater fleet-release │ ├─ samples/ EchoNode EchoServer │ └─ tests/ - ├─ shared/ # Monitor 自己的共享库 - │ ├─ Monitor.Protocol/ # 契约真源:Kind/Plan 目录、任务集、monitor.* 方法、UDP 包、RawChunk、错误码 - │ ├─ Monitor.Measurement/ # 采集引擎:积木、Recorder、任务调和(见 measurement.md) + ├─ shared/ # MoShiro 自己的共享库 + │ ├─ MoShiro.Protocol/ # 契约真源:Kind/Plan 目录、任务集、moshiro.* 方法、UDP 包、RawChunk、错误码 + │ ├─ MoShiro.Measurement/ # 采集引擎:积木、Recorder、任务调和(见 measurement.md) │ └─ tests/ - ├─ probe/ src/Monitor.Probe/ tests/ # 家宽端采集器 - ├─ agent/ src/Monitor.Agent/ tests/ # VPS 端采集器 + ├─ probe/ src/MoShiro.Probe/ tests/ # 家宽端采集器 + ├─ agent/ src/MoShiro.Agent/ tests/ # VPS 端采集器 ├─ src/ # Backend(面板) - │ ├─ Monitor.Backend/ # 宿主与组合根(Program.cs) - │ ├─ Monitor.Backend.Api/ # API 层:HTTP 端点、DTO、错误映射 - │ ├─ Monitor.Backend.Gateway/ # 在 Fleet.Server.Link 上注册 Monitor 的方法、状态与收件箱 - │ ├─ Monitor.Backend.Application/ # 用例层 + 端口接口 - │ ├─ Monitor.Backend.Analysis/ # 分析(纯计算,见 analysis.md) - │ └─ Monitor.Backend.Storage.Sqlite/ # 端口接口的实现,同时实现 IFleetStore + │ ├─ MoShiro.Backend/ # 宿主与组合根(Program.cs) + │ ├─ MoShiro.Backend.Api/ # API 层:HTTP 端点、DTO、错误映射 + │ ├─ MoShiro.Backend.Gateway/ # 在 Fleet.Server.Link 上注册 MoShiro 的方法、状态与收件箱 + │ ├─ MoShiro.Backend.Application/ # 用例层 + 端口接口 + │ ├─ MoShiro.Backend.Analysis/ # 分析(纯计算,见 analysis.md) + │ └─ MoShiro.Backend.Storage.Sqlite/ # 端口接口的实现,同时实现 IFleetStore ├─ tests/ ├─ openapi/v1.json # 构建生成并提交 - └─ Monitor.Backend.slnx + └─ MoShiro.Backend.slnx ``` -节点机器上的发布物是三个独立进程,使用不同的系统用户:`monitor-agent`(或 `monitor-probe`)、`fleet-enroll`(一次性运行)、`fleet-updater`(常驻或定时运行)。 +节点机器上的发布物是三个独立进程,使用不同的系统用户:`moshiro-agent`(或 `moshiro-probe`)、`fleet-enroll`(一次性运行)、`fleet-updater`(常驻或定时运行)。 ## 3. 依赖关系 @@ -65,21 +65,21 @@ monitor/ │ └─────┬──────┘ │ │ ▼ ▼ ▼ ▼ ┌──────────────────────────────────┐ ┌─────────────────────────┐ - │ Monitor.Protocol │ │ Fleet.Server.* │ + │ MoShiro.Protocol │ │ Fleet.Server.* │ └────────────────┬─────────────────┘ └───────────┬─────────────┘ ▼ ▼ ┌──────────────────────────────────────────────────────────────────┐ │ Fleet(节点侧):Core · Identity · Rpc · Link · Hosting │ └──────────────────────────────────────────────────────────────────┘ - 反向依赖一律禁止:fleet/** ✗→ Monitor.* + 反向依赖一律禁止:fleet/** ✗→ MoShiro.* ``` 简要来说: ```text -Probe, Agent ──> Measurement ──> Monitor.Protocol ──> Fleet.Core / Fleet.Rpc +Probe, Agent ──> Measurement ──> MoShiro.Protocol ──> Fleet.Core / Fleet.Rpc Probe, Agent ──> Fleet.Link / Fleet.Hosting / Fleet.Identity -Backend.* ──> Monitor.Protocol + Fleet.Server.* (不引用 Measurement) +Backend.* ──> MoShiro.Protocol + Fleet.Server.* (不引用 Measurement) fleet-enroll ──> Fleet.Enrollment.Client ──> Fleet.Identity / Core fleet-updater ──> Fleet.Update ──> Fleet.Core (不引用 Link,也不读取节点身份) Web ──> openapi/v1.json @@ -87,12 +87,12 @@ Web ──> openapi/v1.json | 项目 | 允许依赖 | 禁止 | | --- | --- | --- | -| `Fleet.*`(节点侧) | BCL、其他节点侧 Fleet 库 | 任何 NuGet 运行时包;任何 `Monitor.*` | -| `Fleet.Server.*` | ASP.NET Core、`Microsoft.Data.Sqlite`(仅 Sqlite 项目)、节点侧 Fleet 库 | 任何 `Monitor.*` | -| `Monitor.Protocol` | Fleet.Core、Fleet.Rpc | 任何 I/O、套接字、日志 | -| `Monitor.Measurement` | Monitor.Protocol、Fleet.Core | NuGet;日志(只通过计数接收器上报);读取配置;统计或解释逻辑 | +| `Fleet.*`(节点侧) | BCL、其他节点侧 Fleet 库 | 任何 NuGet 运行时包;任何 `MoShiro.*` | +| `Fleet.Server.*` | ASP.NET Core、`Microsoft.Data.Sqlite`(仅 Sqlite 项目)、节点侧 Fleet 库 | 任何 `MoShiro.*` | +| `MoShiro.Protocol` | Fleet.Core、Fleet.Rpc | 任何 I/O、套接字、日志 | +| `MoShiro.Measurement` | MoShiro.Protocol、Fleet.Core | NuGet;日志(只通过计数接收器上报);读取配置;统计或解释逻辑 | | Probe / Agent | 上述库,以及 Fleet.Link / Hosting / Identity | NuGet 运行时包;彼此互相引用;Fleet.Update;Fleet.Enrollment.Client | -| Backend.* | Monitor.Protocol、Fleet.Server.*,以及第 5.2 节列出的包 | Measurement、EF Core 等依赖反射的库 | +| Backend.* | MoShiro.Protocol、Fleet.Server.*,以及第 5.2 节列出的包 | Measurement、EF Core 等依赖反射的库 | 分析器类包(`PrivateAssets="all"`)不受限制。`scripts/check-deps` 在 CI 中检查上述规则。 @@ -102,28 +102,28 @@ Web ──> openapi/v1.json 组件、扩展点与接入方式见 [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: +> **实现状态**:`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` 还只是设计(纯二进制方式的更新);容器节点的更新是宿主机上的 `moshiro-update` 脚本和面板的 `ReleaseService`,见 [deployment.md](deployment.md)。原来的 `MoShiro.Common` 和 `MoShiro.NodeHost` 已经并入 Fleet: | 原位置 | 现位置 | | --- | --- | -| `Monitor.Common`(Result、缓冲区、VarInt、有界队列、重放窗口、退避、ID、令牌哈希) | `Fleet.Core` | -| `Monitor.NodeHost`(模块宿主、日志、配置、ConfigStore、服务外壳) | `Fleet.Hosting` | +| `MoShiro.Common`(Result、缓冲区、VarInt、有界队列、重放窗口、退避、ID、令牌哈希) | `Fleet.Core` | +| `MoShiro.NodeHost`(模块宿主、日志、配置、ConfigStore、服务外壳) | `Fleet.Hosting` | | 节点密钥与签名 | `Fleet.Identity` | | JSON-RPC 信封与路由 | `Fleet.Rpc` | | Agent 注册 | `Fleet.Enrollment.Client` + `fleet-enroll` | -### 4.2 Monitor.Protocol(Monitor 的契约真源) +### 4.2 MoShiro.Protocol(MoShiro 的契约真源) ```text -Monitor.Protocol/ -├─ ProtocolVersion ProtocolLimits MonitorErrorCode(在 Fleet 错误码基础上扩展) +MoShiro.Protocol/ +├─ ProtocolVersion ProtocolLimits MoShiroErrorCode(在 Fleet 错误码基础上扩展) ├─ Ids/ AgentId(= Fleet NodeId)、ProbeId、TaskId、TestId、ChunkId、StreamId ├─ Catalog/ │ ├─ Kinds/ KindSpec 声明(积木组合 + ParamSpec + 记录列 + 预算) │ ├─ Plans/ 多阶段测试方案 │ └─ ParamSpec 参数定义(类型、默认值、上下限),节点与 Backend 共用同一套校验 ├─ Tasks/ TaskSet(Fleet 期望状态的文档类型)、调和结果 -├─ Rpc/ monitor.* 方法名、params/result、二进制通道号(从 16 开始) +├─ Rpc/ moshiro.* 方法名、params/result、二进制通道号(从 16 开始) ├─ Wire/ UDP 包、数据连接首帧 ├─ Raw/ 自描述 RawChunk:头部、列类型、列编解码、Brotli、crc32c └─ Json/ AgentRpcContext、ProbeRpcContext(按信道拆分,便于裁剪) @@ -131,7 +131,7 @@ Monitor.Protocol/ 累加器、直方图等统计类型**不在 Protocol 中**,只属于 `Backend.Analysis`。 -### 4.3 Monitor.Measurement(采集引擎) +### 4.3 MoShiro.Measurement(采集引擎) 包含: @@ -147,14 +147,14 @@ Monitor.Protocol/ ### 5.1 Agent 与 Probe:组装 -节点的组装点是 `AgentApp` 和 `ProbeApp`:在构造函数里**显式组装**,不使用 DI 容器,也不做反射扫描;没有全局状态,测试可以在同一进程里启动多个。连接、认证、重连、发件箱、状态同步都由 Fleet 提供,Monitor 只写业务: +节点的组装点是 `AgentApp` 和 `ProbeApp`:在构造函数里**显式组装**,不使用 DI 容器,也不做反射扫描;没有全局状态,测试可以在同一进程里启动多个。连接、认证、重连、发件箱、状态同步都由 Fleet 提供,MoShiro 只写业务: ```text AgentApp Probe(ProbeApp) FleetLink(到面板) FleetLink(到面板) - 状态 TaskSet、方法 monitor.test.* 状态 probe_agents(Agent 列表)、hello 带系统和构建信息 - 发件箱 RawChunk(通道 16) 心跳带更新器的状态;monitor.update.* 通知 - 心跳带更新器的状态;monitor.update.* AgentDirectory → 每个 Agent 一个 AgentConnection(点对点会话) + 状态 TaskSet、方法 moshiro.test.* 状态 probe_agents(Agent 列表)、hello 带系统和构建信息 + 发件箱 RawChunk(通道 16) 心跳带更新器的状态;moshiro.update.* 通知 + 心跳带更新器的状态;moshiro.update.* AgentDirectory → 每个 Agent 一个 AgentConnection(点对点会话) PeerListener / PeerHub(Probe 会话) 会话内:线路任务集 line_task_set 的调和、角色、列块发件箱(通道 17) UdpMux / TcpAcceptor(数据端口) TaskManager / TestRunner / ChunkAssembler @@ -163,7 +163,7 @@ AgentApp Probe(ProbeApp) 模块之间通过构造函数传入的接口或 `Channel` 通信,禁止通过静态全局状态共享数据。 -**新增 RPC 方法**:在 `Monitor.Protocol/Rpc` 中定义 params/result,然后在组装处加一行 `.On(Methods.X, HandleX)`。 +**新增 RPC 方法**:在 `MoShiro.Protocol/Rpc` 中定义 params/result,然后在组装处加一行 `.On(Methods.X, HandleX)`。 ### 5.2 Backend:分层与 API 层 @@ -172,7 +172,7 @@ AgentApp Probe(ProbeApp) │ │ ▲ ┌──────────────▼───────────────┐ ┌───────────▼──────────┐ │ │ Backend.Api │ │ Backend.Gateway │ │ - │ Public/ Admin/ │ │ 注册 monitor.* 方法 │ │ + │ Public/ Admin/ │ │ 注册 moshiro.* 方法 │ │ │ 认证(Cookie/CSRF/令牌) │ │ TaskSet 状态提供者 │ │ │ 错误映射 · OpenAPI · SSE 端点 │ │ RawChunk 收件箱 │ │ └──────────────┬───────────────┘ └───────────┬──────────┘ │ @@ -187,7 +187,7 @@ AgentApp Probe(ProbeApp) │ 实现端口 ┌───────────────────────────┴─────────────────────────────┐ │ Backend.Storage.Sqlite:段文件 + SQLite(只做读写) │ - │ 同时实现 IFleetStore(Fleet 的节点和令牌表放在 monitor.db 中)│ + │ 同时实现 IFleetStore(Fleet 的节点和令牌表放在 moshiro.db 中)│ └─────────────────────────────────────────────────────────┘ 端口:IAgentStore、ITaskStore、IRawStore、IMetricStore、ITestStore、IAuthStore @@ -198,16 +198,16 @@ AgentApp Probe(ProbeApp) - **Api 不能引用 Storage**(编译期保证);Api 和 Gateway 只调用 Application。 - Application 返回 `Result`。**错误码到 HTTP 状态的映射只在 Api 层做一次**(`ErrorCodeHttpMap`);到 JSON-RPC 错误的映射由 Fleet.Rpc 统一完成。 - Api 层按调用方分目录:`Public/`、`Admin/`,公共部分放在 `Common/`(分页、ETag、认证过滤器、ProblemFactory、`EndpointConventions`)。每类资源一个端点文件,新增资源时在 `ApiModule.Map` 中加一行。 -- 注册端点、签名认证、连接注册表、状态推送、收件箱 ACK、发布通道都来自 Fleet(`MapFleetEnrollment`、`MapFleetLink`、`MapFleetReleases`)。Monitor 只提供: +- 注册端点、签名认证、连接注册表、状态推送、收件箱 ACK、发布通道都来自 Fleet(`MapFleetEnrollment`、`MapFleetLink`、`MapFleetReleases`)。MoShiro 只提供: - `IFleetStore` 的实现; - `TaskSet` 的状态提供者; - `RawChunk` 的收件箱; - - `monitor.*` 方法。 + - `moshiro.*` 方法。 - public 层的 DTO 单独定义,只暴露可以公开的字段,并统一经过“可见性过滤器”。 - 请求校验:简单字段约束使用 .NET 10 内置的 Minimal API 校验;任务参数统一用 `ParamSpec` 校验(与节点共用);跨字段规则放在 Application 层。 - Backend 使用 ASP.NET Core 自带的 DI 容器,只在组合根显式注册。 -Backend.Analysis 只依赖 Monitor.Protocol 和 Fleet.Core,没有 I/O,可以单独做成离线 CLI 使用。 +Backend.Analysis 只依赖 MoShiro.Protocol 和 Fleet.Core,没有 I/O,可以单独做成离线 CLI 使用。 Backend 允许的包:ASP.NET Core 共享框架、`Microsoft.Data.Sqlite`、`Microsoft.AspNetCore.OpenApi`、`Microsoft.Extensions.ApiDescription.Server`(仅构建期使用)。 @@ -221,11 +221,11 @@ Backend 允许的包:ASP.NET Core 共享框架、`Microsoft.Data.Sqlite`、`Mi | 原始数据格式 | `Protocol/Raw`(自描述) | 新增列不需要升级版本;四处使用同一份编解码器 | | 新增统计指标 | `Analysis/Metrics` 中加 1 行 `MetricDef` | 存储、降采样、API、SSE、目录全部自动处理;回填 30 天历史 | | 服务评分 | `Analysis/Models` | 查询时计算,立即对全部历史生效 | -| 新增 RPC 能力 | `Monitor.Protocol/Rpc` + 组装处加 1 行 | 通过 Fleet 的 capabilities 协商,旧节点不受影响 | -| 注册、认证、重连、发件箱、状态同步 | `backend/fleet/` | Monitor 和所有其他产品同时受益 | +| 新增 RPC 能力 | `MoShiro.Protocol/Rpc` + 组装处加 1 行 | 通过 Fleet 的 capabilities 协商,旧节点不受影响 | +| 注册、认证、重连、发件箱、状态同步 | `backend/fleet/` | MoShiro 和所有其他产品同时受益 | | 更新机制 | `Fleet.Update` + `fleet-updater` | 所有产品共用;产品只提供发布清单 | | REST DTO 与端点 | `Backend.Api` | 生成 OpenAPI,CI 检查 diff,前端的 TS 类型随之更新 | -| 错误码 | Fleet 错误码 + `MonitorErrorCode` | REST、JSON-RPC、OpenAPI、TS 全部一致 | +| 错误码 | Fleet 错误码 + `MoShiroErrorCode` | REST、JSON-RPC、OpenAPI、TS 全部一致 | | 版本、编译设置、风格、禁用 API | `backend/` 下的 props、`.editorconfig`、`BannedSymbols*.txt` | 所有项目 | ## 7. 编译设置 @@ -334,10 +334,10 @@ M0 阶段先测出基线,之后作为 CI 的回归门槛(超出 10% 即失 | C 库 | musl:使用 `linux-musl-*` 目标发布 | | TLS | .NET 在 Linux 上依赖 OpenSSL,而 OpenWrt 默认使用 mbedTLS/wolfSSL。需要安装 `libopenssl3`(安装脚本自动检查并提示)。启动时自检,缺少时明确报错,不会静默降级 | | 全球化 | 已启用 `InvariantGlobalization`,不依赖 ICU | -| 服务管理 | procd:提供 `/etc/init.d/monitor-probe`(`procd_set_param respawn`,前台运行,日志输出到 logd);`Fleet.Hosting.ServiceShell` 在 procd 下不需要 `sd_notify` | -| 存储 | 闪存很小,而且需要避免频繁写入:二进制放在 `/usr/lib/monitor/`,配置和身份放在 `/etc/monitor/`(sysupgrade 时通过 `/lib/upgrade/keep.d/` 保留);测量数据本来就不落盘;`ConfigStore` 只在 Agent 列表变化时写入 | +| 服务管理 | procd:提供 `/etc/init.d/moshiro-probe`(`procd_set_param respawn`,前台运行,日志输出到 logd);`Fleet.Hosting.ServiceShell` 在 procd 下不需要 `sd_notify` | +| 存储 | 闪存很小,而且需要避免频繁写入:二进制放在 `/usr/lib/moshiro/`,配置和身份放在 `/etc/moshiro/`(sysupgrade 时通过 `/lib/upgrade/keep.d/` 保留);测量数据本来就不落盘;`ConfigStore` 只在 Agent 列表变化时写入 | | 内存 | 预算 ≤ 32 MB,适合 ≥ 256 MB 内存的路由器;在本地配置中限制重负载测试的码率(`limits.max_test_mbps`),防止路由器 CPU 成为瓶颈,反过来影响测量结果 | -| 测量偏差 | Probe 运行在网关上时,自己没有 NAT;一旦路由器 CPU 饱和,结果就会失真。因此 Probe 在 `monitor.event` 中上报自身的 CPU 繁忙标记(仅进程级别),分析层据此标注置信说明 | +| 测量偏差 | Probe 运行在网关上时,自己没有 NAT;一旦路由器 CPU 饱和,结果就会失真。因此 Probe 在 `moshiro.event` 中上报自身的 CPU 繁忙标记(仅进程级别),分析层据此标注置信说明 | | 打包 | 提供 `tar.gz` 和 procd 脚本;包格式(ipk 或 apk)按目标 OpenWrt 版本提供 | | 更新 | `fleet-updater` 使用 procd 适配器;`keep_versions = 1`,下载前检查剩余空间,避免写满闪存 | diff --git a/docs/protocol.md b/docs/protocol.md index 9c8f7e3..0352ece 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -1,35 +1,35 @@ # 线协议 -Monitor 的两条控制链路都建立在 Fleet 之上: +MoShiro 的两条控制链路都建立在 Fleet 之上: - **Agent ↔ Backend**:Fleet 连接服务端(wss),见 [fleet/link.md](fleet/link.md); - **Probe ↔ Agent**:Fleet 节点间点对点会话(TLS),见 [fleet/link.md](fleet/link.md) 第 8 节。 -认证、心跳、重连、能力协商、可靠发件箱、期望状态同步、JSON-RPC 信封都由 Fleet 负责。本文只定义 **Monitor 自己的内容**:`monitor.*` 方法、状态文档、二进制通道,以及 Fleet 之外的数据面(UDP 包和数据连接)。所有类型都放在 `Monitor.Protocol` 中。 +认证、心跳、重连、能力协商、可靠发件箱、期望状态同步、JSON-RPC 信封都由 Fleet 负责。本文只定义 **MoShiro 自己的内容**:`moshiro.*` 方法、状态文档、二进制通道,以及 Fleet 之外的数据面(UDP 包和数据连接)。所有类型都放在 `MoShiro.Protocol` 中。 ## 1. Fleet 能力的映射 -| Monitor 需要的能力 | 使用的 Fleet 机制 | Monitor 提供的内容 | +| MoShiro 需要的能力 | 使用的 Fleet 机制 | MoShiro 提供的内容 | | --- | --- | --- | | 下发任务 | 期望状态 `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) | +| 更新 | 通知 `moshiro.update.offer` / `moshiro.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` | | Probe 交付列块 | 点对点会话的发件箱,二进制通道 **17** | 列块:`u32 stream_id \| u32 first_seq \| u32 count \| 列数据(Brotli)` | | 节点自身状态 | `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:<列表>`;Agent 附加 `platform`(系统、架构、版本、tag、提交、构建时间)、`tls_spki`、`probe_ports`、`local_limits` 摘要;Probe 附加 `platform` | +| 版本与能力 | `fleet.hello` | capabilities 包括 `moshiro.kinds:<列表>`、`moshiro.blocks:<列表>`;Agent 附加 `platform`(系统、架构、版本、tag、提交、构建时间)、`tls_spki`、`probe_ports`、`local_limits` 摘要;Probe 附加 `platform` | -## 2. `monitor.*` 方法 +## 2. `moshiro.*` 方法 ### Agent ↔ Backend | 方法 | 方向 | 类型 | 参数与结果 | | --- | --- | --- | --- | -| `monitor.test.run` | B→A | 请求 | `test_id`、`probe_id`(在哪个 Probe 上跑)、`plan`、`phases[]` → `accepted`,或错误 `test_busy`、`probe_offline` | -| `monitor.probe.disconnect` | B→A | 请求 | `probe_id` → `accepted`(Agent 是否真的有这个会话);Agent 关闭该 Probe 的会话 | -| `monitor.test.cancel` | B→A | 请求 | `test_id` | -| `monitor.test.status` | A→B | 通知 | `test_id`、`status`(`running`、`phase_done`、`succeeded`、`failed`、`aborted`)、`phase?`、`error_code?` | -| `monitor.event` | A→B | 通知 | `kind`(`probe_connected`(`detail` 是 `probe_id`,`probe` 里带版本、远端地址和连接时间)、`probe_disconnected`、`endpoint_changed`、`session_rejected`、`clock_step`、`gap`、`cpu_saturated`(节点进程 CPU 饱和,可能影响测量,由 Probe 或 Agent 上报))、`at_utc`、`detail` | +| `moshiro.test.run` | B→A | 请求 | `test_id`、`probe_id`(在哪个 Probe 上跑)、`plan`、`phases[]` → `accepted`,或错误 `test_busy`、`probe_offline` | +| `moshiro.probe.disconnect` | B→A | 请求 | `probe_id` → `accepted`(Agent 是否真的有这个会话);Agent 关闭该 Probe 的会话 | +| `moshiro.test.cancel` | B→A | 请求 | `test_id` | +| `moshiro.test.status` | A→B | 通知 | `test_id`、`status`(`running`、`phase_done`、`succeeded`、`failed`、`aborted`)、`phase?`、`error_code?` | +| `moshiro.event` | A→B | 通知 | `kind`(`probe_connected`(`detail` 是 `probe_id`,`probe` 里带版本、远端地址和连接时间)、`probe_disconnected`、`endpoint_changed`、`session_rejected`、`clock_step`、`gap`、`cpu_saturated`(节点进程 CPU 饱和,可能影响测量,由 Probe 或 Agent 上报))、`at_utc`、`detail` | 心跳的附加字段里有 `probes[]`:Agent 当前连着的每个 Probe(`id`、`version`、`remote`、`since_utc_ms`),每次都是完整快照,Backend 靠它纠正错过的连接事件。点对点握手的认证消息里可以带 Probe 的 `version`(只用于展示)。块头里的 `probe_id` 以 Agent 认证过的会话为准,Probe 自报的值会被覆盖。 @@ -38,10 +38,10 @@ Monitor 的两条控制链路都建立在 Fleet 之上: | 方法 | 方向 | 类型 | 参数与结果 | | --- | --- | --- | --- | | `fleet.state.sync`(文档类型 `line_task_set`) | A→P | 请求 | 这条线路的任务(`id`、`kind`、`params`、`stream_id`、`port`)和 Agent 的数据面端口 | -| `monitor.clock` | P→A | 请求 | `probe_mono_us` → `agent_mono_us`;Probe 按往返中点估计两端单调时钟的偏移(取最小往返的一次),记入块头 `probe_offset_us` | -| `monitor.test.phase` | A→P | 请求 | 测试的一个阶段:`test_id`、`phase`、`kind`、`params`、`stream_id`、`duration_s`、`port` → `accepted`;Probe 在回复之前准备好自己一侧的角色(单向数据流:先发几个探活包让 Agent 学到源地址) | -| `monitor.test.phase_end` | A→P | 请求 | `sent_count`(Agent 发出的包数)→ `fragments`:Probe 收尾,把列块交给 Agent,回复交出的个数;Agent 等全部列块合并上传之后才进入下一阶段 | -| `monitor.test.abort` | A→P | 通知 | `test_id` | +| `moshiro.clock` | P→A | 请求 | `probe_mono_us` → `agent_mono_us`;Probe 按往返中点估计两端单调时钟的偏移(取最小往返的一次),记入块头 `probe_offset_us` | +| `moshiro.test.phase` | A→P | 请求 | 测试的一个阶段:`test_id`、`phase`、`kind`、`params`、`stream_id`、`duration_s`、`port` → `accepted`;Probe 在回复之前准备好自己一侧的角色(单向数据流:先发几个探活包让 Agent 学到源地址) | +| `moshiro.test.phase_end` | A→P | 请求 | `sent_count`(Agent 发出的包数)→ `fragments`:Probe 收尾,把列块交给 Agent,回复交出的个数;Agent 等全部列块合并上传之后才进入下一阶段 | +| `moshiro.test.abort` | A→P | 通知 | `test_id` | | `fleet.outbox.ack` | 双向 | 通知 | Probe 的列块(通道 17)是至少一次投递,Agent 每收到一个就确认,重复到达的只处理一次 | 会话密钥不需要单独的方法:点对点握手(TLS 固定证书 → `peer.challenge` → Probe 用身份私钥签 `(peer_id, nonce, client_pub, server_pub)` → 两端各自 ECDH + HKDF)直接得到 `session_id` 和 `session_key`,`peer.auth` 的应答只回显 `session_id` 用来互相确认。 @@ -58,7 +58,7 @@ Probe 只记录自己一侧的列,Agent 在数据面记录另一侧,由 Agen 单块最多 `MaxRecordsPerChunk`(50 000)条记录:Probe 必须把大的阶段按序号范围切成多个列块(例如 100 Mbit/s、1000 B 的 10 秒阶段是 125 000 个包 → 4 个列块),`phase_end` 的应答报告总块数。块头里 Probe 必须带上 `agent_id`(它知道自己在和哪个 Agent 通信)。 -新增方法只需要在 `Monitor.Protocol/Rpc` 中定义,并在节点组装处加一行 `.On(...)`。旧节点会通过 capabilities 协商自动避开不支持的方法。 +新增方法只需要在 `MoShiro.Protocol/Rpc` 中定义,并在节点组装处加一行 `.On(...)`。旧节点会通过 capabilities 协商自动避开不支持的方法。 ## 3. 数据面(不属于 Fleet) @@ -95,7 +95,7 @@ Probe 只记录自己一侧的列,Agent 在数据面记录另一侧,由 Agen ## 4. 版本演进 - Fleet 层的兼容规则见 [fleet/link.md](fleet/link.md)。 -- `monitor.*` 方法:新增方法或可选字段不需要升级版本,接收方忽略未知字段。 +- `moshiro.*` 方法:新增方法或可选字段不需要升级版本,接收方忽略未知字段。 - RawChunk:块头自描述,新增列或新增 Kind 不需要升级 `container_version`;只有容器格式变化时才升级。 - UDP 包和数据连接首帧的布局变化时,必须升级 `protocol_version`,并更新黄金字节测试。 - Agent 同时支持当前版本和上一个版本的 Probe;Backend 能解码保留期内所有的 `container_version`。 diff --git a/docs/storage.md b/docs/storage.md index bc6afd1..bdea426 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -1,12 +1,12 @@ # Backend 存储 -实现位于 `Monitor.Backend.Storage.Sqlite`,负责实现 `Backend.Application` 中定义的端口接口,**只做读写,不含业务逻辑**。降采样和分析由 Application 调用 Analysis 完成。 +实现位于 `MoShiro.Backend.Storage.Sqlite`,负责实现 `Backend.Application` 中定义的端口接口,**只做读写,不含业务逻辑**。降采样和分析由 Application 调用 Analysis 完成。 ## 1. 目录布局 ```text data/ -├─ monitor.db # SQLite:配置、身份、原始块索引、指标、测试结果 +├─ moshiro.db # SQLite:配置、身份、原始块索引、指标、测试结果 └─ raw/ └─ 2026-09-27/ # 按 UTC 日期分目录,保留期满整目录删除 ├─ agt_01J…/ # 每条线路或每个 Agent 一个目录 @@ -28,7 +28,7 @@ data/ ## 3. 表结构 -`agents`、`join_tokens` 以及公钥和状态版本字段,同时作为 Fleet 的 `IFleetStore` 存储(由 `Monitor.Backend.Storage.Sqlite` 实现),和 Monitor 的其他数据放在同一个 `monitor.db` 中,由同一个单写入者写入。发布与灰度相关的表(`fleet_releases`、`fleet_rollouts`)由 `Fleet.Server.Releases` 自带的迁移脚本创建(职责见 [fleet/update.md](fleet/update.md) 第 6 节)。 +`agents`、`join_tokens` 以及公钥和状态版本字段,同时作为 Fleet 的 `IFleetStore` 存储(由 `MoShiro.Backend.Storage.Sqlite` 实现),和 MoShiro 的其他数据放在同一个 `moshiro.db` 中,由同一个单写入者写入。发布与灰度相关的表(`fleet_releases`、`fleet_rollouts`)由 `Fleet.Server.Releases` 自带的迁移脚本创建(职责见 [fleet/update.md](fleet/update.md) 第 6 节)。 ```sql -- 身份与注册 ------------------------------------------------------------- diff --git a/packaging/compose/agent/compose.yaml b/packaging/compose/agent/compose.yaml index 567d78b..26bdbdf 100644 --- a/packaging/compose/agent/compose.yaml +++ b/packaging/compose/agent/compose.yaml @@ -3,15 +3,15 @@ # 首次注册、配置导入和自动更新见 docs/deployment.md。 services: agent: - image: ${MONITOR_IMAGE:-registry.oeo.one/monitor/monitor-agent}:${MONITOR_VERSION:-0.1.1} - container_name: monitor-agent + image: ${MOSHIRO_IMAGE:-registry.oeo.one/moshiro/moshiro-agent}:${MOSHIRO_VERSION:-0.1.1} + container_name: moshiro-agent restart: unless-stopped init: true # Probe 要直连 Agent 的 7443(TCP+UDP)和 7444(TCP),用宿主网络,不做端口映射 network_mode: host volumes: - - agent-config:/etc/monitor - - agent-status:/etc/monitor/update-status:ro + - agent-config:/etc/moshiro + - agent-status:/etc/moshiro/update-status:ro - /etc/os-release:/etc/os-release:ro # 系统目录只读,不加 :z/:Z;SELinux 隔离在容器级别禁用。 - /proc:/host/proc:ro @@ -36,6 +36,6 @@ services: volumes: agent-config: - name: monitor-agent-config + name: moshiro-agent-config agent-status: - name: monitor-agent-status + name: moshiro-agent-status diff --git a/packaging/compose/backend/compose.yaml b/packaging/compose/backend/compose.yaml index 0e1be0c..0cd9867 100644 --- a/packaging/compose/backend/compose.yaml +++ b/packaging/compose/backend/compose.yaml @@ -1,15 +1,15 @@ # Docker / Podman 共用,数据库和原始数据保存在命名 volume 中。 services: backend: - image: ${MONITOR_IMAGE:-registry.oeo.one/monitor/monitor-backend}:${MONITOR_VERSION:-0.1.1} - container_name: monitor-backend + image: ${MOSHIRO_IMAGE:-registry.oeo.one/moshiro/moshiro-backend}:${MOSHIRO_VERSION:-0.1.1} + container_name: moshiro-backend restart: unless-stopped ports: - "127.0.0.1:8080:8080" environment: - Monitor__InitialAdminPassword: ${MONITOR_INITIAL_ADMIN_PASSWORD:?Set MONITOR_INITIAL_ADMIN_PASSWORD} + MoShiro__InitialAdminPassword: ${MOSHIRO_INITIAL_ADMIN_PASSWORD:?Set MOSHIRO_INITIAL_ADMIN_PASSWORD} volumes: - backend-data:/data volumes: backend-data: - name: monitor-backend-data + name: moshiro-backend-data diff --git a/packaging/compose/probe/compose.yaml b/packaging/compose/probe/compose.yaml index 98aea62..7aac5a7 100644 --- a/packaging/compose/probe/compose.yaml +++ b/packaging/compose/probe/compose.yaml @@ -3,14 +3,14 @@ # 首次注册、配置导入和自动更新见 docs/deployment.md。 services: probe: - image: ${MONITOR_IMAGE:-registry.oeo.one/monitor/monitor-probe}:${MONITOR_VERSION:-0.1.1} - container_name: monitor-probe + image: ${MOSHIRO_IMAGE:-registry.oeo.one/moshiro/moshiro-probe}:${MOSHIRO_VERSION:-0.1.1} + container_name: moshiro-probe restart: unless-stopped init: true network_mode: host volumes: - - probe-config:/etc/monitor - - probe-status:/etc/monitor/update-status:ro + - probe-config:/etc/moshiro + - probe-status:/etc/moshiro/update-status:ro - /etc/os-release:/etc/os-release:ro read_only: true tmpfs: @@ -27,6 +27,6 @@ services: volumes: probe-config: - name: monitor-probe-config + name: moshiro-probe-config probe-status: - name: monitor-probe-status + name: moshiro-probe-status diff --git a/packaging/container/Agent.Containerfile b/packaging/container/Agent.Containerfile index 32dd036..15156ec 100644 --- a/packaging/container/Agent.Containerfile +++ b/packaging/container/Agent.Containerfile @@ -1,17 +1,17 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine ARG APP_VERSION=unknown -LABEL org.opencontainers.image.title="Monitor Agent" \ - org.opencontainers.image.description="MyMonitor node agent for Linux" \ +LABEL org.opencontainers.image.title="MoShiro Agent" \ + org.opencontainers.image.description="MoShiro node agent for Linux" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.version="$APP_VERSION" -COPY --chown=1654:1654 app/monitor-agent /usr/local/bin/monitor-agent +COPY --chown=1654:1654 app/moshiro-agent /usr/local/bin/moshiro-agent COPY --chown=1654:1654 app/fleet-enroll /usr/local/bin/fleet-enroll -COPY --chown=1654:1654 config/ /etc/monitor/ +COPY --chown=1654:1654 config/ /etc/moshiro/ USER 1654:1654 -WORKDIR /etc/monitor -VOLUME ["/etc/monitor"] -ENTRYPOINT ["/usr/local/bin/monitor-agent"] -CMD ["--config", "/etc/monitor/agent.json"] +WORKDIR /etc/moshiro +VOLUME ["/etc/moshiro"] +ENTRYPOINT ["/usr/local/bin/moshiro-agent"] +CMD ["--config", "/etc/moshiro/agent.json"] diff --git a/packaging/container/Agent.NativeBuild.Containerfile b/packaging/container/Agent.NativeBuild.Containerfile index 4f81658..ef818a6 100644 --- a/packaging/container/Agent.NativeBuild.Containerfile +++ b/packaging/container/Agent.NativeBuild.Containerfile @@ -3,16 +3,16 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine-aot AS build WORKDIR /src/backend COPY restore/backend/ ./ # restore 也要带 PublishAot:不然不会还原 ILCompiler,之后 --no-restore 的发布就悄悄变成「裁剪过的框架依赖程序」 -RUN dotnet restore agent/src/Monitor.Agent -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts \ +RUN dotnet restore agent/src/MoShiro.Agent -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts \ && dotnet restore fleet/tools/fleet-enroll -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts COPY source/backend/ ./ # 构建信息:CI 通过 --build-arg 传入;不传就是空(放在 restore 之后,每次构建都变的时间不会让依赖还原的缓存失效) -ARG MONITOR_TAG= -ARG MONITOR_COMMIT= -ARG MONITOR_BUILT_AT= -ENV MONITOR_TAG=$MONITOR_TAG MONITOR_COMMIT=$MONITOR_COMMIT MONITOR_BUILT_AT=$MONITOR_BUILT_AT -RUN dotnet publish agent/src/Monitor.Agent -m:3 -c Release -r linux-musl-x64 \ +ARG MOSHIRO_TAG= +ARG MOSHIRO_COMMIT= +ARG MOSHIRO_BUILT_AT= +ENV MOSHIRO_TAG=$MOSHIRO_TAG MOSHIRO_COMMIT=$MOSHIRO_COMMIT MOSHIRO_BUILT_AT=$MOSHIRO_BUILT_AT +RUN dotnet publish agent/src/MoShiro.Agent -m:3 -c Release -r linux-musl-x64 \ --self-contained true --no-restore -p:PublishAot=true -p:UseAotAnywhere=false \ -p:UseAppHost=true -p:StripSymbols=true \ -p:DebugType=none -p:DebugSymbols=false --artifacts-path /artifacts -o /out/agent \ @@ -21,23 +21,23 @@ RUN dotnet publish agent/src/Monitor.Agent -m:3 -c Release -r linux-musl-x64 \ -p:UseAppHost=true -p:StripSymbols=true \ -p:DebugType=none -p:DebugSymbols=false --artifacts-path /artifacts -o /out/enroll # 原生程序有几 MB;几十 KB 的是只会去找 .dll 的启动器 -RUN test -x /out/agent/monitor-agent && test -x /out/enroll/fleet-enroll \ - && test "$(stat -c%s /out/agent/monitor-agent)" -gt 2000000 && test "$(stat -c%s /out/enroll/fleet-enroll)" -gt 2000000 +RUN test -x /out/agent/moshiro-agent && test -x /out/enroll/fleet-enroll \ + && test "$(stat -c%s /out/agent/moshiro-agent)" -gt 2000000 && test "$(stat -c%s /out/enroll/fleet-enroll)" -gt 2000000 FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine ARG APP_VERSION=main -LABEL org.opencontainers.image.title="Monitor Agent" \ - org.opencontainers.image.description="MyMonitor node agent for Linux" \ +LABEL org.opencontainers.image.title="MoShiro Agent" \ + org.opencontainers.image.description="MoShiro node agent for Linux" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.version="$APP_VERSION" -COPY --chown=1654:1654 --from=build /out/agent/monitor-agent /usr/local/bin/monitor-agent +COPY --chown=1654:1654 --from=build /out/agent/moshiro-agent /usr/local/bin/moshiro-agent COPY --chown=1654:1654 --from=build /out/enroll/fleet-enroll /usr/local/bin/fleet-enroll -COPY --chown=1654:1654 config/ /etc/monitor/ +COPY --chown=1654:1654 config/ /etc/moshiro/ USER 1654:1654 -WORKDIR /etc/monitor -VOLUME ["/etc/monitor"] -ENTRYPOINT ["/usr/local/bin/monitor-agent"] -CMD ["--config", "/etc/monitor/agent.json"] +WORKDIR /etc/moshiro +VOLUME ["/etc/moshiro"] +ENTRYPOINT ["/usr/local/bin/moshiro-agent"] +CMD ["--config", "/etc/moshiro/agent.json"] diff --git a/packaging/container/Backend.Containerfile b/packaging/container/Backend.Containerfile index 26d6101..d0c8f5b 100644 --- a/packaging/container/Backend.Containerfile +++ b/packaging/container/Backend.Containerfile @@ -1,20 +1,20 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine ARG APP_VERSION=unknown -LABEL org.opencontainers.image.title="Monitor Backend" \ +LABEL org.opencontainers.image.title="MoShiro Backend" \ org.opencontainers.image.description="Self-hosted network path quality monitoring backend" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.version="$APP_VERSION" WORKDIR /app -COPY --chown=1654:1654 app/monitor-backend /app/monitor-backend +COPY --chown=1654:1654 app/moshiro-backend /app/moshiro-backend COPY --chown=1654:1654 data/.keep /data/.keep ENV ASPNETCORE_HTTP_PORTS=8080 \ - Monitor__DataDirectory=/data \ + MoShiro__DataDirectory=/data \ DOTNET_EnableDiagnostics=0 USER 1654:1654 VOLUME ["/data"] EXPOSE 8080 -ENTRYPOINT ["/app/monitor-backend"] +ENTRYPOINT ["/app/moshiro-backend"] diff --git a/packaging/container/Probe.Containerfile b/packaging/container/Probe.Containerfile index 8f198b7..8ca719e 100644 --- a/packaging/container/Probe.Containerfile +++ b/packaging/container/Probe.Containerfile @@ -1,16 +1,16 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine ARG APP_VERSION=unknown -LABEL org.opencontainers.image.title="Monitor Probe" \ - org.opencontainers.image.description="MyMonitor network measurement probe for Linux" \ +LABEL org.opencontainers.image.title="MoShiro Probe" \ + org.opencontainers.image.description="MoShiro network measurement probe for Linux" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.version="$APP_VERSION" -COPY --chown=1654:1654 app/monitor-probe /usr/local/bin/monitor-probe -COPY --chown=1654:1654 config/ /etc/monitor/ +COPY --chown=1654:1654 app/moshiro-probe /usr/local/bin/moshiro-probe +COPY --chown=1654:1654 config/ /etc/moshiro/ USER 1654:1654 -WORKDIR /etc/monitor -VOLUME ["/etc/monitor"] -ENTRYPOINT ["/usr/local/bin/monitor-probe"] -CMD ["--config", "/etc/monitor/probe.json"] +WORKDIR /etc/moshiro +VOLUME ["/etc/moshiro"] +ENTRYPOINT ["/usr/local/bin/moshiro-probe"] +CMD ["--config", "/etc/moshiro/probe.json"] diff --git a/packaging/container/Probe.NativeBuild.Containerfile b/packaging/container/Probe.NativeBuild.Containerfile index 76abe65..387e83a 100644 --- a/packaging/container/Probe.NativeBuild.Containerfile +++ b/packaging/container/Probe.NativeBuild.Containerfile @@ -3,35 +3,35 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine-aot AS build WORKDIR /src/backend COPY restore/backend/ ./ # restore 也要带 PublishAot:不然不会还原 ILCompiler,之后 --no-restore 的发布就悄悄变成「裁剪过的框架依赖程序」 -RUN dotnet restore probe/src/Monitor.Probe -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts +RUN dotnet restore probe/src/MoShiro.Probe -r linux-musl-x64 -p:PublishAot=true --artifacts-path /artifacts COPY source/backend/ ./ # 构建信息:CI 通过 --build-arg 传入;不传就是空(放在 restore 之后,每次构建都变的时间不会让依赖还原的缓存失效) -ARG MONITOR_TAG= -ARG MONITOR_COMMIT= -ARG MONITOR_BUILT_AT= -ENV MONITOR_TAG=$MONITOR_TAG MONITOR_COMMIT=$MONITOR_COMMIT MONITOR_BUILT_AT=$MONITOR_BUILT_AT -RUN dotnet publish probe/src/Monitor.Probe -m:3 -c Release -r linux-musl-x64 \ +ARG MOSHIRO_TAG= +ARG MOSHIRO_COMMIT= +ARG MOSHIRO_BUILT_AT= +ENV MOSHIRO_TAG=$MOSHIRO_TAG MOSHIRO_COMMIT=$MOSHIRO_COMMIT MOSHIRO_BUILT_AT=$MOSHIRO_BUILT_AT +RUN dotnet publish probe/src/MoShiro.Probe -m:3 -c Release -r linux-musl-x64 \ --self-contained true --no-restore -p:PublishAot=true -p:UseAotAnywhere=false \ -p:UseAppHost=true -p:StripSymbols=true \ -p:DebugType=none -p:DebugSymbols=false --artifacts-path /artifacts -o /out/probe # 原生程序有几 MB;几十 KB 的是只会去找 .dll 的启动器 -RUN test -x /out/probe/monitor-probe \ - && test "$(stat -c%s /out/probe/monitor-probe)" -gt 2000000 +RUN test -x /out/probe/moshiro-probe \ + && test "$(stat -c%s /out/probe/moshiro-probe)" -gt 2000000 FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine ARG APP_VERSION=main -LABEL org.opencontainers.image.title="Monitor Probe" \ - org.opencontainers.image.description="MyMonitor network measurement probe for Linux" \ +LABEL org.opencontainers.image.title="MoShiro Probe" \ + org.opencontainers.image.description="MoShiro network measurement probe for Linux" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.version="$APP_VERSION" -COPY --chown=1654:1654 --from=build /out/probe/monitor-probe /usr/local/bin/monitor-probe -COPY --chown=1654:1654 config/ /etc/monitor/ +COPY --chown=1654:1654 --from=build /out/probe/moshiro-probe /usr/local/bin/moshiro-probe +COPY --chown=1654:1654 config/ /etc/moshiro/ USER 1654:1654 -WORKDIR /etc/monitor -VOLUME ["/etc/monitor"] -ENTRYPOINT ["/usr/local/bin/monitor-probe"] -CMD ["--config", "/etc/monitor/probe.json"] +WORKDIR /etc/moshiro +VOLUME ["/etc/moshiro"] +ENTRYPOINT ["/usr/local/bin/moshiro-probe"] +CMD ["--config", "/etc/moshiro/probe.json"] diff --git a/packaging/install.sh b/packaging/install.sh index 05d7d73..569f9b5 100644 --- a/packaging/install.sh +++ b/packaging/install.sh @@ -1,18 +1,18 @@ #!/bin/sh -# Installs monitor-agent or monitor-probe from an extracted release directory (the directory this script lives in). +# Installs moshiro-agent or moshiro-probe from an extracted release directory (the directory this script lives in). # POSIX sh: runs on Debian/Ubuntu/Alpine/OpenWrt (busybox). # # ./install.sh agent --server https://panel.example.com --join --name hk-1 # ./install.sh probe [--probe-json /path/to/probe.json] # ./install.sh uninstall agent|probe [--purge] # -# Binaries go to /usr/lib/monitor (root-owned: the collector cannot rewrite its own executable); -# config and identity go to /etc/monitor (owned by the unprivileged `monitor` user). +# Binaries go to /usr/lib/moshiro (root-owned: the collector cannot rewrite its own executable); +# config and identity go to /etc/moshiro (owned by the unprivileged `moshiro` user). set -eu -PREFIX=/usr/lib/monitor -CONFIG=/etc/monitor -USER_NAME=monitor +PREFIX=/usr/lib/moshiro +CONFIG=/etc/moshiro +USER_NAME=moshiro HERE="$(cd "$(dirname "$0")" && pwd)" # busybox on OpenWrt has no `install` @@ -95,22 +95,22 @@ install_role() { if [ "$have_user" -eq 1 ]; then chown "$USER_NAME" "$CONFIG"; fi chmod 0750 "$CONFIG" - install_binary "monitor-$role" + install_binary "moshiro-$role" if [ "$role" = agent ]; then install_binary fleet-enroll; fi say "installed binaries to $PREFIX" init="$(init_system)" case "$init" in systemd) - put 0644 "$HERE/systemd/monitor-$role.service" "/etc/systemd/system/monitor-$role.service" - if [ "$have_user" -eq 0 ]; then sed -i '/^User=/d;/^Group=/d' "/etc/systemd/system/monitor-$role.service"; fi + put 0644 "$HERE/systemd/moshiro-$role.service" "/etc/systemd/system/moshiro-$role.service" + if [ "$have_user" -eq 0 ]; then sed -i '/^User=/d;/^Group=/d' "/etc/systemd/system/moshiro-$role.service"; fi systemctl daemon-reload ;; procd) [ "$role" = probe ] || die "the agent is meant for servers; on OpenWrt only the probe is supported" - put 0755 "$HERE/openwrt/monitor-probe.init" /etc/init.d/monitor-probe + put 0755 "$HERE/openwrt/moshiro-probe.init" /etc/init.d/moshiro-probe mkdir -p /lib/upgrade/keep.d - put 0644 "$HERE/openwrt/monitor.keep" /lib/upgrade/keep.d/monitor ;; - *) echo "warning: no systemd or procd found: start $PREFIX/monitor-$role --config $CONFIG/$role.json with your own supervisor" >&2 ;; + put 0644 "$HERE/openwrt/moshiro.keep" /lib/upgrade/keep.d/moshiro ;; + *) echo "warning: no systemd or procd found: start $PREFIX/moshiro-$role --config $CONFIG/$role.json with your own supervisor" >&2 ;; esac ready=0 @@ -136,18 +136,18 @@ install_role() { if [ "$ready" -eq 1 ] && [ "$no_start" -eq 0 ]; then case "$init" in - systemd) systemctl enable --now "monitor-$role" ;; - procd) /etc/init.d/monitor-probe enable; /etc/init.d/monitor-probe restart ;; + systemd) systemctl enable --now "moshiro-$role" ;; + procd) /etc/init.d/moshiro-probe enable; /etc/init.d/moshiro-probe restart ;; esac - say "monitor-$role is installed" + say "moshiro-$role is installed" else say "installed, not started yet. Next:" if [ "$role" = agent ]; then echo " $PREFIX/fleet-enroll --server --join --name --identity-dir $CONFIG (as the '$USER_NAME' user)" - echo " write $CONFIG/agent.json (see README.md), then: systemctl enable --now monitor-agent" + echo " write $CONFIG/agent.json (see README.md), then: systemctl enable --now moshiro-agent" else echo " save the panel's probe identity (POST /api/admin/probe/identity) as $CONFIG/probe.json (mode 0600, owner $USER_NAME)" - echo " then: systemctl enable --now monitor-probe (OpenWrt: /etc/init.d/monitor-probe enable && /etc/init.d/monitor-probe start)" + echo " then: systemctl enable --now moshiro-probe (OpenWrt: /etc/init.d/moshiro-probe enable && /etc/init.d/moshiro-probe start)" fi fi } @@ -157,15 +157,15 @@ uninstall() { if [ "${2:-}" = "--purge" ]; then purge=1; fi case "$(init_system)" in systemd) - systemctl disable --now "monitor-$role" 2>/dev/null || true - rm -f "/etc/systemd/system/monitor-$role.service" + systemctl disable --now "moshiro-$role" 2>/dev/null || true + rm -f "/etc/systemd/system/moshiro-$role.service" systemctl daemon-reload ;; procd) - /etc/init.d/monitor-probe stop 2>/dev/null || true - /etc/init.d/monitor-probe disable 2>/dev/null || true - rm -f /etc/init.d/monitor-probe /lib/upgrade/keep.d/monitor ;; + /etc/init.d/moshiro-probe stop 2>/dev/null || true + /etc/init.d/moshiro-probe disable 2>/dev/null || true + rm -f /etc/init.d/moshiro-probe /lib/upgrade/keep.d/moshiro ;; esac - rm -f "$PREFIX/monitor-$role" + rm -f "$PREFIX/moshiro-$role" if [ "$role" = agent ]; then rm -f "$PREFIX/fleet-enroll"; fi rmdir "$PREFIX" 2>/dev/null || true if [ "$purge" -eq 1 ]; then diff --git a/packaging/openwrt/monitor.keep b/packaging/openwrt/monitor.keep deleted file mode 100644 index 0e84b1b..0000000 --- a/packaging/openwrt/monitor.keep +++ /dev/null @@ -1 +0,0 @@ -/etc/monitor/ diff --git a/packaging/openwrt/monitor-probe.init b/packaging/openwrt/moshiro-probe.init similarity index 65% rename from packaging/openwrt/monitor-probe.init rename to packaging/openwrt/moshiro-probe.init index a1a2351..be5cddf 100644 --- a/packaging/openwrt/monitor-probe.init +++ b/packaging/openwrt/moshiro-probe.init @@ -1,16 +1,16 @@ #!/bin/sh /etc/rc.common -# procd service for monitor-probe (OpenWrt). Installed as /etc/init.d/monitor-probe. +# procd service for moshiro-probe (OpenWrt). Installed as /etc/init.d/moshiro-probe. START=95 STOP=10 USE_PROCD=1 -PROG=/usr/lib/monitor/monitor-probe -CONF=/etc/monitor/probe.json +PROG=/usr/lib/moshiro/moshiro-probe +CONF=/etc/moshiro/probe.json start_service() { [ -f "$CONF" ] || { - logger -t monitor-probe "$CONF not found: download it from the panel (POST /api/admin/probe/identity)" + logger -t moshiro-probe "$CONF not found: download it from the panel (POST /api/admin/probe/identity)" return 1 } procd_open_instance @@ -21,6 +21,6 @@ start_service() { procd_set_param stderr 1 procd_set_param limits nofile=4096 # Run unprivileged when the installer could create the user (busybox adduser may be missing) - id monitor >/dev/null 2>&1 && procd_set_param user monitor + id moshiro >/dev/null 2>&1 && procd_set_param user moshiro procd_close_instance } diff --git a/packaging/openwrt/moshiro.keep b/packaging/openwrt/moshiro.keep new file mode 100644 index 0000000..d5a4993 --- /dev/null +++ b/packaging/openwrt/moshiro.keep @@ -0,0 +1 @@ +/etc/moshiro/ diff --git a/packaging/systemd/monitor-probe-container.service b/packaging/systemd/monitor-probe-container.service deleted file mode 100644 index 015f69b..0000000 --- a/packaging/systemd/monitor-probe-container.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Monitor Probe (Podman container) -After=network-online.target -Wants=network-online.target - -[Service] -EnvironmentFile=/etc/monitor-update/probe.image -Restart=always -RestartSec=5 -TimeoutStopSec=30 -ExecStartPre=-/usr/bin/podman rm -f monitor-probe -ExecStart=/usr/bin/podman run --rm --name monitor-probe --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v monitor-probe-config:/etc/monitor -v monitor-probe-status:/etc/monitor/update-status:ro ${IMAGE} -ExecStop=/usr/bin/podman stop -t 20 monitor-probe - -[Install] -WantedBy=multi-user.target diff --git a/packaging/systemd/monitor-probe-docker.service b/packaging/systemd/monitor-probe-docker.service deleted file mode 100644 index 9d7b06a..0000000 --- a/packaging/systemd/monitor-probe-docker.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Monitor Probe (Docker container) -After=network-online.target -Wants=network-online.target - -[Service] -EnvironmentFile=/etc/monitor-update/probe.image -Restart=always -RestartSec=5 -TimeoutStopSec=30 -ExecStartPre=-/usr/bin/docker rm -f monitor-probe -ExecStart=/usr/bin/docker run --rm --name monitor-probe --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v monitor-probe-config:/etc/monitor -v monitor-probe-status:/etc/monitor/update-status:ro ${IMAGE} -ExecStop=/usr/bin/docker stop -t 20 monitor-probe - -[Install] -WantedBy=multi-user.target diff --git a/packaging/systemd/monitor-probe-rootless.service b/packaging/systemd/monitor-probe-rootless.service deleted file mode 100644 index 723fce4..0000000 --- a/packaging/systemd/monitor-probe-rootless.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Monitor Probe (rootless Podman container) -After=network-online.target -Wants=network-online.target - -[Service] -EnvironmentFile=%h/.config/monitor/probe.image -Restart=always -RestartSec=5 -TimeoutStopSec=30 -ExecStartPre=-/usr/bin/podman rm -f monitor-probe -ExecStart=/usr/bin/podman run --rm --name monitor-probe --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v monitor-probe-config:/etc/monitor -v monitor-probe-status:/etc/monitor/update-status:ro ${IMAGE} -ExecStop=/usr/bin/podman stop -t 20 monitor-probe - -[Install] -WantedBy=default.target diff --git a/packaging/systemd/monitor-agent-docker.service b/packaging/systemd/moshiro-agent-container.service similarity index 57% rename from packaging/systemd/monitor-agent-docker.service rename to packaging/systemd/moshiro-agent-container.service index c24724b..d016713 100644 --- a/packaging/systemd/monitor-agent-docker.service +++ b/packaging/systemd/moshiro-agent-container.service @@ -1,16 +1,16 @@ [Unit] -Description=Monitor Agent (Docker container) +Description=MoShiro Agent (Podman container) After=network-online.target Wants=network-online.target [Service] -EnvironmentFile=/etc/monitor-update/agent.image +EnvironmentFile=/etc/moshiro-update/agent.image Restart=always RestartSec=5 TimeoutStopSec=30 -ExecStartPre=-/usr/bin/docker rm -f monitor-agent -ExecStart=/usr/bin/docker run --rm --name monitor-agent --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root -v monitor-agent-config:/etc/monitor -v monitor-agent-status:/etc/monitor/update-status:ro ${IMAGE} -ExecStop=/usr/bin/docker stop -t 20 monitor-agent +ExecStartPre=-/usr/bin/podman rm -f moshiro-agent +ExecStart=/usr/bin/podman run --rm --name moshiro-agent --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root -v moshiro-agent-config:/etc/moshiro -v moshiro-agent-status:/etc/moshiro/update-status:ro ${IMAGE} +ExecStop=/usr/bin/podman stop -t 20 moshiro-agent [Install] WantedBy=multi-user.target diff --git a/packaging/systemd/monitor-agent-container.service b/packaging/systemd/moshiro-agent-docker.service similarity index 57% rename from packaging/systemd/monitor-agent-container.service rename to packaging/systemd/moshiro-agent-docker.service index 5f285e1..657c540 100644 --- a/packaging/systemd/monitor-agent-container.service +++ b/packaging/systemd/moshiro-agent-docker.service @@ -1,16 +1,16 @@ [Unit] -Description=Monitor Agent (Podman container) +Description=MoShiro Agent (Docker container) After=network-online.target Wants=network-online.target [Service] -EnvironmentFile=/etc/monitor-update/agent.image +EnvironmentFile=/etc/moshiro-update/agent.image Restart=always RestartSec=5 TimeoutStopSec=30 -ExecStartPre=-/usr/bin/podman rm -f monitor-agent -ExecStart=/usr/bin/podman run --rm --name monitor-agent --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root -v monitor-agent-config:/etc/monitor -v monitor-agent-status:/etc/monitor/update-status:ro ${IMAGE} -ExecStop=/usr/bin/podman stop -t 20 monitor-agent +ExecStartPre=-/usr/bin/docker rm -f moshiro-agent +ExecStart=/usr/bin/docker run --rm --name moshiro-agent --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root -v moshiro-agent-config:/etc/moshiro -v moshiro-agent-status:/etc/moshiro/update-status:ro ${IMAGE} +ExecStop=/usr/bin/docker stop -t 20 moshiro-agent [Install] WantedBy=multi-user.target diff --git a/packaging/systemd/monitor-agent-rootless.service b/packaging/systemd/moshiro-agent-rootless.service similarity index 56% rename from packaging/systemd/monitor-agent-rootless.service rename to packaging/systemd/moshiro-agent-rootless.service index 5278a8d..9a53d9e 100644 --- a/packaging/systemd/monitor-agent-rootless.service +++ b/packaging/systemd/moshiro-agent-rootless.service @@ -1,16 +1,16 @@ [Unit] -Description=Monitor Agent (rootless Podman container) +Description=MoShiro Agent (rootless Podman container) After=network-online.target Wants=network-online.target [Service] -EnvironmentFile=%h/.config/monitor/agent.image +EnvironmentFile=%h/.config/moshiro/agent.image Restart=always RestartSec=5 TimeoutStopSec=30 -ExecStartPre=-/usr/bin/podman rm -f monitor-agent -ExecStart=/usr/bin/podman run --rm --name monitor-agent --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root -v monitor-agent-config:/etc/monitor -v monitor-agent-status:/etc/monitor/update-status:ro ${IMAGE} -ExecStop=/usr/bin/podman stop -t 20 monitor-agent +ExecStartPre=-/usr/bin/podman rm -f moshiro-agent +ExecStart=/usr/bin/podman run --rm --name moshiro-agent --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /:/host/root:ro -e HOST_PROC=/host/proc -e HOST_SYS=/host/sys -e HOST_ROOT=/host/root -v moshiro-agent-config:/etc/moshiro -v moshiro-agent-status:/etc/moshiro/update-status:ro ${IMAGE} +ExecStop=/usr/bin/podman stop -t 20 moshiro-agent [Install] WantedBy=default.target diff --git a/packaging/systemd/monitor-agent.service b/packaging/systemd/moshiro-agent.service similarity index 84% rename from packaging/systemd/monitor-agent.service rename to packaging/systemd/moshiro-agent.service index ebcc774..e6e9ea5 100644 --- a/packaging/systemd/monitor-agent.service +++ b/packaging/systemd/moshiro-agent.service @@ -1,18 +1,18 @@ [Unit] -Description=Monitor agent (network line quality collector) +Description=MoShiro agent (network line quality collector) After=network-online.target Wants=network-online.target [Service] Type=exec -User=monitor -Group=monitor -ExecStart=/usr/lib/monitor/monitor-agent --config /etc/monitor/agent.json +User=moshiro +Group=moshiro +ExecStart=/usr/lib/moshiro/moshiro-agent --config /etc/moshiro/agent.json Restart=always RestartSec=5 # The agent is a pure collector: it reads its config and identity, never writes files, never starts processes. -ReadOnlyPaths=/etc/monitor +ReadOnlyPaths=/etc/moshiro NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes diff --git a/packaging/systemd/moshiro-probe-container.service b/packaging/systemd/moshiro-probe-container.service new file mode 100644 index 0000000..a0aea4b --- /dev/null +++ b/packaging/systemd/moshiro-probe-container.service @@ -0,0 +1,16 @@ +[Unit] +Description=MoShiro Probe (Podman container) +After=network-online.target +Wants=network-online.target + +[Service] +EnvironmentFile=/etc/moshiro-update/probe.image +Restart=always +RestartSec=5 +TimeoutStopSec=30 +ExecStartPre=-/usr/bin/podman rm -f moshiro-probe +ExecStart=/usr/bin/podman run --rm --name moshiro-probe --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v moshiro-probe-config:/etc/moshiro -v moshiro-probe-status:/etc/moshiro/update-status:ro ${IMAGE} +ExecStop=/usr/bin/podman stop -t 20 moshiro-probe + +[Install] +WantedBy=multi-user.target diff --git a/packaging/systemd/moshiro-probe-docker.service b/packaging/systemd/moshiro-probe-docker.service new file mode 100644 index 0000000..c24d6c1 --- /dev/null +++ b/packaging/systemd/moshiro-probe-docker.service @@ -0,0 +1,16 @@ +[Unit] +Description=MoShiro Probe (Docker container) +After=network-online.target +Wants=network-online.target + +[Service] +EnvironmentFile=/etc/moshiro-update/probe.image +Restart=always +RestartSec=5 +TimeoutStopSec=30 +ExecStartPre=-/usr/bin/docker rm -f moshiro-probe +ExecStart=/usr/bin/docker run --rm --name moshiro-probe --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v moshiro-probe-config:/etc/moshiro -v moshiro-probe-status:/etc/moshiro/update-status:ro ${IMAGE} +ExecStop=/usr/bin/docker stop -t 20 moshiro-probe + +[Install] +WantedBy=multi-user.target diff --git a/packaging/systemd/moshiro-probe-rootless.service b/packaging/systemd/moshiro-probe-rootless.service new file mode 100644 index 0000000..186966e --- /dev/null +++ b/packaging/systemd/moshiro-probe-rootless.service @@ -0,0 +1,16 @@ +[Unit] +Description=MoShiro Probe (rootless Podman container) +After=network-online.target +Wants=network-online.target + +[Service] +EnvironmentFile=%h/.config/moshiro/probe.image +Restart=always +RestartSec=5 +TimeoutStopSec=30 +ExecStartPre=-/usr/bin/podman rm -f moshiro-probe +ExecStart=/usr/bin/podman run --rm --name moshiro-probe --network host --read-only --security-opt no-new-privileges --security-opt label=disable --cap-drop all --tmpfs /tmp:rw,noexec,nosuid,size=64m,mode=1777 -v /etc/os-release:/etc/os-release:ro -v moshiro-probe-config:/etc/moshiro -v moshiro-probe-status:/etc/moshiro/update-status:ro ${IMAGE} +ExecStop=/usr/bin/podman stop -t 20 moshiro-probe + +[Install] +WantedBy=default.target diff --git a/packaging/systemd/monitor-probe.service b/packaging/systemd/moshiro-probe.service similarity index 83% rename from packaging/systemd/monitor-probe.service rename to packaging/systemd/moshiro-probe.service index db90db4..aaf9141 100644 --- a/packaging/systemd/monitor-probe.service +++ b/packaging/systemd/moshiro-probe.service @@ -1,19 +1,19 @@ [Unit] -Description=Monitor probe (network line quality measurement origin) +Description=MoShiro probe (network line quality measurement origin) After=network-online.target Wants=network-online.target [Service] Type=exec -User=monitor -Group=monitor -ExecStart=/usr/lib/monitor/monitor-probe --config /etc/monitor/probe.json +User=moshiro +Group=moshiro +ExecStart=/usr/lib/moshiro/moshiro-probe --config /etc/moshiro/probe.json Restart=always RestartSec=5 # The probe is a pure collector: it never starts processes and writes no measurement data. # The only file the probe writes is agents.json (the latest agent list) next to probe.json. -ReadWritePaths=/etc/monitor +ReadWritePaths=/etc/moshiro NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes diff --git a/packaging/update/install-updater.sh b/packaging/update/install-updater.sh index 0752b7b..bf74bb9 100644 --- a/packaging/update/install-updater.sh +++ b/packaging/update/install-updater.sh @@ -30,8 +30,8 @@ case "$POLICY" in auto|manual) ;; *) echo "--policy must be auto or manual" >&2; case "$ENGINE" in podman|docker) ;; *) echo "--engine must be podman or docker" >&2; exit 2 ;; esac if [ -n "$DATA_VOLUME" ]; then [ -z "$DATA_DIR" ] || { echo "use --volume or --data-dir, not both" >&2; exit 2; } - [ -n "$HELPER_IMAGE" ] || { echo "--volume needs --helper-image (an already pulled Monitor image)" >&2; exit 2; } - STATUS_VOLUME="${STATUS_VOLUME:-monitor-$COMPONENT-status}" + [ -n "$HELPER_IMAGE" ] || { echo "--volume needs --helper-image (an already pulled MoShiro image)" >&2; exit 2; } + STATUS_VOLUME="${STATUS_VOLUME:-moshiro-$COMPONENT-status}" "$ENGINE" image inspect "$HELPER_IMAGE" >/dev/null "$ENGINE" volume inspect "$DATA_VOLUME" >/dev/null 2>&1 || "$ENGINE" volume create "$DATA_VOLUME" >/dev/null "$ENGINE" volume inspect "$STATUS_VOLUME" >/dev/null 2>&1 || "$ENGINE" volume create "$STATUS_VOLUME" >/dev/null @@ -42,15 +42,15 @@ fi if [ "$SERVICE_SCOPE" = user ]; then [ "$(id -u)" -ne 0 ] || { echo "--user must run as the rootless Podman user, without sudo" >&2; exit 1; } [ "$ENGINE" = podman ] || { echo "--user currently supports rootless Podman; Docker uses system services" >&2; exit 2; } - CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/monitor" - STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/monitor-update" + CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/moshiro" + STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/moshiro-update" UNIT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user" BIN_DIR="$HOME/.local/bin" TARGET=default.target else [ "$(id -u)" -eq 0 ] || { echo "system installation needs root; use --user for rootless Podman" >&2; exit 1; } - CONF_DIR=/etc/monitor-update - STATE_DIR=/var/lib/monitor-update + CONF_DIR=/etc/moshiro-update + STATE_DIR=/var/lib/moshiro-update UNIT_DIR=/etc/systemd/system BIN_DIR=/usr/local/sbin TARGET=multi-user.target @@ -64,8 +64,8 @@ unit_escape() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; } HERE="$(cd "$(dirname "$0")" && pwd)" install -d -m 755 "$CONF_DIR" "$STATE_DIR" "$UNIT_DIR" "$BIN_DIR" -BIN="$BIN_DIR/monitor-update" -install -m 755 "$HERE/monitor-update.sh" "$BIN" +BIN="$BIN_DIR/moshiro-update" +install -m 755 "$HERE/moshiro-update.sh" "$BIN" install -m 644 "$HERE/release-key.pub.pem" "$CONF_DIR/release-key.pub.pem" if [ -n "$DATA_VOLUME" ]; then : # Named volumes are initialized by the image; no host directory or chown needed. @@ -92,49 +92,49 @@ PUBKEY="$CONF_DIR/release-key.pub.pem" chmod 600 "$CONF_DIR/$COMPONENT.conf" if [ -n "$DATA_VOLUME" ]; then - cat > "$UNIT_DIR/monitor-update-$COMPONENT.service" < "$UNIT_DIR/moshiro-update-$COMPONENT.service" < "$UNIT_DIR/monitor-update-$COMPONENT.timer" < "$UNIT_DIR/moshiro-update-$COMPONENT.timer" < "$UNIT_DIR/monitor-update-$action-$COMPONENT.service" < "$UNIT_DIR/moshiro-update-$action-$COMPONENT.service" < "$UNIT_DIR/monitor-update-$action-$COMPONENT.path" < "$UNIT_DIR/moshiro-update-$action-$COMPONENT.path" </dev/null 2>&1 || true - service_ctl enable --now "monitor-update-$COMPONENT.timer" + service_ctl disable --now "moshiro-update-check-$COMPONENT.path" "moshiro-update-apply-$COMPONENT.path" >/dev/null 2>&1 || true + service_ctl enable --now "moshiro-update-$COMPONENT.timer" else - service_ctl disable --now "monitor-update-$COMPONENT.timer" >/dev/null 2>&1 || true - service_ctl enable --now "monitor-update-check-$COMPONENT.path" "monitor-update-apply-$COMPONENT.path" + service_ctl disable --now "moshiro-update-$COMPONENT.timer" >/dev/null 2>&1 || true + service_ctl enable --now "moshiro-update-check-$COMPONENT.path" "moshiro-update-apply-$COMPONENT.path" fi fi echo "updater installed for $COMPONENT (engine: $ENGINE, scope: $SERVICE_SCOPE, policy: $POLICY)" diff --git a/packaging/update/monitor-update.sh b/packaging/update/moshiro-update.sh similarity index 90% rename from packaging/update/monitor-update.sh rename to packaging/update/moshiro-update.sh index 309038c..6ea503a 100644 --- a/packaging/update/monitor-update.sh +++ b/packaging/update/moshiro-update.sh @@ -1,10 +1,10 @@ #!/bin/sh -# Monitor 容器节点(Agent、Probe)的宿主机更新器。用法:monitor-update <组件> 组件 = agent 或 probe +# MoShiro 容器节点(Agent、Probe)的宿主机更新器。用法:moshiro-update <组件> 组件 = agent 或 probe # # 信任模型(防篡改、防中间人): # - 只接受用发布私钥签名的清单(ECDSA P-256 / SHA-256,对清单文件的原始字节签名);公钥放在本机,面板和 Registry 都改不了它。 # - 清单里写的是镜像的 digest:拉取时用 repo@sha256:…,内容寻址,Registry 或链路上的人换不出另一个镜像。 -# - 镜像仓库、组件、服务名都来自本机的配置(/etc/monitor-update/<组件>.conf),清单里写什么都不会改变拉哪个仓库。 +# - 镜像仓库、组件、服务名都来自本机的配置(/etc/moshiro-update/<组件>.conf),清单里写什么都不会改变拉哪个仓库。 # - 清单的 seq 必须比已安装的大(防止旧清单被重放回滚)。 # - 业务进程(Agent、Probe)只能往数据目录里写「清单」和「请求」文件,更新由这个宿主机脚本完成,业务进程不执行任何命令。 # Shell snippets below execute inside helper containers. @@ -13,27 +13,27 @@ set -eu CMD="${1:-}" COMPONENT="${2:-}" -case "$CMD" in check|apply) ;; *) echo "usage: monitor-update " >&2; exit 2 ;; esac +case "$CMD" in check|apply) ;; *) echo "usage: moshiro-update " >&2; exit 2 ;; esac case "$COMPONENT" in agent|probe) ;; *) echo "component must be agent or probe" >&2; exit 2 ;; esac if [ "$(id -u)" -eq 0 ]; then - DEFAULT_CONF_DIR=/etc/monitor-update + DEFAULT_CONF_DIR=/etc/moshiro-update else - DEFAULT_CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/monitor" + DEFAULT_CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/moshiro" fi -CONF="${MONITOR_UPDATE_CONF_DIR:-$DEFAULT_CONF_DIR}/$COMPONENT.conf" +CONF="${MOSHIRO_UPDATE_CONF_DIR:-$DEFAULT_CONF_DIR}/$COMPONENT.conf" [ -r "$CONF" ] || { echo "missing $CONF" >&2; exit 2; } -# 配置:DATA_DIR(容器里 /etc/monitor 对应的宿主机目录)、IMAGE_REPO、UNIT、PUBKEY、POLICY(auto|manual)、AUTHFILE(可选)、 +# 配置:DATA_DIR(容器里 /etc/moshiro 对应的宿主机目录)、IMAGE_REPO、UNIT、PUBKEY、POLICY(auto|manual)、AUTHFILE(可选)、 # STATE_DIR(已安装版本的记录)、ENV_FILE(容器单元读取的 IMAGE=… 文件) # shellcheck disable=SC1090 . "$CONF" : "${IMAGE_REPO:?}" "${UNIT:?}" "${PUBKEY:?}" "${ENV_FILE:?}" POLICY="${POLICY:-manual}" -STATE_DIR="${STATE_DIR:-/var/lib/monitor-update}" +STATE_DIR="${STATE_DIR:-/var/lib/moshiro-update}" ENGINE="${ENGINE:-podman}" SERVICE_SCOPE="${SERVICE_SCOPE:-system}" DATA_VOLUME="${DATA_VOLUME:-}" -STATUS_VOLUME="${STATUS_VOLUME:-monitor-$COMPONENT-status}" +STATUS_VOLUME="${STATUS_VOLUME:-moshiro-$COMPONENT-status}" case "$ENGINE" in podman|docker) ;; *) echo "unsupported engine: $ENGINE" >&2; exit 2 ;; esac case "$SERVICE_SCOPE" in system|user) ;; *) echo "unsupported service scope: $SERVICE_SCOPE" >&2; exit 2 ;; esac service_ctl() { @@ -84,8 +84,8 @@ fetch_offer() { for name in offer.manifest offer.sig; do if [ -n "$DATA_VOLUME" ]; then "$ENGINE" run --pull=never --rm --network none --read-only --cap-drop ALL --security-opt no-new-privileges \ - -v "$DATA_VOLUME:/etc/monitor:ro" --entrypoint /bin/sh "$HELPER_IMAGE" \ - -c 'f="/etc/monitor/update/$1"; if [ -f "$f" ] && [ ! -L "$f" ]; then head -c 8192 "$f"; fi' sh "$name" > "$WORK/$name" + -v "$DATA_VOLUME:/etc/moshiro:ro" --entrypoint /bin/sh "$HELPER_IMAGE" \ + -c 'f="/etc/moshiro/update/$1"; if [ -f "$f" ] && [ ! -L "$f" ]; then head -c 8192 "$f"; fi' sh "$name" > "$WORK/$name" continue fi f="$UPDATE_DIR/$name" @@ -163,7 +163,7 @@ do_check() { do_apply() { if [ -n "$DATA_VOLUME" ]; then "$ENGINE" run --pull=never --rm --network none --read-only --cap-drop ALL --security-opt no-new-privileges \ - -v "$DATA_VOLUME:/etc/monitor" --entrypoint /bin/sh "$HELPER_IMAGE" -c 'rm -f /etc/monitor/update/apply.request' + -v "$DATA_VOLUME:/etc/moshiro" --entrypoint /bin/sh "$HELPER_IMAGE" -c 'rm -f /etc/moshiro/update/apply.request' else rm -f "$UPDATE_DIR/apply.request" fi @@ -200,8 +200,8 @@ case "$CMD" in check) if [ -n "$DATA_VOLUME" ] && [ "$POLICY" != auto ]; then requested="$($ENGINE run --pull=never --rm --network none --read-only --cap-drop ALL --security-opt no-new-privileges \ - -v "$DATA_VOLUME:/etc/monitor:ro" --entrypoint /bin/sh "$HELPER_IMAGE" \ - -c '[ ! -f /etc/monitor/update/apply.request ] || printf yes')" + -v "$DATA_VOLUME:/etc/moshiro:ro" --entrypoint /bin/sh "$HELPER_IMAGE" \ + -c '[ ! -f /etc/moshiro/update/apply.request ] || printf yes')" if [ "$requested" = yes ]; then do_apply; else do_check; fi else do_check diff --git a/scripts/package.sh b/scripts/package.sh index 8ab721b..d5e39ba 100644 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# 打包:scripts/package.sh → backend/artifacts/packages/monitor---.tar.gz(附 .sha256) +# 打包:scripts/package.sh → backend/artifacts/packages/moshiro---.tar.gz(附 .sha256) # 内容:节点二进制(agent 还带 fleet-enroll)、systemd 单元、OpenWrt procd 脚本、install.sh。 # 发布产物要先用 scripts/publish.sh 生成(agent 需要 agent 和 fleet-enroll 两个)。 set -euo pipefail @@ -7,14 +7,14 @@ role="${1:?usage: package.sh }" rid="${2:?usage: package.sh }" root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" version="$(sed -n 's#.*\(.*\).*#\1#p' "$root/Directory.Build.props" | head -1)" -name="monitor-$role-$version-$rid" +name="moshiro-$role-$version-$rid" stage="$root/backend/artifacts/packages/stage/$name" rm -rf "$stage" mkdir -p "$stage/systemd" -cp "$root/backend/artifacts/publish/$role/$rid/monitor-$role" "$stage/" +cp "$root/backend/artifacts/publish/$role/$rid/moshiro-$role" "$stage/" cp "$root/packaging/install.sh" "$stage/" -cp "$root/packaging/systemd/monitor-$role.service" "$stage/systemd/" +cp "$root/packaging/systemd/moshiro-$role.service" "$stage/systemd/" cp "$root/$role/README.md" "$stage/README.md" if [ "$role" = agent ]; then cp "$root/backend/artifacts/publish/fleet-enroll/$rid/fleet-enroll" "$stage/" diff --git a/scripts/publish.sh b/scripts/publish.sh index 963dc0c..f772815 100644 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -9,18 +9,18 @@ rid="${2:?usage: publish.sh }" root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" case "$project" in - agent) csproj="backend/agent/src/Monitor.Agent" ;; - probe) csproj="backend/probe/src/Monitor.Probe" ;; - backend) csproj="backend/src/Monitor.Backend" ;; + agent) csproj="backend/agent/src/MoShiro.Agent" ;; + probe) csproj="backend/probe/src/MoShiro.Probe" ;; + backend) csproj="backend/src/MoShiro.Backend" ;; fleet-enroll) csproj="backend/fleet/tools/fleet-enroll" ;; *) echo "unknown project: $project" >&2; exit 2 ;; esac # 构建信息(程序里显示的 tag、提交、构建时间):没在 git 仓库里就留空 -export MONITOR_TAG="${MONITOR_TAG:-$(git -C "$root" describe --tags --always --dirty 2>/dev/null || true)}" -export MONITOR_COMMIT="${MONITOR_COMMIT:-$(git -C "$root" rev-parse --short=10 HEAD 2>/dev/null || true)}" -export MONITOR_BUILT_AT="${MONITOR_BUILT_AT:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" -build_env=(-e MONITOR_TAG -e MONITOR_COMMIT -e MONITOR_BUILT_AT) +export MOSHIRO_TAG="${MOSHIRO_TAG:-$(git -C "$root" describe --tags --always --dirty 2>/dev/null || true)}" +export MOSHIRO_COMMIT="${MOSHIRO_COMMIT:-$(git -C "$root" rev-parse --short=10 HEAD 2>/dev/null || true)}" +export MOSHIRO_BUILT_AT="${MOSHIRO_BUILT_AT:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}" +build_env=(-e MOSHIRO_TAG -e MOSHIRO_COMMIT -e MOSHIRO_BUILT_AT) out="backend/artifacts/publish/$project/$rid" publish_options=() @@ -32,11 +32,11 @@ args=(publish "$csproj" -c Release -r "$rid" -p:PublishAot=true -p:DebugType=non case "$rid" in linux-musl-x64) # SDK 镜像的版本可以比 global.json 新(rollForward = latestFeature) - docker run --rm "${build_env[@]}" -v "$root:/src" -v monitor-nuget:/root/.nuget -v "monitor-artifacts-$rid:/artifacts" -w /src \ + docker run --rm "${build_env[@]}" -v "$root:/src" -v moshiro-nuget:/root/.nuget -v "moshiro-artifacts-$rid:/artifacts" -w /src \ mcr.microsoft.com/dotnet/sdk:10.0-alpine-aot dotnet "${args[@]}" ;; linux-x64) - docker run --rm "${build_env[@]}" -v "$root:/src" -v monitor-nuget:/root/.nuget -v "monitor-artifacts-$rid:/artifacts" -w /src mcr.microsoft.com/dotnet/sdk:10.0-noble-aot dotnet "${args[@]}" + docker run --rm "${build_env[@]}" -v "$root:/src" -v moshiro-nuget:/root/.nuget -v "moshiro-artifacts-$rid:/artifacts" -w /src mcr.microsoft.com/dotnet/sdk:10.0-noble-aot dotnet "${args[@]}" ;; linux-musl-arm64|linux-arm64) echo "$rid needs an arm64 Alpine builder (native or qemu): run this script on an arm64 host or in CI" >&2 diff --git a/scripts/smoke-linux.sh b/scripts/smoke-linux.sh index 0c64f29..d026590 100644 --- a/scripts/smoke-linux.sh +++ b/scripts/smoke-linux.sh @@ -3,7 +3,7 @@ # # RID=linux-musl-x64 IMAGE=alpine:3.22 (默认) # RID=linux-x64 IMAGE=debian:bookworm-slim -# MODE=install 用打包产物里的 install.sh 安装(agent 注册、probe 身份、以 monitor 用户运行),而不是直接运行二进制 +# MODE=install 用打包产物里的 install.sh 安装(agent 注册、probe 身份、以 moshiro 用户运行),而不是直接运行二进制 # # 用法(在仓库根目录;Windows 下先 export MSYS_NO_PATHCONV=1;MODE=install 需要先运行 scripts/package.sh): # docker run --rm -e RID -e MODE -v "$PWD/backend/artifacts:/art:ro" -v "$PWD/scripts/smoke-linux.sh:/smoke.sh:ro" $IMAGE sh /smoke.sh [秒数] @@ -23,9 +23,9 @@ work=/tmp/smoke mkdir -p "$work/data" "$work/probe" base=http://127.0.0.1:18080 -export ASPNETCORE_URLS="$base" Monitor__DataDirectory="$work/data" Monitor__InitialAdminUser=admin \ - Monitor__InitialAdminPassword="correct horse battery" Monitor__SecureCookies=false -(cd "$pub/backend/$rid" && ./monitor-backend >"$work/backend.log" 2>&1 &) +export ASPNETCORE_URLS="$base" MoShiro__DataDirectory="$work/data" MoShiro__InitialAdminUser=admin \ + MoShiro__InitialAdminPassword="correct horse battery" MoShiro__SecureCookies=false +(cd "$pub/backend/$rid" && ./moshiro-backend >"$work/backend.log" 2>&1 &) for i in $(seq 1 60); do curl -fs "$base/healthz" >/dev/null 2>&1 && break; sleep 1; done curl -fs "$base/healthz" >/dev/null || { cat "$work/backend.log"; echo "backend did not start"; exit 1; } echo "backend up" @@ -38,27 +38,27 @@ auth="Authorization: Bearer $token" join=$(curl -fs -X POST "$base/api/admin/join-tokens" -H "$auth" -H 'Content-Type: application/json' -d '{"auto_approve":true,"max_uses":1,"expires_in_hours":1}' | jq -r .plaintext) if [ "$mode" = install ]; then - # 安装到 /usr/lib/monitor 和 /etc/monitor,以 monitor 用户运行(没有 systemd:自己启动) - mkdir -p /tmp/pkg && for role in agent probe; do tar -C /tmp/pkg -xzf /art/packages/monitor-$role-*-$rid.tar.gz; done - (cd /tmp/pkg/monitor-agent-* && ./install.sh agent --server "$base" --join "$join" --name smoke-agent --no-start) - ls -l /usr/lib/monitor /etc/monitor - run_as() { su -s /bin/sh monitor -c "exec $*"; } - run_as /usr/lib/monitor/monitor-agent --config /etc/monitor/agent.json >"$work/agent.log" 2>&1 & + # 安装到 /usr/lib/moshiro 和 /etc/moshiro,以 moshiro 用户运行(没有 systemd:自己启动) + mkdir -p /tmp/pkg && for role in agent probe; do tar -C /tmp/pkg -xzf /art/packages/moshiro-$role-*-$rid.tar.gz; done + (cd /tmp/pkg/moshiro-agent-* && ./install.sh agent --server "$base" --join "$join" --name smoke-agent --no-start) + ls -l /usr/lib/moshiro /etc/moshiro + run_as() { su -s /bin/sh moshiro -c "exec $*"; } + run_as /usr/lib/moshiro/moshiro-agent --config /etc/moshiro/agent.json >"$work/agent.log" 2>&1 & agent_pid=$! sleep 3 curl -fs -X POST "$base/api/admin/probe/identity" -H "$auth" -H 'Content-Type: application/json' -d '{}' >"$work/probe/probe.json" - (cd /tmp/pkg/monitor-probe-* && ./install.sh probe --probe-json "$work/probe/probe.json" --no-start) - run_as /usr/lib/monitor/monitor-probe --config /etc/monitor/probe.json >"$work/probe.log" 2>&1 & + (cd /tmp/pkg/moshiro-probe-* && ./install.sh probe --probe-json "$work/probe/probe.json" --no-start) + run_as /usr/lib/moshiro/moshiro-probe --config /etc/moshiro/probe.json >"$work/probe.log" 2>&1 & probe_pid=$! else mkdir -p "$work/agent-id" "$pub/fleet-enroll/$rid/fleet-enroll" --server "$base" --join "$join" --name smoke-agent --identity-dir "$work/agent-id" printf '{"fleet":{"server":"%s","identity_dir":"%s"},"listen":{"bind":"127.0.0.1"}}' "$base" "$work/agent-id" >"$work/agent.json" - "$pub/agent/$rid/monitor-agent" --config "$work/agent.json" >"$work/agent.log" 2>&1 & + "$pub/agent/$rid/moshiro-agent" --config "$work/agent.json" >"$work/agent.log" 2>&1 & agent_pid=$! sleep 3 curl -fs -X POST "$base/api/admin/probe/identity" -H "$auth" -H 'Content-Type: application/json' -d '{}' >"$work/probe/probe.json" - "$pub/probe/$rid/monitor-probe" --config "$work/probe/probe.json" >"$work/probe.log" 2>&1 & + "$pub/probe/$rid/moshiro-probe" --config "$work/probe/probe.json" >"$work/probe.log" 2>&1 & probe_pid=$! fi diff --git a/scripts/smoke-systemd.sh b/scripts/smoke-systemd.sh index 6954109..65c222a 100644 --- a/scripts/smoke-systemd.sh +++ b/scripts/smoke-systemd.sh @@ -6,7 +6,7 @@ apt-get update >/dev/null && apt-get install -y --no-install-recommends curl jq base=http://127.0.0.1:18080 mkdir -p /tmp/data cd /art/publish/backend/linux-x64 -ASPNETCORE_URLS=$base Monitor__DataDirectory=/tmp/data Monitor__InitialAdminUser=admin Monitor__InitialAdminPassword="correct horse battery" Monitor__SecureCookies=false nohup ./monitor-backend >/tmp/backend.log 2>&1 & +ASPNETCORE_URLS=$base MoShiro__DataDirectory=/tmp/data MoShiro__InitialAdminUser=admin MoShiro__InitialAdminPassword="correct horse battery" MoShiro__SecureCookies=false nohup ./moshiro-backend >/tmp/backend.log 2>&1 & for i in $(seq 1 60); do curl -fs $base/healthz >/dev/null 2>&1 && break; sleep 1; done login=$(curl -si -X POST $base/api/admin/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"correct horse battery"}') cookie=$(echo "$login" | grep -i '^set-cookie' | head -1 | sed 's/^[^:]*: //; s/;.*//' | tr -d '\r') @@ -14,18 +14,18 @@ csrf=$(echo "$login" | tail -1 | jq -r .csrf_token) token=$(curl -fs -X POST $base/api/admin/api-tokens -H "Cookie: $cookie" -H "X-CSRF-Token: $csrf" -H 'Content-Type: application/json' -d '{"name":"t","scope":"admin"}' | jq -r .plaintext) auth="Authorization: Bearer $token" join=$(curl -fs -X POST $base/api/admin/join-tokens -H "$auth" -H 'Content-Type: application/json' -d '{"auto_approve":true,"max_uses":1,"expires_in_hours":1}' | jq -r .plaintext) -mkdir -p /tmp/pkg; for r in agent probe; do tar -C /tmp/pkg -xzf /art/packages/monitor-$r-*-linux-x64.tar.gz; done -(cd /tmp/pkg/monitor-agent-* && ./install.sh agent --server $base --join "$join" --name sysd-agent) +mkdir -p /tmp/pkg; for r in agent probe; do tar -C /tmp/pkg -xzf /art/packages/moshiro-$r-*-linux-x64.tar.gz; done +(cd /tmp/pkg/moshiro-agent-* && ./install.sh agent --server $base --join "$join" --name sysd-agent) sleep 3 curl -fs -X POST $base/api/admin/probe/identity -H "$auth" -H 'Content-Type: application/json' -d '{}' >/tmp/probe.json -(cd /tmp/pkg/monitor-probe-* && ./install.sh probe --probe-json /tmp/probe.json) +(cd /tmp/pkg/moshiro-probe-* && ./install.sh probe --probe-json /tmp/probe.json) sleep 25 -systemctl is-active monitor-agent monitor-probe +systemctl is-active moshiro-agent moshiro-probe echo "agents: $(curl -fs $base/api/admin/agents -H "$auth" | jq -c '[.items[] | {name, status, probe_connected}]')" echo "chunks: $(curl -fs $base/api/admin/raw/chunks -H "$auth" | jq -c '[.items[].kind] | length')" -systemctl show monitor-agent -p MemoryCurrent -p NRestarts | tr '\n' ' '; systemctl show monitor-probe -p MemoryCurrent -p NRestarts | tr '\n' ' '; echo -ps -o user,rss,comm -C monitor-agent,monitor-probe -journalctl -u monitor-agent -u monitor-probe --no-pager | tail -8 -echo "--- sandbox check: agent cannot write /etc/monitor"; nsenter -t $(systemctl show monitor-agent -p MainPID --value) -m sh -c 'touch /etc/monitor/x 2>&1; ls /proc/self/status >/dev/null' 2>&1 | head -2 -grep -E "NoNewPrivs|CapEff|Seccomp:" /proc/$(systemctl show monitor-agent -p MainPID --value)/status -grep -E "NoNewPrivs|CapEff|Seccomp:" /proc/$(systemctl show monitor-probe -p MainPID --value)/status +systemctl show moshiro-agent -p MemoryCurrent -p NRestarts | tr '\n' ' '; systemctl show moshiro-probe -p MemoryCurrent -p NRestarts | tr '\n' ' '; echo +ps -o user,rss,comm -C moshiro-agent,moshiro-probe +journalctl -u moshiro-agent -u moshiro-probe --no-pager | tail -8 +echo "--- sandbox check: agent cannot write /etc/moshiro"; nsenter -t $(systemctl show moshiro-agent -p MainPID --value) -m sh -c 'touch /etc/moshiro/x 2>&1; ls /proc/self/status >/dev/null' 2>&1 | head -2 +grep -E "NoNewPrivs|CapEff|Seccomp:" /proc/$(systemctl show moshiro-agent -p MainPID --value)/status +grep -E "NoNewPrivs|CapEff|Seccomp:" /proc/$(systemctl show moshiro-probe -p MainPID --value)/status diff --git a/scripts/smoke-wsl.py b/scripts/smoke-wsl.py index 10c6d6b..029bb4f 100644 --- a/scripts/smoke-wsl.py +++ b/scripts/smoke-wsl.py @@ -1,8 +1,8 @@ """跨机器的真实链路验证:Windows 上的 Probe(NativeAOT exe)→ WSL2 里的 Agent 和 Backend(真实的虚拟网络、不同的单调时钟原点)。 前提:WSL 里有 Debian(root)、tc(netem 注入丢包/延迟/黑洞用);已经用 scripts/publish.sh 发布了 linux-x64 的 agent、backend、fleet-enroll, -并且 Windows 上发布了 probe(dotnet publish backend/probe/src/Monitor.Probe -c Release -r win-x64 -p:PublishAot=true -o backend/artifacts/aot-probe)。 -把 linux-x64 的产物复制到 WSL 的 /root/mon/,在 WSL 里用 setsid -f nohup 启动 monitor-backend(监听 0.0.0.0:29181),再运行本脚本。 +并且 Windows 上发布了 probe(dotnet publish backend/probe/src/MoShiro.Probe -c Release -r win-x64 -p:PublishAot=true -o backend/artifacts/aot-probe)。 +把 linux-x64 的产物复制到 WSL 的 /root/mon/,在 WSL 里用 setsid -f nohup 启动 moshiro-backend(监听 0.0.0.0:29181),再运行本脚本。 故障注入见 docs/deployment.md 第 3 节。 """ import json, os, subprocess, sys, time, urllib.request, urllib.error @@ -10,8 +10,8 @@ import json, os, subprocess, sys, time, urllib.request, urllib.error # 要连的是本机的虚拟网络(WSL),不能走环境变量里的 HTTP(S)_PROXY OPENER = urllib.request.build_opener(urllib.request.ProxyHandler({})) -ROOT = r"C:\Users\JustMe\Desktop\monitor-proj" -PROBE_EXE = os.path.join(ROOT, "backend", "artifacts", "aot-probe", "monitor-probe.exe") +ROOT = r"C:\Users\JustMe\Desktop\moshiro-proj" +PROBE_EXE = os.path.join(ROOT, "backend", "artifacts", "aot-probe", "moshiro-probe.exe") WORK = r"C:\Users\JustMe\AppData\Local\Temp\realnet" PORT = 29181 @@ -45,18 +45,18 @@ def req(method, path, body=None, headers=None, ctype="application/json"): def main(): os.makedirs(WORK, exist_ok=True) print("wsl ip", WSLIP) - resp, login = req("POST", "/api/admin/auth/login", {"username": "admin", "password": os.environ.get("MONITOR_ADMIN_PASSWORD", "correct horse battery")}) + resp, login = req("POST", "/api/admin/auth/login", {"username": "admin", "password": os.environ.get("MOSHIRO_ADMIN_PASSWORD", "correct horse battery")}) cookie = resp.headers["Set-Cookie"].split(";")[0] _, tok = req("POST", "/api/admin/api-tokens", {"name": "net", "scope": "admin"}, {"Cookie": cookie, "X-CSRF-Token": login["csrf_token"]}) auth = {"Authorization": "Bearer " + tok["plaintext"]} json.dump({"base": BASE, "token": tok["plaintext"], "wslip": WSLIP}, open(WORK + r"\state.json", "w")) _, join = req("POST", "/api/admin/join-tokens", {"auto_approve": True, "max_uses": 1, "expires_in_hours": 1}, auth) - wsl("pkill monitor-agent; cd /root/mon && rm -rf id agent.json") + wsl("pkill moshiro-agent; cd /root/mon && rm -rf id agent.json") print(wsl(f"cd /root/mon && ./fleet-enroll --server http://127.0.0.1:{PORT} --join {join['plaintext']} --name wsl-agent --identity-dir /root/mon/id")) cfg = {"fleet": {"server": f"http://127.0.0.1:{PORT}", "identity_dir": "/root/mon/id"}, "listen": {"bind": "0.0.0.0"}} wsl("cat > /root/mon/agent.json <<'EOF'\n" + json.dumps(cfg) + "\nEOF") - wsl("cd /root/mon && (setsid nohup ./monitor-agent --config /root/mon/agent.json > agent.log 2>&1 < /dev/null &)") + wsl("cd /root/mon && (setsid nohup ./moshiro-agent --config /root/mon/agent.json > agent.log 2>&1 < /dev/null &)") time.sleep(3) _, agents = req("GET", "/api/admin/agents", None, auth) agent = agents["items"][0] diff --git a/scripts/stability_wsl.py b/scripts/stability_wsl.py index cf91d09..69dac5b 100644 --- a/scripts/stability_wsl.py +++ b/scripts/stability_wsl.py @@ -5,7 +5,7 @@ Probe↔Agent 走真实的虚拟网络(eth0),Agent↔Backend 走 WSL 里的回环(故障用 tc 注入在 lo 上)。 前提:WSL 里有 Debian(root)和 tc;已发布 linux-x64 的 agent、backend、fleet-enroll(scripts/publish.sh), -以及 Windows 的 probe(dotnet publish backend/probe/src/Monitor.Probe -c Release -r win-x64 -p:PublishAot=true -o backend/artifacts/aot-probe)。 +以及 Windows 的 probe(dotnet publish backend/probe/src/MoShiro.Probe -c Release -r win-x64 -p:PublishAot=true -o backend/artifacts/aot-probe)。 用法:python scripts/stability_wsl.py [场景名 ...] (不带参数运行全部) 场景:init graceful crash probe_link backend_link silent soak @@ -23,11 +23,11 @@ import urllib.request OPENER = urllib.request.build_opener(urllib.request.ProxyHandler({})) ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -PROBE_EXE = os.path.join(ROOT, "backend", "artifacts", "aot-probe", "monitor-probe.exe") +PROBE_EXE = os.path.join(ROOT, "backend", "artifacts", "aot-probe", "moshiro-probe.exe") PUB = "/mnt/" + ROOT[0].lower() + ROOT[2:].replace("\\", "/") + "/backend/artifacts/publish" WORK = os.path.join(os.environ.get("TEMP", "."), "stability") PORT = 29181 -ADMIN_PASSWORD = os.environ.get("MONITOR_ADMIN_PASSWORD", "correct horse battery") # 测试用后端的初始管理员密码 +ADMIN_PASSWORD = os.environ.get("MOSHIRO_ADMIN_PASSWORD", "correct horse battery") # 测试用后端的初始管理员密码 RESULTS = [] HIDDEN = subprocess.CREATE_NO_WINDOW # 所有子进程都不弹窗 @@ -109,14 +109,14 @@ def rss_mb(name): def start_backend(): - wsl(f"cd /root/mon && /root/mon/start.sh backend env ASPNETCORE_URLS=http://0.0.0.0:{PORT} Monitor__DataDirectory=/root/mon/data " - f"Monitor__InitialAdminUser=admin 'Monitor__InitialAdminPassword={ADMIN_PASSWORD}' Monitor__SecureCookies=false ./monitor-backend") + wsl(f"cd /root/mon && /root/mon/start.sh backend env ASPNETCORE_URLS=http://0.0.0.0:{PORT} MoShiro__DataDirectory=/root/mon/data " + f"MoShiro__InitialAdminUser=admin 'MoShiro__InitialAdminPassword={ADMIN_PASSWORD}' MoShiro__SecureCookies=false ./moshiro-backend") return wait_until(backend_up, 30) def start_agent(): - wsl("/root/mon/start.sh agent ./monitor-agent --config /root/mon/agent.json") - return wait_until(lambda: bool(pids("monitor-agent")), 10) + wsl("/root/mon/start.sh agent ./moshiro-agent --config /root/mon/agent.json") + return wait_until(lambda: bool(pids("moshiro-agent")), 10) def stop_proc(name, sig="TERM", timeout=30): @@ -124,7 +124,7 @@ def stop_proc(name, sig="TERM", timeout=30): start = now() wsl(f"pkill -{sig} -x {name} || true") gone = wait_until(lambda: not pids(name), timeout, 0.2) - code = wsl(f"cat /root/mon/{name.replace('monitor-', '')}.exit 2>/dev/null || true") + code = wsl(f"cat /root/mon/{name.replace('moshiro-', '')}.exit 2>/dev/null || true") return (now() - start if gone is not None else None), (int(code) if code.strip().lstrip('-').isdigit() else None) @@ -209,9 +209,9 @@ def setup(fresh=True): """Clean state, copy the artifacts, start backend/agent, create the probe identity. The probe is not started.""" global TOKEN, AGENT_ID PROBE.stop("kill") - wsl("pkill -9 -x monitor-agent; pkill -9 -x monitor-backend; tc qdisc del dev eth0 ingress 2>/dev/null; tc qdisc del dev eth0 root 2>/dev/null; tc qdisc del dev lo root 2>/dev/null; tc qdisc del dev lo ingress 2>/dev/null; true") + wsl("pkill -9 -x moshiro-agent; pkill -9 -x moshiro-backend; tc qdisc del dev eth0 ingress 2>/dev/null; tc qdisc del dev eth0 root 2>/dev/null; tc qdisc del dev lo root 2>/dev/null; tc qdisc del dev lo ingress 2>/dev/null; true") if fresh: - wsl(f"rm -rf /root/mon && mkdir -p /root/mon && cp {PUB}/backend/linux-x64/* {PUB}/agent/linux-x64/* {PUB}/fleet-enroll/linux-x64/* /root/mon/ && chmod +x /root/mon/monitor-* /root/mon/fleet-enroll") + wsl(f"rm -rf /root/mon && mkdir -p /root/mon && cp {PUB}/backend/linux-x64/* {PUB}/agent/linux-x64/* {PUB}/fleet-enroll/linux-x64/* /root/mon/ && chmod +x /root/mon/moshiro-* /root/mon/fleet-enroll") wsl("cat > /root/mon/start.sh <<'EOF'\n" + LAUNCHER + "EOF\nchmod +x /root/mon/start.sh") if os.path.isdir(WORK): subprocess.run(["cmd", "/c", "rmdir", "/s", "/q", WORK], capture_output=True, creationflags=HIDDEN) @@ -279,7 +279,7 @@ def healthy(): def ensure_healthy(timeout=60): if not backend_up(): start_backend() - if not pids("monitor-agent"): + if not pids("moshiro-agent"): start_agent() if not PROBE.alive(): PROBE.start() @@ -292,7 +292,7 @@ def scenario_init(): print("== init: cold starts in every order, bad configuration") # 1. everything down, then start in the opposite order: probe -> agent -> backend (each must retry until its peer is up) PROBE.stop("kill") - wsl("pkill -9 -x monitor-agent; pkill -9 -x monitor-backend; true") + wsl("pkill -9 -x moshiro-agent; pkill -9 -x moshiro-backend; true") time.sleep(1) PROBE.start() time.sleep(3) @@ -307,7 +307,7 @@ def scenario_init(): # 2. backend first, then agent, then probe (the normal order) after a full stop PROBE.stop("kill") - wsl("pkill -9 -x monitor-agent; pkill -9 -x monitor-backend; true") + wsl("pkill -9 -x moshiro-agent; pkill -9 -x moshiro-backend; true") start_backend() start_agent() PROBE.start() @@ -322,9 +322,9 @@ def scenario_init(): check("probe: malformed config exits 1 with a readable error", r.returncode == 1 and "startup failed" in r.stderr + r.stdout, (r.stderr + r.stdout).strip()[:120]) r = subprocess.run([PROBE_EXE, "--config", os.path.join(WORK, "missing.json")], capture_output=True, text=True, timeout=20, creationflags=HIDDEN) check("probe: missing config exits 1 with the file name", r.returncode == 1 and "missing.json" in r.stderr + r.stdout, (r.stderr + r.stdout).strip()[:120]) - out = wsl("cd /root/mon && echo '{ not json' > bad.json && ./monitor-agent --config bad.json 2>&1; echo exit=$?", check=False) + out = wsl("cd /root/mon && echo '{ not json' > bad.json && ./moshiro-agent --config bad.json 2>&1; echo exit=$?", check=False) check("agent: malformed config exits 1", "exit=1" in out and "startup failed" in out, out.strip()[-120:]) - out = wsl("cd /root/mon && printf '{\"fleet\":{\"server\":\"http://127.0.0.1:1\",\"identity_dir\":\"/nonexistent\"}}' > noid.json && ./monitor-agent --config noid.json 2>&1; echo exit=$?", check=False) + out = wsl("cd /root/mon && printf '{\"fleet\":{\"server\":\"http://127.0.0.1:1\",\"identity_dir\":\"/nonexistent\"}}' > noid.json && ./moshiro-agent --config noid.json 2>&1; echo exit=$?", check=False) check("agent: no identity says to run fleet-enroll first", "exit=1" in out and "fleet-enroll" in out, out.strip()[-140:]) out = wsl(f"cd /root/mon && ./fleet-enroll --server http://127.0.0.1:{PORT} --join bogus --name x --identity-dir /tmp/enroll-x 2>&1; echo exit=$?", check=False) check("fleet-enroll: a bad join token fails clearly (non-zero exit)", "exit=0" not in out, out.strip()[-140:]) @@ -345,7 +345,7 @@ def scenario_graceful(): check("probe restarts and reconnects", wait_until(healthy, 30) is not None) # agent - secs, code = stop_proc("monitor-agent", "TERM") + secs, code = stop_proc("moshiro-agent", "TERM") log = wsl("tail -5 /root/mon/agent.log") check("agent exits on SIGTERM within 5 s with code 0", secs is not None and secs < 5 and code == 0, f"{secs and round(secs, 2)}s code={code}") check("agent logs a clean stop (no crash trace)", "stopping" in log and "Unhandled" not in log, log.splitlines()[-1][:100] if log else "") @@ -355,7 +355,7 @@ def scenario_graceful(): check("agent restarts, reconnects to backend and probe", wait_until(healthy, 40) is not None) # backend - secs, code = stop_proc("monitor-backend", "TERM", 30) + secs, code = stop_proc("moshiro-backend", "TERM", 30) log = wsl("tail -5 /root/mon/backend.log") check("backend exits on SIGTERM within 15 s with code 0", secs is not None and secs < 15 and code == 0, f"{secs and round(secs, 2)}s code={code}") check("backend log has no unhandled exception", "Unhandled" not in log and "Exception" not in log, log.replace(chr(10), ' | ')[-160:]) @@ -373,7 +373,7 @@ def scenario_crash(): # backend crash: the agent keeps its outbox; after restart nothing acknowledged is lost t = now() - wsl("pkill -9 -x monitor-backend") + wsl("pkill -9 -x moshiro-backend") time.sleep(25) # agent outbox accumulates (2-3 chunk rounds) start_backend() back = wait_until(healthy, 60) @@ -384,7 +384,7 @@ def scenario_crash(): check("backend kill -9: chunks buffered by the agent are delivered after the restart (no gap)", not g, f"gaps in {n} buckets: {g}; chunks {before}->{after}") # agent crash - wsl("pkill -9 -x monitor-agent") + wsl("pkill -9 -x moshiro-agent") time.sleep(5) start_agent() check("agent kill -9: recovers", wait_until(healthy, 40) is not None) @@ -435,7 +435,7 @@ def scenario_probe_link(): print("== probe<->agent link: flapping, long blackhole (half-open), reconnect storms") ensure_healthy() time.sleep(20) - mem0 = (PROBE.working_set_mb(), rss_mb("monitor-agent")) + mem0 = (PROBE.working_set_mb(), rss_mb("moshiro-agent")) # 1. flapping: 3 s blackholes every 10 s, 8 times for _ in range(8): @@ -458,7 +458,7 @@ def scenario_probe_link(): # 3. reconnect storm: kill the agent 12 times in a row for _ in range(12): - wsl("pkill -9 -x monitor-agent; true") + wsl("pkill -9 -x moshiro-agent; true") time.sleep(0.5) start_agent() time.sleep(2) @@ -475,7 +475,7 @@ def scenario_probe_link(): time.sleep(2) check("8 probe restarts in a row: converges", wait_until(healthy, 60) is not None) time.sleep(40) - mem1 = (PROBE.working_set_mb(), rss_mb("monitor-agent")) + mem1 = (PROBE.working_set_mb(), rss_mb("moshiro-agent")) check("memory stays bounded after the storms", mem1[0] < mem0[0] + 15 and mem1[1] < mem0[1] + 15, f"probe {mem0[0]:.1f}->{mem1[0]:.1f} MB, agent {mem0[1]:.1f}->{mem1[1]:.1f} MB") ensure_healthy() @@ -501,7 +501,7 @@ def scenario_backend_link(): check("steady state: ~2 chunks / 10 s (udp + tcp), no duplicates", 4 <= c1 - c0 <= 8, f"{c1 - c0} chunks in 30 s") # the backend is stopped for a while (graceful): the agent keeps collecting, delivers after - stop_proc("monitor-backend", "TERM", 30) + stop_proc("moshiro-backend", "TERM", 30) time.sleep(40) start_backend() check("backend down 40 s then up: everything converges", wait_until(healthy, 90) is not None) @@ -525,16 +525,16 @@ def scenario_silent(): check("... and when the probe thaws it comes back", wait_until(healthy, 90) is not None) # a frozen agent: the probe must drop the session and keep retrying; the backend must mark the agent offline - wsl("pkill -STOP -x monitor-agent") + wsl("pkill -STOP -x moshiro-agent") seen_off = wait_until(lambda: not agent_online(), 150, 1) check("frozen agent: the backend marks it offline", seen_off is not None, f"{seen_off and round(seen_off)}s" if seen_off else "still online after 150 s") - wsl("pkill -CONT -x monitor-agent") + wsl("pkill -CONT -x moshiro-agent") check("... and when the agent thaws everything converges", wait_until(healthy, 90) is not None) # a frozen backend: the agent must notice (WebSocket ping timeout) and reconnect once the backend thaws - wsl("pkill -STOP -x monitor-backend") + wsl("pkill -STOP -x moshiro-backend") time.sleep(70) - wsl("pkill -CONT -x monitor-backend") + wsl("pkill -CONT -x moshiro-backend") check("frozen backend (70 s): converges after it thaws", wait_until(healthy, 90) is not None) time.sleep(30) g, n = gaps(3) @@ -546,7 +546,7 @@ def scenario_soak(): print("== soak: 8 minutes with random faults; memory, convergence and data continuity") ensure_healthy() time.sleep(20) - mem0 = (PROBE.working_set_mb(), rss_mb("monitor-agent"), rss_mb("monitor-backend")) + mem0 = (PROBE.working_set_mb(), rss_mb("moshiro-agent"), rss_mb("moshiro-backend")) import random rnd = random.Random(7) end = now() + 8 * 60 @@ -563,7 +563,7 @@ def scenario_soak(): time.sleep(rnd.uniform(5, 12)) clear_netem("eth0") elif fault == "agent_kill": - wsl("pkill -9 -x monitor-agent; true") + wsl("pkill -9 -x moshiro-agent; true") time.sleep(1) start_agent() elif fault == "probe_kill": @@ -574,7 +574,7 @@ def scenario_soak(): clear_netem("eth0") check(f"after {faults} random faults everything converges", ensure_healthy(90) is not None) time.sleep(45) - mem1 = (PROBE.working_set_mb(), rss_mb("monitor-agent"), rss_mb("monitor-backend")) + mem1 = (PROBE.working_set_mb(), rss_mb("moshiro-agent"), rss_mb("moshiro-backend")) check("memory bounded", mem1[0] < mem0[0] + 15 and mem1[1] < mem0[1] + 20 and mem1[2] < mem0[2] + 40, f"probe {mem0[0]:.0f}->{mem1[0]:.0f}, agent {mem0[1]:.0f}->{mem1[1]:.0f}, backend {mem0[2]:.0f}->{mem1[2]:.0f} MB") m, _ = metrics("30s") check("the final minute is measured correctly", (m.get("loss.up") or 0) < 1 and (m.get("loss.down") or 0) < 1, f"{m.get('loss.up')} {m.get('loss.down')}") @@ -601,7 +601,7 @@ def main(): except Exception as ex: check(f"{name} ran to completion", False, repr(ex)[:300]) PROBE.stop("kill") - wsl("pkill -9 -x monitor-agent; pkill -9 -x monitor-backend; tc qdisc del dev eth0 root 2>/dev/null; tc qdisc del dev eth0 ingress 2>/dev/null; tc qdisc del dev lo ingress 2>/dev/null; true") + wsl("pkill -9 -x moshiro-agent; pkill -9 -x moshiro-backend; tc qdisc del dev eth0 root 2>/dev/null; tc qdisc del dev eth0 ingress 2>/dev/null; tc qdisc del dev lo ingress 2>/dev/null; true") failed = [r for r in RESULTS if not r[1]] print(f"\n{len(RESULTS) - len(failed)}/{len(RESULTS)} checks passed") for name, _, detail in failed: diff --git a/scripts/test-container-updater.py b/scripts/test-container-updater.py index 1af2566..b9f3100 100644 --- a/scripts/test-container-updater.py +++ b/scripts/test-container-updater.py @@ -8,8 +8,8 @@ import shlex import subprocess import tempfile -SCRIPT = Path(__file__).resolve().parents[1] / "packaging/update/monitor-update.sh" -with tempfile.TemporaryDirectory(prefix="monitor-updater-test-") as directory: +SCRIPT = Path(__file__).resolve().parents[1] / "packaging/update/moshiro-update.sh" +with tempfile.TemporaryDirectory(prefix="moshiro-updater-test-") as directory: root = Path(directory) (root / "bin").mkdir() (root / "data/update").mkdir(parents=True) @@ -39,8 +39,8 @@ case "${0##*/}" in esac """) tool.chmod(0o755) - env = dict(os.environ, PATH=str(root / "bin") + os.pathsep + os.environ["PATH"], MONITOR_UPDATE_CONF_DIR=str(root / "conf"), TEST_LOG=str(log), TEST_MODE=str(mode)) - repo = "registry.example/monitor/monitor-agent" + env = dict(os.environ, PATH=str(root / "bin") + os.pathsep + os.environ["PATH"], MOSHIRO_UPDATE_CONF_DIR=str(root / "conf"), TEST_LOG=str(log), TEST_MODE=str(mode)) + repo = "registry.example/moshiro/moshiro-agent" digest = "sha256:" + "a" * 64 image_file = root / "agent.image" state = root / "state" @@ -48,7 +48,7 @@ esac auth.write_text('{"auths":{}}') def configure(engine="podman", scope="system", policy="manual", explicit=True): - values = dict(DATA_DIR=str(root / "data"), IMAGE_REPO=repo, UNIT="monitor-agent.service", PUBKEY=str(pub), ENV_FILE=str(image_file), STATE_DIR=str(state), POLICY=policy, AUTHFILE=str(auth)) + values = dict(DATA_DIR=str(root / "data"), IMAGE_REPO=repo, UNIT="moshiro-agent.service", PUBKEY=str(pub), ENV_FILE=str(image_file), STATE_DIR=str(state), POLICY=policy, AUTHFILE=str(auth)) if explicit: values.update(ENGINE=engine, SERVICE_SCOPE=scope) (root / "conf/agent.conf").write_text("".join(k + "=" + shlex.quote(v) + "\n" for k, v in values.items())) diff --git a/web/README.md b/web/README.md index 815f7c8..448b3c9 100644 --- a/web/README.md +++ b/web/README.md @@ -19,7 +19,7 @@ npm run gen:api # 后端接口变了之后,重新生成 shared/ npm run lint:css # 颜色只能写在 shared/theme/tokens.css,其他样式一律引用令牌 ``` -连别的后端(本机跑前端、连线上的):登录页左边选后端(这个页面自己所在的、保存过的,或者「添加后端」),右边都是用户名和密码;添加后端填地址、用户名、密码,登录成功才保存。跨域用不了 Cookie,所以登录时后端会返回会话令牌(`return_session`),前端存在浏览器里、之后每个请求带上,12 小时过期、退出登录即作废;用户看不到也不需要管它。后端需要放行这个页面的来源(`Monitor__CorsOrigins`)。展示端和管理端侧栏的插头图标是同一个切换器。 +连别的后端(本机跑前端、连线上的):登录页左边选后端(这个页面自己所在的、保存过的,或者「添加后端」),右边都是用户名和密码;添加后端填地址、用户名、密码,登录成功才保存。跨域用不了 Cookie,所以登录时后端会返回会话令牌(`return_session`),前端存在浏览器里、之后每个请求带上,12 小时过期、退出登录即作废;用户看不到也不需要管它。后端需要放行这个页面的来源(`MoShiro__CorsOrigins`)。展示端和管理端侧栏的插头图标是同一个切换器。 约定: - 展示端能拿到的主机数据只有汇总的数字(CPU、内存、磁盘占用,网卡累计流量与瞬时网速,运行时长),**没有 CPU 型号、主机名、网卡名,也没有任何 IP**——后端的公开接口不返回它们,Agent 也根本不采集。展示端不显示 Probe 的版本和来源地址(这些只在管理端)。 diff --git a/web/admin/index.html b/web/admin/index.html index 20cdccb..9f3ab12 100644 --- a/web/admin/index.html +++ b/web/admin/index.html @@ -3,7 +3,7 @@ - MyMonitor · 管理 + MoShiro · 管理
diff --git a/web/admin/package.json b/web/admin/package.json index 006f82d..a5f8ab4 100644 --- a/web/admin/package.json +++ b/web/admin/package.json @@ -1,5 +1,5 @@ { - "name": "monitor-admin", + "name": "moshiro-admin", "private": true, "version": "0.1.0", "type": "module", diff --git a/web/admin/src/App.tsx b/web/admin/src/App.tsx index c930c93..cb61d56 100644 --- a/web/admin/src/App.tsx +++ b/web/admin/src/App.tsx @@ -54,7 +54,7 @@ function Login() {