From 63d0780fda56e52a67fd99cee722cb991482469c Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:15:18 +0800
Subject: [PATCH 01/11] docs: correct monitoring lifecycle guidance
---
apps/docs/content/docs/en/deployment.mdx | 2 +-
apps/docs/content/docs/en/mobile.mdx | 7 +++----
apps/docs/content/docs/en/monitoring.mdx | 20 ++++++-------------
apps/docs/content/docs/en/security-events.mdx | 2 +-
apps/docs/content/docs/en/storage-sizing.mdx | 2 +-
apps/docs/content/docs/zh/deployment.mdx | 2 +-
apps/docs/content/docs/zh/mobile.mdx | 7 +++----
apps/docs/content/docs/zh/monitoring.mdx | 20 ++++++-------------
apps/docs/content/docs/zh/security-events.mdx | 2 +-
apps/docs/content/docs/zh/storage-sizing.mdx | 2 +-
10 files changed, 24 insertions(+), 42 deletions(-)
diff --git a/apps/docs/content/docs/en/deployment.mdx b/apps/docs/content/docs/en/deployment.mdx
index db816d222..ea215d88a 100644
--- a/apps/docs/content/docs/en/deployment.mdx
+++ b/apps/docs/content/docs/en/deployment.mdx
@@ -577,7 +577,7 @@ ServerBee is designed for lightweight VPS instances:
| Agent CPU | Negligible | < 1% of 1 core |
| Agent RAM | ~27 MB measured cgroup steady state | 30–40 MB headroom |
-Database size depends on the number of monitored servers and your retention settings. As a rough guide, expect about 1 MB of raw records per server per day. With 50 servers and the default 7-day retention, the database is roughly 350 MB.
+Database size depends on server count, enabled writers, sampling intervals, and retention settings. Use [Storage & Sizing](/en/docs/storage-sizing) for the measured 30-day baseline, feature multipliers, and WAL headroom instead of combining rough estimates from different retention windows.
For glibc-based Server deployments, set `MALLOC_ARENA_MAX=2` in the service environment to match the measured 140–170 MB production range. See [Resource Usage](/en/docs/resource-usage) for scope and measurement details.
diff --git a/apps/docs/content/docs/en/mobile.mdx b/apps/docs/content/docs/en/mobile.mdx
index a82b351f2..98c92eccf 100644
--- a/apps/docs/content/docs/en/mobile.mdx
+++ b/apps/docs/content/docs/en/mobile.mdx
@@ -62,14 +62,13 @@ refresh_ttl = 2592000 # Refresh token lifetime in seconds (default: 30 days)
## Managing Paired Devices
-### View Connected Devices
+### View Paired Devices
In the web app, go to **Settings** → **Mobile Devices** to see all paired iOS devices:
- Device name (e.g., "iPhone 15 Pro")
- Paired date and time
- Last active timestamp
-- Current online/offline status
### Revoke Access
@@ -115,7 +114,7 @@ To enable push notifications, configure APNs credentials in the web app:
- **Token Rotation**: Refresh tokens are rotated on every use (single-use). Old refresh tokens cannot be reused.
- **Fixed Expiry**: Access tokens have fixed 15-minute expiry (no sliding renewal) to limit exposure if stolen
- **Secure Storage**: Tokens are stored in iOS Keychain with appropriate accessibility levels
-- **Automatic Cleanup**: Expired sessions are cleaned up by the server's background task (12-hour interval)
+- **Automatic Cleanup**: The server's background task checks for expired sessions hourly
## Troubleshooting
@@ -129,7 +128,7 @@ To enable push notifications, configure APNs credentials in the web app:
- Verify APNs credentials are correct in the notification channel
- Ensure the iOS app has granted notification permissions
-- Check that the device shows as "Online" in Mobile Devices list
+- Confirm that the device still appears in the Mobile Devices list and has not been revoked
- Review server logs for APNs delivery errors
### Authentication Failures
diff --git a/apps/docs/content/docs/en/monitoring.mdx b/apps/docs/content/docs/en/monitoring.mdx
index ca80d03d1..5005dbf40 100644
--- a/apps/docs/content/docs/en/monitoring.mdx
+++ b/apps/docs/content/docs/en/monitoring.mdx
@@ -194,12 +194,12 @@ The server detail page defaults to **Real-time mode**. In this mode, charts disp
- **Data source**: Accumulated from `BrowserMessage::Update` events via the `['servers']` TanStack Query cache
- **Update interval**: ~3 seconds (matches the agent report interval)
-- **Buffer size**: 10-minute ring buffer (~200 data points), automatically trimmed
+- **Buffer size**: Grows to 250 data points, then trims back to the newest 200 points
- **Deduplication**: Uses the server-side `last_active` timestamp to filter duplicate events
- **Available charts**: CPU, Memory, Disk, Network In/Out, Load Average (1m)
- **Time axis**: First tick shows `HH:mm:ss`, subsequent ticks show `mm:ss`
-Temperature, GPU, and Disk I/O charts are not available in real-time mode because the WebSocket `ServerStatus` message does not include these fields. Switch to a historical view to see temperature, GPU, and disk I/O data.
+Temperature, GPU, and Disk I/O charts are not currently available in real-time mode. WebSocket `BrowserMessage::Update` messages include aggregate disk read/write rates in `LiveMetrics`, but the detail-page real-time chart model does not yet map those fields. Switch to a historical view to see temperature, GPU, and per-disk I/O data.
### Disk I/O Charts
@@ -237,7 +237,7 @@ ServerBee automatically cleans up expired data via a background task that runs o
| Audit logs | 180 days | `retention.audit_logs_days` |
-The hourly task runs in a fixed order: hourly aggregation first, then cleanup. This guarantees that minute-level data is rolled up into hourly summaries before it is deleted, so long-term trend data is never lost.
+Aggregation and cleanup run in separate hourly background tasks. Cleanup starts with a 60-second offset to reduce overlap with aggregation, but the tasks do not form one fixed-order transaction. Keep enough raw-data retention for aggregation to run reliably, and monitor task errors if long-term history matters.
Example of overriding the retention policy:
@@ -250,15 +250,7 @@ records_hourly_days = 365 # keep hourly data for 1 year
### Disk Space Estimate
-Estimated disk usage for 1000 agents:
-
-| Data Type | Estimate |
-|-----------|----------|
-| Minute-level records (7 days) | ~5 GB |
-| Hourly records (90 days) | ~3 GB |
-| Total (30 days) | ~8 GB |
-
-Actual usage depends on the number of servers and the configured retention periods.
+Database growth depends on server count, enabled writers, sampling intervals, and retention settings. Use [Storage & Sizing](/en/docs/storage-sizing) as the sizing reference; it documents measured April 2026 results, a single 30-day baseline, feature multipliers, and WAL headroom.
## Server Groups and Management
@@ -311,7 +303,7 @@ Agent Server Browser
| | delete old records |
```
-The agent reports every 3 seconds. The server caches the latest report in memory and immediately broadcasts it to connected browsers. Every 60 seconds, all cached reports are batch-written to SQLite. Every hour, raw records are aggregated into hourly summaries, and expired data is cleaned up based on retention settings.
+The agent reports every 3 seconds. The server caches the latest report in memory and immediately broadcasts it to connected browsers. Every 60 seconds, all cached reports are batch-written to SQLite. Separate hourly tasks aggregate raw records into hourly summaries and clean up expired data based on retention settings.
## Network Quality Views
@@ -327,7 +319,7 @@ The time range bar offers these options:
| Mode | Data Source | Description |
|------|-------------|-------------|
-| **Real-time** | WebSocket ring buffer | Live streaming data (default) |
+| **Real-time** | WebSocket in-memory buffer | Live streaming data (default) |
| **1h** | REST API (raw records) | Last 1 hour from database |
| **6h** | REST API (raw records) | Last 6 hours |
| **24h** | REST API (raw records) | Last 24 hours |
diff --git a/apps/docs/content/docs/en/security-events.mdx b/apps/docs/content/docs/en/security-events.mdx
index 6b7ef0221..8be61cf23 100644
--- a/apps/docs/content/docs/en/security-events.mdx
+++ b/apps/docs/content/docs/en/security-events.mdx
@@ -115,7 +115,7 @@ See [Firewall Blocklist](/en/docs/firewall) for the full feature, guardrails, an
## Retention
-Security events are kept for **30 days** by default. Tune with `retention.security_event_days` (env: `SERVERBEE_RETENTION__SECURITY_EVENT_DAYS`). The cleanup task purges expired rows daily.
+Security events are kept for **30 days** by default. Tune with `retention.security_event_days` (env: `SERVERBEE_RETENTION__SECURITY_EVENT_DAYS`). The cleanup task checks for expired rows hourly.
## Data Flow
diff --git a/apps/docs/content/docs/en/storage-sizing.mdx b/apps/docs/content/docs/en/storage-sizing.mdx
index eb9659f70..7d8c8ed57 100644
--- a/apps/docs/content/docs/en/storage-sizing.mdx
+++ b/apps/docs/content/docs/en/storage-sizing.mdx
@@ -7,7 +7,7 @@ icon: Database
This page explains how ServerBee grows its SQLite database over time and gives a 30-day estimate for common 20-server deployments.
-These numbers are based on the current schema and retention defaults, measured from temporary SQLite databases generated against the repository code in April 2026. They describe the main database file after 30 days from a fresh deployment. In production, reserve another 10% to 20% for SQLite WAL headroom and transient growth.
+These numbers were measured in April 2026 from temporary SQLite databases generated with the repository schema and retention defaults at that time. Use this page as the dated sizing reference, not as a guarantee for later schemas or workloads. The results describe the main database file after 30 days from a fresh deployment. In production, reserve another 10% to 20% for SQLite WAL headroom and transient growth.
## Assumptions
diff --git a/apps/docs/content/docs/zh/deployment.mdx b/apps/docs/content/docs/zh/deployment.mdx
index f066a4a49..97ddee8a9 100644
--- a/apps/docs/content/docs/zh/deployment.mdx
+++ b/apps/docs/content/docs/zh/deployment.mdx
@@ -577,7 +577,7 @@ ServerBee 面向轻量级 VPS 实例设计:
| Agent CPU | 可忽略 | < 单核的 1% |
| Agent 内存 | 实测稳态 cgroup 约 27 MB | 30–40 MB 余量 |
-数据库大小取决于被监控服务器的数量和你的保留设置。粗略估计,每台服务器每天约产生 1 MB 原始记录。以 50 台服务器、默认 7 天保留为例,数据库约为 350 MB。
+数据库大小取决于服务器数量、启用的数据写入功能、采样间隔和保留设置。请使用[存储与容量规划](/zh/docs/storage-sizing)中的实测 30 天基线、功能乘数和 WAL 预留建议,避免混用不同保留窗口的粗略估算。
glibc 环境下部署 Server 时,在服务环境中设置 `MALLOC_ARENA_MAX=2`,才能与实测 140–170 MB 的生产范围保持一致。具体测量范围见[资源开销](/zh/docs/resource-usage)。
diff --git a/apps/docs/content/docs/zh/mobile.mdx b/apps/docs/content/docs/zh/mobile.mdx
index 340f487ac..b5d59be4c 100644
--- a/apps/docs/content/docs/zh/mobile.mdx
+++ b/apps/docs/content/docs/zh/mobile.mdx
@@ -62,14 +62,13 @@ refresh_ttl = 2592000 # 刷新令牌有效期(秒),默认 30 天
## 管理已配对设备
-### 查看已连接设备
+### 查看已配对设备
在 Web 应用中,进入 **设置** → **移动设备** 查看所有已配对的 iOS 设备:
- 设备名称(如 "iPhone 15 Pro")
- 配对日期和时间
- 最后活跃时间戳
-- 当前在线/离线状态
### 撤销访问权限
@@ -115,7 +114,7 @@ refresh_ttl = 2592000 # 刷新令牌有效期(秒),默认 30 天
- **令牌轮换**:每次使用时轮换刷新令牌(一次性使用),旧刷新令牌不能重用
- **固定过期**:访问令牌固定 15 分钟过期(无滑动续期),以限制被盗后的暴露时间
- **安全存储**:令牌使用适当的可访问性级别存储在 iOS Keychain 中
-- **自动清理**:过期会话由服务器的后台任务清理(12 小时间隔)
+- **自动清理**:服务器后台任务每小时检查并清理过期会话
## 故障排除
@@ -129,7 +128,7 @@ refresh_ttl = 2592000 # 刷新令牌有效期(秒),默认 30 天
- 验证通知渠道中的 APNs 凭证是否正确
- 确保 iOS 应用已授予通知权限
-- 检查设备是否在移动设备列表中显示为"在线"
+- 确认设备仍在移动设备列表中,且未被撤销
- 查看服务器日志中的 APNs 投递错误
### 认证失败
diff --git a/apps/docs/content/docs/zh/monitoring.mdx b/apps/docs/content/docs/zh/monitoring.mdx
index 020896441..d41fc4e18 100644
--- a/apps/docs/content/docs/zh/monitoring.mdx
+++ b/apps/docs/content/docs/zh/monitoring.mdx
@@ -194,12 +194,12 @@ Agent 上报 (每 3 秒)
- **数据来源**:通过 `['servers']` TanStack Query 缓存订阅 `BrowserMessage::Update` 事件,自动累积数据点
- **更新频率**:约 3 秒一次(与 Agent 上报间隔一致)
-- **缓冲区大小**:10 分钟环形缓冲区(约 200 个数据点),超出自动裁剪
+- **缓冲区大小**:增长到 250 个数据点后,裁剪为最新的 200 个点
- **去重机制**:基于服务端 `last_active` 时间戳过滤重复事件
- **可用图表**:CPU、内存、磁盘、网络入/出、负载(1 分钟)
- **时间轴格式**:第一个刻度显示 `HH:mm:ss`,后续刻度显示 `mm:ss`
-温度、GPU 和磁盘 I/O 图表在实时模式下不可用,因为 WebSocket 推送的 `ServerStatus` 消息中不包含这些字段。切换到历史视图即可查看温度、GPU 和磁盘 I/O 数据。
+实时模式目前不提供温度、GPU 和磁盘 I/O 图表。WebSocket 的 `BrowserMessage::Update` 消息已在 `LiveMetrics` 中携带磁盘读写汇总速率,但详情页实时图表模型尚未映射这些字段。切换到历史视图可查看温度、GPU 和分盘 I/O 数据。
### 磁盘 I/O 图表
@@ -237,7 +237,7 @@ ServerBee 自动清理过期数据,由后台任务每小时执行一次:
| 审计日志 | 180 天 | `retention.audit_logs_days` |
-每小时任务按固定顺序执行:先进行小时聚合,再执行数据清理。这保证了分钟级数据在被清理前已经完成了小时级聚合,不会丢失长期趋势数据。
+聚合和清理是两个独立的每小时后台任务。清理任务以 60 秒偏移启动,以减少与聚合任务重叠,但两者并非按固定顺序执行的同一事务。若长期历史数据很重要,请为聚合保留足够的原始数据,并监控后台任务错误。
修改保留策略示例:
@@ -250,15 +250,7 @@ records_hourly_days = 365 # 小时级数据保留 1 年
### 磁盘空间估算
-以 1000 台 Agent 为例的磁盘占用预估:
-
-| 数据类型 | 估算 |
-|----------|------|
-| 分钟级记录 (7 天) | 约 5 GB |
-| 小时级记录 (90 天) | 约 3 GB |
-| 总计 (30 天) | 约 8 GB |
-
-实际占用取决于服务器数量和保留时间配置。
+数据库增长取决于服务器数量、启用的数据写入功能、采样间隔和保留设置。容量规划请以[存储与容量规划](/zh/docs/storage-sizing)为准,其中提供了 2026 年 4 月的实测数据、统一的 30 天基线、功能乘数和 WAL 预留建议。
## 服务器分组和管理
@@ -311,7 +303,7 @@ Agent Server 浏览器
| | 删除过期记录 |
```
-Agent 每 3 秒上报一次。Server 将最新一份上报缓存在内存中,并立即广播给已连接的浏览器。每 60 秒,所有缓存的上报会批量写入 SQLite。每小时,原始记录会聚合为小时级汇总,并根据保留策略清理过期数据。
+Agent 每 3 秒上报一次。Server 将最新一份上报缓存在内存中,并立即广播给已连接的浏览器。每 60 秒,所有缓存的上报会批量写入 SQLite。两个独立的每小时任务分别将原始记录聚合为小时级汇总,并根据保留策略清理过期数据。
## 网络质量视图
@@ -327,7 +319,7 @@ Agent 每 3 秒上报一次。Server 将最新一份上报缓存在内存中,
| 模式 | 数据来源 | 说明 |
|------|----------|------|
-| **Real-time** | WebSocket 环形缓冲区 | 实时流式数据(默认) |
+| **Real-time** | WebSocket 内存缓冲区 | 实时流式数据(默认) |
| **1h** | REST API(原始记录) | 最近 1 小时的数据库记录 |
| **6h** | REST API(原始记录) | 最近 6 小时 |
| **24h** | REST API(原始记录) | 最近 24 小时 |
diff --git a/apps/docs/content/docs/zh/security-events.mdx b/apps/docs/content/docs/zh/security-events.mdx
index cc04352b3..96a11c3d0 100644
--- a/apps/docs/content/docs/zh/security-events.mdx
+++ b/apps/docs/content/docs/zh/security-events.mdx
@@ -115,7 +115,7 @@ Alerts 页面提供三张 **预设卡片**,一键创建规则。预设已填
## 数据保留
-安全事件默认保留 **30 天**。可通过 `retention.security_event_days`(环境变量 `SERVERBEE_RETENTION__SECURITY_EVENT_DAYS`)调整。清理任务每天自动清除过期数据。
+安全事件默认保留 **30 天**。可通过 `retention.security_event_days`(环境变量 `SERVERBEE_RETENTION__SECURITY_EVENT_DAYS`)调整。清理任务每小时检查并清除过期数据。
## 数据链路
diff --git a/apps/docs/content/docs/zh/storage-sizing.mdx b/apps/docs/content/docs/zh/storage-sizing.mdx
index f36de8aba..067329e99 100644
--- a/apps/docs/content/docs/zh/storage-sizing.mdx
+++ b/apps/docs/content/docs/zh/storage-sizing.mdx
@@ -7,7 +7,7 @@ icon: Database
本页介绍 ServerBee 的 SQLite 数据库如何随时间增长,并为常见的 20 服务器部署场景提供 30 天存储估算。
-这些数值基于当前数据库结构和默认保留策略,于 2026 年 4 月通过对仓库代码生成的临时 SQLite 数据库测量得出。它们描述的是全新部署运行 30 天后的主数据库文件大小。在生产环境中,请额外预留 10% 到 20% 的空间作为 SQLite WAL(预写日志)和临时增长的缓冲。
+这些数值于 2026 年 4 月通过临时 SQLite 数据库测量得出,采用当时的仓库数据库结构和默认保留策略。请将本页作为注明日期的容量规划基准,而不是对后续数据库结构或工作负载的保证。结果描述的是全新部署运行 30 天后的主数据库文件大小。在生产环境中,请额外预留 10% 到 20% 的空间作为 SQLite WAL(预写日志)和临时增长的缓冲。
## 假设条件
From 58ca29725d59274cda63b72eeaf4895fe93d8482 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:15:48 +0800
Subject: [PATCH 02/11] docs: clarify testing and capability ownership
---
apps/docs/content/docs/en/index.mdx | 2 +-
apps/docs/content/docs/en/testing.mdx | 34 +++++++--------
apps/docs/content/docs/zh/alerts.mdx | 2 +-
apps/docs/content/docs/zh/index.mdx | 2 +-
apps/docs/content/docs/zh/testing.mdx | 62 +++++++++++++--------------
apps/docs/scripts/check-contracts.ts | 28 ++++++++++++
6 files changed, 79 insertions(+), 51 deletions(-)
diff --git a/apps/docs/content/docs/en/index.mdx b/apps/docs/content/docs/en/index.mdx
index 5bb17403f..cb07e766d 100644
--- a/apps/docs/content/docs/en/index.mdx
+++ b/apps/docs/content/docs/en/index.mdx
@@ -51,7 +51,7 @@ Sign in via GitHub, Google, or any generic OIDC provider. Mobile sessions, devic
### Tested and CI-enforced
-The project ships with 3,800+ automated tests — including integration suites that drive the real Axum router over HTTP and WebSocket through a mock-agent harness — and every change passes a zero-warning Clippy lint, the full Rust suite, and frontend type/lint checks in CI. See [Testing & Quality](/en/docs/testing).
+The project ships with more than 3,800 automated tests, including integration suites that drive the real Axum router over HTTP and WebSocket through a mock-agent harness. Whenever the main CI workflow runs, it enforces frontend and installer checks; Rust-relevant pull requests and pushes that trigger the workflow also run zero-warning Clippy and the Rust test jobs. See [Testing & Quality](/en/docs/testing).
## Tech Stack
diff --git a/apps/docs/content/docs/en/testing.mdx b/apps/docs/content/docs/en/testing.mdx
index 4bb2ae3b9..19c9cb06b 100644
--- a/apps/docs/content/docs/en/testing.mdx
+++ b/apps/docs/content/docs/en/testing.mdx
@@ -1,21 +1,21 @@
---
title: Testing & Quality
-description: How ServerBee is tested — 3,800+ automated tests, a mock-agent WebSocket harness, and CI-enforced quality gates.
+description: How ServerBee verifies Rust, WebSocket, and frontend behavior with layered automated tests and path-aware CI gates.
icon: FlaskConical
---
-ServerBee treats reliability as a feature. The codebase ships with a large, fast automated test suite that exercises the full stack — from pure Rust logic to the live WebSocket control plane and the React UI — and every change is gated by CI.
+ServerBee treats reliability as a feature. The codebase ships with a large, fast automated test suite that exercises the full stack — from pure Rust logic to the live WebSocket control plane and the React UI. CI applies the relevant quality gates based on the event and changed paths.
-## Test suite at a glance
+## Test layers at a glance
-| Area | Tests | What it covers |
-|------|-------|----------------|
-| `common` crate | 149 | Protocol messages, capability bitmask, SSRF guard, shared types |
-| `agent` crate | 891 | Collectors, reporter WebSocket loop, pinger, file manager, security detectors, firewall, IP quality |
-| `server` crate | 2,223 | REST handlers, WebSocket handlers, services, background tasks, migrations |
-| Frontend (`apps/web`) | 600+ | Hooks, components, stores, and utilities (Vitest) |
+| Area | What it covers |
+|------|----------------|
+| `common` crate | Protocol messages, capability bitmask, SSRF guard, shared types |
+| `agent` crate | Collectors, reporter WebSocket loop, pinger, file manager, security detectors, firewall, IP quality |
+| `server` crate | REST handlers, WebSocket handlers, services, background tasks, migrations |
+| Frontend (`apps/web`) | Hooks, components, stores, and utilities (Vitest) |
-That is **3,800+ automated tests** in total, all green on every CI run.
+Together, these layers contain **more than 3,800 automated tests**. The exact total changes as coverage grows, so this page documents the testing layers and enforced commands instead of a point-in-time count for each area.
## How we test
@@ -27,7 +27,7 @@ Pure logic is tested next to the code in inline `#[cfg(test)]` modules: protocol
### Integration tests
-The server's 42 integration suites drive the **real Axum router over HTTP and WebSocket** against a freshly migrated, isolated SQLite database. A purpose-built **mock-agent harness** (`crates/server/tests/common/mod.rs`) stands up a simulated agent on a real WebSocket connection, so the control plane is exercised end-to-end without a live host:
+The server integration suites drive the **real Axum router over HTTP and WebSocket** against a freshly migrated, isolated SQLite database. A purpose-built **mock-agent harness** (`crates/server/tests/common/mod.rs`) stands up a simulated agent on a real WebSocket connection, so the control plane is exercised end-to-end without a live host:
- terminal relay, Docker log streaming, file operations, and scheduled-task dispatch
- real-time browser fan-out (initial full sync plus incremental updates)
@@ -37,7 +37,7 @@ On the agent side, the reporter's connect → handshake → dispatch → reconne
### Frontend tests
-The React app is covered by [Vitest](https://vitest.dev) across 89 test files — hooks, components, state stores, and utilities — run with `make web-test`.
+The React app is covered by [Vitest](https://vitest.dev) across hooks, components, state stores, and utilities. Run the frontend suite with `make web-test`.
The integration harness lets contributors verify real request/response and WebSocket flows on a laptop, with no agent, Docker daemon, or external service required.
@@ -71,11 +71,11 @@ cargo llvm-cov -p serverbee-server --summary-only
## CI gates
-Every push and pull request runs:
+The main CI workflow runs for pushes and pull requests to `main` when its path filters match. Whenever it runs, CI enforces:
-- `cargo clippy --workspace -- -D warnings` — zero-warning lint
-- `cargo test --workspace` — the full Rust suite
-- `bun run tsc -b` and `bun x ultracite check` — frontend type check and lint (Biome)
-- POSIX and ShellCheck linting of the `install.sh` install script
+- frontend build, type checking, Vitest, and Ultracite lint
+- installer transaction tests plus POSIX, BusyBox, and ShellCheck validation
+
+For pull requests, changes to Rust sources, manifests, toolchain files, or the CI workflow also enable `cargo check`, zero-warning Clippy, and the Rust test jobs. Pull requests without Rust-relevant changes skip the Rust jobs. Pushes that trigger this workflow run the Rust gates.
Release builds cross-compile the binary for five targets (Linux x86_64/aarch64 musl, macOS x86_64/aarch64, Windows x86_64) and publish multi-arch Docker images.
diff --git a/apps/docs/content/docs/zh/alerts.mdx b/apps/docs/content/docs/zh/alerts.mdx
index 489bad449..e4f651c0e 100644
--- a/apps/docs/content/docs/zh/alerts.mdx
+++ b/apps/docs/content/docs/zh/alerts.mdx
@@ -197,7 +197,7 @@ Alerts 页面为 `capability_grant_detected`(「临时授予能力」)提供
## 阻断源 IP
-安全事件类规则(`ssh_brute_force_detected`、`port_scan_detected`)可以开启**阻断源 IP**。触发时,ServerBee 会指示受影响 Agent 的防火墙阻断攻击源 IP,把检测变为自动处置。该能力需要服务器具备 `CAP_FIREWALL_BLOCK` 权限。详见 [安全事件检测](/zh/docs/security-events) 和 [防火墙管理](/zh/docs/firewall)。
+安全事件类规则(`ssh_brute_force_detected`、`port_scan_detected`)可以开启**阻断源 IP**。触发时,ServerBee 会指示受影响 Agent 的防火墙阻断攻击源 IP,把检测变为自动处置。这要求 Agent 上报 `firewall_block` 能力(`CAP_FIREWALL_BLOCK`)。该能力默认启用,但只能在 Agent 主机的 `[capabilities]` 配置中禁用,Server 端不能切换。详见 [安全事件检测](/zh/docs/security-events) 和 [防火墙管理](/zh/docs/firewall)。
## 通知渠道
diff --git a/apps/docs/content/docs/zh/index.mdx b/apps/docs/content/docs/zh/index.mdx
index 8a8d0e2fc..a0171117b 100644
--- a/apps/docs/content/docs/zh/index.mdx
+++ b/apps/docs/content/docs/zh/index.mdx
@@ -51,7 +51,7 @@ Agent 通过 WebSocket 与 Server 保持长连接,实现实时数据推送。
### 完善的测试与 CI 把关
-项目自带 3800+ 自动化测试 —— 其中包括通过 mock-agent 测试桩驱动真实 Axum 路由、经由 HTTP 与 WebSocket 的集成套件 —— 每次改动都要通过零警告的 Clippy lint、完整 Rust 测试套件,以及前端的类型与 lint 检查。详见[测试与质量](/zh/docs/testing)。
+项目包含 3,800 项以上的自动化测试,其中的集成测试通过 mock-agent 测试桩,经由 HTTP 和 WebSocket 驱动真实的 Axum 路由。每次触发主 CI 工作流时,都会执行前端和安装脚本检查;包含 Rust 相关变更的 Pull Request,以及已触发该工作流的 push,还会运行零警告 Clippy 和 Rust 测试任务。详见[测试与质量](/zh/docs/testing)。
## 技术栈
diff --git a/apps/docs/content/docs/zh/testing.mdx b/apps/docs/content/docs/zh/testing.mdx
index a39a67029..1880f977c 100644
--- a/apps/docs/content/docs/zh/testing.mdx
+++ b/apps/docs/content/docs/zh/testing.mdx
@@ -1,54 +1,54 @@
---
title: 测试与质量
-description: ServerBee 如何做测试 —— 3800+ 自动化测试、mock-agent WebSocket 测试桩,以及 CI 强制的质量门槛。
+description: ServerBee 如何通过分层自动化测试和按变更路径执行的 CI 门槛,验证 Rust、WebSocket 与前端行为。
icon: FlaskConical
---
-ServerBee 把可靠性当作一项功能来对待。代码库自带一套规模庞大、运行飞快的自动化测试,覆盖从纯 Rust 逻辑、实时 WebSocket 控制面到 React 界面的整条链路,且每次改动都由 CI 把关。
+ServerBee 把可靠性视为产品能力。代码库提供一套规模大、运行快的自动化测试,覆盖从 Rust 逻辑、实时 WebSocket 控制面到 React 界面的完整链路。CI 会根据触发事件和变更路径执行相应的质量检查。
-## 测试规模一览
+## 测试分层一览
-| 范围 | 测试数 | 覆盖内容 |
-|------|--------|----------|
-| `common` crate | 149 | 协议消息、能力位掩码、SSRF guard、共享类型 |
-| `agent` crate | 891 | 采集器、reporter WebSocket 循环、pinger、文件管理、安全检测器、防火墙、IP 质量 |
-| `server` crate | 2,223 | REST 处理器、WebSocket 处理器、服务层、后台任务、数据库迁移 |
-| 前端(`apps/web`) | 600+ | Hooks、组件、状态 store、工具函数(Vitest) |
+| 范围 | 覆盖内容 |
+|------|----------|
+| `common` crate | 协议消息、能力位掩码、SSRF 防护、共享类型 |
+| `agent` crate | 采集器、reporter WebSocket 循环、pinger、文件管理、安全检测器、防火墙、IP 质量 |
+| `server` crate | REST 处理器、WebSocket 处理器、服务层、后台任务、数据库迁移 |
+| 前端(`apps/web`) | Hook、组件、状态存储和工具函数(Vitest) |
-合计 **3800+ 自动化测试**,每次 CI 运行全部通过。
+这些层级合计包含 **3,800 项以上的自动化测试**。测试数量会随着覆盖范围扩大而变化,因此本页记录稳定的测试分层和检查命令,不逐项维护某个时间点的精确数量。
## 我们怎么测
-ServerBee 采用分层策略,为每一层匹配最合适的测试方式。
+ServerBee 采用分层策略,为每一层选择合适的测试方式。
### 单元测试
-纯逻辑就近放在源文件的内联 `#[cfg(test)]` 模块里测试:协议(反)序列化、能力位掩码、SSRF guard、告警评估、成本计算、各类解析器以及采集器。它们以毫秒级运行,构成了 agent 与 `common` 测试套件的主体。
+纯逻辑就近放在源文件的内联 `#[cfg(test)]` 模块中测试,包括协议序列化与反序列化、能力位掩码、SSRF 防护、告警评估、成本计算、解析器和采集器。这类测试通常在毫秒级完成,构成 `agent` 与 `common` 测试套件的主体。
### 集成测试
-服务端的 42 个集成套件让测试直接驱动**真实的 Axum 路由,经由 HTTP 与 WebSocket**,跑在一个全新迁移、相互隔离的 SQLite 数据库之上。一套专门构建的 **mock-agent 测试桩**(`crates/server/tests/common/mod.rs`)会在真实的 WebSocket 连接上拉起一个模拟 Agent,从而无需真实主机即可端到端地演练整个控制面:
+服务端集成测试通过 HTTP 和 WebSocket 直接驱动**真实的 Axum 路由**,并使用完成全新迁移的独立 SQLite 数据库。专用的 **mock-agent 测试桩**(`crates/server/tests/common/mod.rs`)通过真实 WebSocket 连接启动模拟 Agent,无需真实主机即可端到端验证控制面:
- 终端中继、Docker 日志流、文件操作、定时任务派发
-- 实时浏览器广播(首次全量同步 + 增量更新)
-- 安全事件、告警、能力门控,以及 Admin/Member 权限
+- 实时浏览器广播(首次全量同步和后续增量更新)
+- 安全事件、告警、能力门控,以及 Admin/Member 权限
-在 Agent 一侧,reporter 的 连接 → 握手 → 派发 → 重连 主循环则跑在一个**进程内的假 WebSocket 服务器**之上,从而无需真实后端即可测试其网络核心。
+在 Agent 侧,reporter 的“连接 → 握手 → 派发 → 重连”主循环由**进程内的模拟 WebSocket 服务器**驱动,无需真实后端即可测试网络核心。
### 前端测试
-React 应用由 [Vitest](https://vitest.dev) 覆盖,分布在 89 个测试文件中 —— 涵盖 Hooks、组件、状态 store 与工具函数 —— 通过 `make web-test` 运行。
+React 应用使用 [Vitest](https://vitest.dev) 测试 Hook、组件、状态存储和工具函数。运行 `make web-test` 即可执行前端测试套件。
-这套集成测试桩让贡献者在一台笔记本上就能验证真实的请求/响应与 WebSocket 流程,无需 Agent、Docker 守护进程或任何外部服务。
+这套集成测试桩让贡献者可以在笔记本电脑上验证真实的请求、响应和 WebSocket 流程,无需 Agent、Docker 守护进程或外部服务。
## 覆盖率
-覆盖率使用 [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov) 测量:
+覆盖率使用 [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov) 测量:
-| Crate | Region | Line |
-|-------|--------|------|
+| Crate | 区域覆盖率 | 行覆盖率 |
+|-------|------------|----------|
| `common` | 98% | 97% |
| `agent` | 90% | 90% |
| `server` | 92% | 93% |
@@ -58,12 +58,12 @@ Rust 总体区域覆盖率超过 91%。当前较大的剩余缺口集中在进
## 本地运行测试
```bash
-make cargo-test # Rust:整个 workspace 的单元 + 集成测试
-make web-test # 前端:Vitest
-make cargo-clippy # Rust 代码检查(强制 0 warning)
+make cargo-test # Rust: unit + integration across the workspace
+make web-test # Frontend: Vitest
+make cargo-clippy # Rust lint (0 warnings enforced)
```
-测量单个 crate 的覆盖率:
+测量单个 crate 的覆盖率:
```bash
cargo llvm-cov -p serverbee-server --summary-only
@@ -71,11 +71,11 @@ cargo llvm-cov -p serverbee-server --summary-only
## CI 门槛
-每次 push 和 Pull Request 都会运行:
+主 CI 工作流会在路径过滤条件匹配时运行,适用于向 `main` 分支的 push,以及以 `main` 为目标分支的 Pull Request。每次触发都会执行:
-- `cargo clippy --workspace -- -D warnings` —— 零警告 lint
-- `cargo test --workspace` —— 完整的 Rust 测试套件
-- `bun run tsc -b` 与 `bun x ultracite check` —— 前端类型检查与 lint(Biome)
-- 安装脚本 `install.sh` 的 POSIX 与 ShellCheck 检查
+- 前端构建、类型检查、Vitest 测试和 Ultracite lint
+- 安装脚本事务测试,以及 POSIX、BusyBox 和 ShellCheck 验证
-发布构建会为五个目标交叉编译二进制(Linux x86_64/aarch64 musl、macOS x86_64/aarch64、Windows x86_64),并发布多架构 Docker 镜像。
+对于 Pull Request,如果 Rust 源码、manifest、工具链文件或 CI 工作流发生变化,还会运行 `cargo check`、零警告 Clippy 和 Rust 测试任务。没有 Rust 相关变更的 Pull Request 会跳过 Rust 任务。对于已触发此工作流的 push,CI 会运行 Rust 质量门槛。
+
+发布构建会为五个目标交叉编译二进制文件(Linux x86_64/aarch64 musl、macOS x86_64/aarch64、Windows x86_64),并发布多架构 Docker 镜像。
diff --git a/apps/docs/scripts/check-contracts.ts b/apps/docs/scripts/check-contracts.ts
index 4f94af550..0a0e6c3f9 100644
--- a/apps/docs/scripts/check-contracts.ts
+++ b/apps/docs/scripts/check-contracts.ts
@@ -176,4 +176,32 @@ for (const locale of locales) {
)
}
+const enTesting = await text(join(contentRoot, 'en/testing.mdx'))
+const zhTesting = await text(join(contentRoot, 'zh/testing.mdx'))
+for (const [locale, testing] of [
+ ['en', enTesting],
+ ['zh', zhTesting]
+] as const) {
+ invariant(!/\b891\b|2,223|89 test files|89 个测试文件/.test(testing), `${locale}/testing.mdx has stale test counts`)
+}
+invariant(
+ enTesting.includes('Pull requests without Rust-relevant changes skip the Rust jobs.'),
+ 'en/testing.mdx does not explain the path-aware Rust CI boundary'
+)
+invariant(
+ zhTesting.includes('没有 Rust 相关变更的 Pull Request 会跳过 Rust 任务。'),
+ 'zh/testing.mdx does not explain the path-aware Rust CI boundary'
+)
+
+const enIndex = await text(join(contentRoot, 'en/index.mdx'))
+const zhIndex = await text(join(contentRoot, 'zh/index.mdx'))
+invariant(!enIndex.includes('every change passes'), 'en/index.mdx overstates CI coverage')
+invariant(!zhIndex.includes('每次改动都要通过'), 'zh/index.mdx overstates CI coverage')
+
+const zhAlerts = await text(join(contentRoot, 'zh/alerts.mdx'))
+invariant(
+ /Agent 上报 `firewall_block` 能力(`CAP_FIREWALL_BLOCK`).*Agent 主机.*Server 端不能切换/.test(zhAlerts),
+ 'zh/alerts.mdx does not identify the agent-owned firewall capability'
+)
+
console.log('PASS: documentation contracts')
From 7c3a165b5912cd33d4bf0e164d03ddbd1cfa4343 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:17:23 +0800
Subject: [PATCH 03/11] docs: correct api authentication and architecture facts
---
apps/docs/content/docs/en/api-reference.mdx | 65 ++++++++++++---------
apps/docs/content/docs/en/architecture.mdx | 65 +++++++++++++--------
apps/docs/content/docs/en/file-manager.mdx | 9 ++-
apps/docs/content/docs/zh/api-reference.mdx | 65 ++++++++++++---------
apps/docs/content/docs/zh/architecture.mdx | 65 +++++++++++++--------
apps/docs/content/docs/zh/file-manager.mdx | 9 ++-
6 files changed, 160 insertions(+), 118 deletions(-)
diff --git a/apps/docs/content/docs/en/api-reference.mdx b/apps/docs/content/docs/en/api-reference.mdx
index 312c499d4..e02dfeaaf 100644
--- a/apps/docs/content/docs/en/api-reference.mdx
+++ b/apps/docs/content/docs/en/api-reference.mdx
@@ -30,11 +30,14 @@ Successful REST responses are wrapped as:
}
```
-Errors use:
+Application errors use an error code and message, with optional `details`:
```json
{
- "error": "Error message describing what went wrong"
+ "error": {
+ "code": "BAD_REQUEST",
+ "message": "Bad request: description of what went wrong"
+ }
}
```
@@ -55,14 +58,14 @@ curl https://your-server/api/servers -b cookies.txt
### API Key
-Use API keys for automation. Create them in Settings → API Keys.
+Use API keys for automation. Admin users can create them in Settings → API Keys.
```bash
curl https://your-server/api/servers \
-H "X-API-Key: serverbee_your-api-key-here"
```
-API keys use the `serverbee_` prefix and are shown only once when created.
+API keys use the `serverbee_` prefix and are shown only once when created. An API key authenticates as its owning user; it is a credential, not a separate role. Requests inherit the owner's current Admin or Member permissions. Only Admin users can create new API keys.
### Bearer Session Token
@@ -85,63 +88,67 @@ curl https://your-server/api/auth/me \
| POST | `/api/mobile/auth/refresh` | Refresh a mobile session |
| POST | `/api/mobile/auth/pair` | Redeem a mobile pairing code |
| POST | `/api/agent/register` | Claim a bound enrollment offer with an Agent-proposed run token |
-| GET | `/api/status` | Default public status page data |
-| GET | `/api/status/{slug}` | Configurable public status page data |
+| GET | `/api/status/config` | Public status page configuration |
+| GET | `/api/status` | Servers included on the public status page |
+| GET | `/api/status/servers/{id}` | Public detail for an included server |
+| GET | `/api/status/servers/{id}/metrics` | Public metrics for an included server |
+| GET | `/api/status/servers/{id}/uptime-daily` | Public daily uptime for an included server |
+| GET | `/api/status/network`, `/api/status/network/{id}` | Public network overview and per-server detail |
+| GET | `/api/status/ip-quality` | Public IP quality overview |
+| GET | `/api/status/incidents`, `/api/status/maintenances` | Public incidents and maintenance windows |
| GET | `/api/settings/brand` | Public brand settings |
| GET | `/api/brand/logo` | Serve uploaded logo |
| GET | `/api/brand/favicon` | Serve uploaded favicon |
-## Authenticated Read Endpoints
+## Authenticated User Endpoints
-Read endpoints are available to Admin and Member users unless noted otherwise.
+These endpoints require a user credential. Admin and Member users can use the listed read and account-scoped operations. API keys receive the same access as their owning users. Admin-only management endpoints are listed separately below.
| Family | Representative endpoints |
|--------|--------------------------|
-| Current user and API keys | `GET /api/auth/me`, `PUT /api/auth/password`, `GET/POST /api/auth/api-keys`, `DELETE /api/auth/api-keys/{id}` |
+| Current user and credentials | `GET /api/auth/me`, `PUT /api/auth/password`, `GET /api/auth/api-keys`, `DELETE /api/auth/api-keys/{id}` |
| 2FA and OAuth accounts | `/api/auth/2fa/*`, `GET/DELETE /api/auth/oauth/accounts/*` |
-| Mobile devices | `POST /api/mobile/auth/logout`, `GET /api/mobile/auth/devices`, `DELETE /api/mobile/auth/devices/{id}` |
+| Mobile sessions and devices | `POST /api/mobile/auth/logout`, `GET /api/mobile/auth/devices`, `DELETE /api/mobile/auth/devices/{id}`, `POST /api/mobile/pair`, `POST /api/mobile/push/register`, `POST /api/mobile/push/unregister` |
| Servers | `GET /api/servers`, `GET /api/servers/{id}`, `GET /api/servers/{id}/records`, `GET /api/servers/{id}/gpu-records` |
| Agent Authority | `GET /api/servers/{id}/agent-authority`, `GET /api/agent-authority/events?server_id={id}` |
-| Groups and tags | `GET /api/server-groups`, `GET /api/server-tags` |
+| Groups and tags | `GET /api/server-groups`, `GET /api/servers/{id}/tags` |
| Uptime and traffic | `GET /api/servers/{id}/uptime-daily`, `GET /api/servers/{id}/traffic` |
| GeoIP | `GET /api/geoip/status` |
| Ping tasks | `GET /api/ping-tasks`, `GET /api/ping-tasks/{id}/records` |
-| Network probes | `/api/network-probes/*`, `/api/servers/{id}/network-probes/*` |
+| Network probes | `GET /api/network-probes/targets`, `GET /api/network-probes/setting`, `GET /api/network-probes/overview` |
| Traceroute results | `GET /api/servers/{id}/traceroute/{request_id}` |
-| Files, read-only | `POST /api/files/{server_id}/list`, `stat`, `read`, `GET /api/files/transfers`, `GET /api/files/download/{transfer_id}` |
| Docker, read-only | `GET /api/servers/{id}/docker/containers`, `stats`, `info`, `events`, `networks`, `volumes` |
| Service monitors | `GET /api/service-monitors`, `GET /api/service-monitors/{id}`, `GET /api/service-monitors/{id}/records` |
-| Status page config | `GET /api/status-pages` |
+| Status page config | `GET /api/status-page` |
| Dashboards | `GET /api/dashboards`, `GET /api/dashboards/default`, `GET /api/dashboards/{id}` |
-| Themes | `GET /api/themes/*` |
| Cost insights | `GET /api/cost/overview`, `GET /api/servers/{id}/cost-insights` |
-| Alert events | alert event read endpoints used by the dashboard |
+| Alert events | `GET /api/alert-events`, `GET /api/alert-events/{alert_key}` |
## Admin Write and Management Endpoints
-Admin role is required for write operations and system management.
+The following management and host-control operations require Admin. Account-scoped operations listed above remain available to authenticated Member users.
| Family | Representative endpoints |
|--------|--------------------------|
+| API key creation | `POST /api/auth/api-keys` |
| Server management | `POST /api/servers` (idempotent onboarding), `PUT/DELETE /api/servers/{id}`, `POST /api/servers/{id}/upgrade` |
| Agent Authority | re-enrollment, offer issue/replace/revoke, and authority revocation under `/api/servers/{id}/agent-authority/*` |
-| Groups and tags | CRUD `/api/server-groups/*`, CRUD `/api/server-tags/*` |
-| Ping and network probes | CRUD `/api/ping-tasks/*`, write endpoints under `/api/network-probes/*` |
+| Groups and tags | `POST /api/server-groups`, `PUT/DELETE /api/server-groups/{id}`, `PUT /api/servers/{id}/tags` |
+| Ping and network probes | `POST /api/ping-tasks`, `PUT/DELETE /api/ping-tasks/{id}`, `POST /api/network-probes/targets`, `PUT/DELETE /api/network-probes/targets/{id}`, `PUT /api/network-probes/setting` |
| Traceroute | `POST /api/servers/{id}/traceroute` |
-| Files | `POST /api/files/{server_id}/write`, `delete`, `mkdir`, `move`, `download`, `upload`, `DELETE /api/files/transfers/{transfer_id}` |
+| Files | All `/api/files/*` endpoints, including list, stat, read, download, transfer management, and file changes |
| Docker actions | `POST /api/servers/{id}/docker/containers/{cid}/action` |
-| Service monitors | CRUD `/api/service-monitors/*`, `POST /api/service-monitors/{id}/check` |
+| Service monitors | `POST /api/service-monitors`, `PUT/DELETE /api/service-monitors/{id}`, `POST /api/service-monitors/{id}/check` |
| Dashboards | `POST /api/dashboards`, `PUT/DELETE /api/dashboards/{id}` |
-| Themes and appearance | theme write endpoints, `PUT /api/settings/brand`, `POST /api/settings/brand/logo`, `POST /api/settings/brand/favicon` |
-| Status pages | CRUD `/api/status-pages/*` |
-| Incidents | CRUD `/api/incidents/*`, `POST /api/incidents/{id}/updates` |
-| Maintenance windows | CRUD `/api/maintenances/*` |
-| Alerts and notifications | CRUD `/api/alert-rules/*`, `/api/notifications/*`, `/api/notification-groups/*` |
+| Brand appearance | `PUT /api/settings/brand`, `POST /api/settings/brand/logo`, `POST /api/settings/brand/favicon` |
+| Status page | `PUT /api/status-page` |
+| Incidents | `GET/POST /api/incidents`, `PUT/DELETE /api/incidents/{id}`, `POST /api/incidents/{id}/updates` |
+| Maintenance windows | `GET/POST /api/maintenances`, `PUT/DELETE /api/maintenances/{id}` |
+| Alerts and notifications | Manage `/api/alert-rules`, `/api/notifications`, `/api/notification-groups`, and their `/{id}` routes |
| Tasks | `GET/POST /api/tasks`, `GET/PUT/DELETE /api/tasks/{id}`, `GET /api/tasks/{id}/results`, `POST /api/tasks/{id}/run` |
-| Users | CRUD `/api/users/*` |
-| Audit and settings | `GET /api/audit-logs`, `/api/settings/*`, `POST /api/settings/backup`, `POST /api/settings/restore` |
+| Users | `GET/POST /api/users`, `GET/PUT/DELETE /api/users/{id}` |
+| Audit and settings | `GET/DELETE /api/audit-logs`, `GET/PUT /api/settings`, `POST /api/settings/backup`, `POST /api/settings/restore` |
| GeoIP | `POST /api/geoip/download` |
-| Mobile push | `POST /api/mobile/pair`, `POST /api/mobile/push/register`, `POST /api/mobile/push/unregister` |
The backup endpoint returns a raw SQLite download. Restore accepts a raw SQLite body with `Content-Type: application/octet-stream` and requires a Server restart. Both are Admin-only, high-risk operations; see [Backup and Restore](/en/docs/deployment#backup-and-restore) for verified commands and safeguards.
diff --git a/apps/docs/content/docs/en/architecture.mdx b/apps/docs/content/docs/en/architecture.mdx
index 4439dad1a..ba86f3c60 100644
--- a/apps/docs/content/docs/en/architecture.mdx
+++ b/apps/docs/content/docs/en/architecture.mdx
@@ -90,8 +90,8 @@ Its main modules are:
|--------|----------------|
| Collector | Periodically collects CPU, memory, disk, network, and other metrics via `sysinfo` |
| Reporter | Manages the WebSocket connection, reports metrics, handles server commands, reconnects on disconnect |
-| ProbeManager | Runs ICMP/TCP/HTTP probe tasks dispatched by the server |
-| Executor | Executes remote shell commands dispatched by the server |
+| PingManager | Runs scheduled ICMP/TCP/HTTP ping tasks dispatched by the server |
+| NetworkProber | Runs scheduled network quality probes and reports aggregate results |
| Terminal | Manages PTY terminal sessions and forwards terminal I/O |
### Common (`crates/common`)
@@ -118,7 +118,7 @@ The built frontend is embedded into the server binary at compile time, so there
## Communication Protocol
-All communication uses WebSocket. Every message is a JSON text frame; terminal I/O data is carried inside the message's `data` field, base64-encoded. The current protocol version is `6`, sent to the agent in the `Welcome` message on connect.
+Agent reporting and control traffic uses WebSocket. Browsers use REST for initial data, history, and management operations, plus WebSocket for real-time updates. WebSocket application messages are JSON text frames; terminal I/O data is carried inside the message's `data` field, base64-encoded. The current Agent protocol version is `6`, sent in the `Welcome` message on connect.
The agent connects to `ws:///api/agent/ws` and authenticates with `Authorization: Bearer `. The Server temporarily accepts the legacy `?token=` form so older Agents can reconnect and upgrade.
@@ -180,7 +180,7 @@ Agent Server
|--- WebSocket connect + token --->|
| | verify token, look up server
|<-- Welcome { server_id, |
- | protocol_version: 4, |
+ | protocol_version: 6, |
| report_interval: 3 } -------|
| |
|--- SystemInfo { cpu_name, | static info on connect/reconnect
@@ -296,15 +296,19 @@ The server spawns several long-running background tasks via `tokio::spawn`:
## Security Model
-### Authentication Layers
+### Authentication Paths
-ServerBee supports three authentication mechanisms, all checked in the auth middleware:
+User-facing REST and WebSocket requests resolve a user from a session cookie, API key, or Bearer session token. The authorization check then uses that user's current role. Agent WebSocket connections use a separate per-Agent run token.
```
Request arrives
+-- Cookie: session_token=xxx --> Session auth (browser)
+-- Header: X-API-Key: serverbee_xxx --> API key auth (automation)
- +-- Query: ?token=xxx --> Agent token auth (agent WebSocket)
+ +-- Header: Authorization: Bearer xxx --> Session auth (mobile)
+
+Agent WebSocket connects
+ +-- Header: Authorization: Bearer xxx --> Agent run-token auth
+ +-- Query: ?token=xxx --> Legacy Agent compatibility only
```
#### Session-based (browser)
@@ -316,10 +320,11 @@ Request arrives
#### API Key (automation)
-- Users can create named API keys for programmatic access.
+- Admin users can create named API keys for programmatic access.
- Key format: `serverbee_` + 32 random bytes (base64url).
- Keys are hashed with argon2; an 8-character plaintext prefix is stored for identification.
- On verification, the prefix narrows the lookup, then argon2 verifies the key.
+- A key authenticates as its owning user and inherits that user's current `admin` or `member` role. An API key is a credential type, not a third role.
#### Agent Token (agents)
@@ -332,22 +337,23 @@ Request arrives
### Role-Based Access
-Two roles exist: `admin` (full access) and `member` (read-only dashboard access).
+Two user roles exist: `admin` (full access) and `member` (read-only dashboard access). Session cookies, Bearer session tokens, and API keys are credentials for one of these users; they do not define additional roles.
Browser-facing WebSockets re-check the persisted session or API key and the user's role while connected. Logout, API-key deletion, password-reset session revocation, account deletion, or a role change closes the existing socket within two seconds instead of preserving handshake-time privileges.
-| Resource | Admin | Member | API Key | Agent |
-|----------|-------|--------|---------|-------|
-| Server list/detail | Yes | Yes (limited) | Yes | - |
-| Server create/update/delete | Yes | No | Yes | - |
-| Alert rule management | Yes | No | Yes | - |
-| Notification config | Yes | No | No | - |
-| User management | Yes | No | No | - |
-| System settings | Yes | No | No | - |
-| Real-time WebSocket | Yes | Yes | Yes | - |
-| Web terminal | Yes | No | No | - |
-| Remote commands | Yes | No | Yes | - |
-| Metric reporting | - | - | - | Yes |
+| Resource | Admin user | Member user | Agent |
+|----------|------------|-------------|-------|
+| Server list/detail | Yes | Yes (limited) | - |
+| Server create/update/delete | Yes | No | - |
+| Alert rule management | Yes | No | - |
+| Notification config | Yes | No | - |
+| User management | Yes | No | - |
+| System settings | Yes | No | - |
+| Real-time server updates | Yes | Yes | - |
+| File Manager | Yes | No | - |
+| Web terminal | Yes | No | - |
+| Remote commands | Yes | No | - |
+| Metric reporting | - | - | Yes |
### OAuth
@@ -392,15 +398,24 @@ ServerBee/
entity/ # sea-orm entities (one module per table)
migration/ # Database migrations
middleware/ # Auth, logging middleware
- agent/ # Agent binary (collector, reporter, terminal)
+ agent/ # Agent binary (collection, reporting, control)
src/
main.rs
config.rs
collector/ # Per-metric collectors
- reporter.rs # WebSocket reporting + reconnect
- probe/ # ICMP/TCP/HTTP probes
- executor.rs # Remote command execution
+ reporter/ # WebSocket reporting, reconnect, commands, and file operations
+ pinger.rs # ICMP/TCP/HTTP ping tasks
+ probe_utils.rs # Shared probe validation and networking helpers
+ network_prober.rs # Scheduled network probes
+ traceroute.rs # Traceroute execution
terminal.rs # PTY terminal
+ file_manager.rs # Restricted remote file operations
+ docker/ # Docker inspection, actions, and log streaming
+ security/ # Host security event detection
+ firewall/ # Firewall block management
+ ip_quality/ # IP quality checks
+ upgrade.rs # Self-upgrade coordinator
+ upgrade/ # Transactional self-upgrade support
apps/
web/ # React frontend (Vite, TanStack, shadcn/ui)
docs/ # Documentation site (TanStack Start + Fumadocs)
diff --git a/apps/docs/content/docs/en/file-manager.mdx b/apps/docs/content/docs/en/file-manager.mdx
index 4df955c91..6a16d1649 100644
--- a/apps/docs/content/docs/en/file-manager.mdx
+++ b/apps/docs/content/docs/en/file-manager.mdx
@@ -6,6 +6,8 @@ icon: FolderOpen
File Manager provides controlled remote filesystem access through the ServerBee agent. It is meant for operational tasks such as checking logs, editing small configuration files, and transferring files without opening a full terminal.
+File Manager is available only to Admin users. This includes browsing, stat, reading, downloading, and transfer management as well as operations that modify files.
+
File Manager is a high-risk feature. Enable it only on trusted servers, and restrict `root_paths` to the minimum directories needed.
@@ -57,10 +59,7 @@ The button is hidden when `CAP_FILE` is not in the server's effective capabiliti
## Permissions
-| Role | Allowed operations |
-|------|--------------------|
-| Admin | Browse, stat, read, write, upload, download, delete, move, create directories, cancel transfers |
-| Member | Browse, stat, read, download, list own transfers |
+Only Admin users can use File Manager. Every file API endpoint is behind the Admin authorization check because even read operations can expose sensitive files from a managed host.
All high-risk file operations are recorded in the audit log, including attempts denied because the capability is disabled.
@@ -101,7 +100,7 @@ Uploads and downloads are chunked. A download creates a temporary transfer on th
## API
-Read endpoints are available to both Admin and Member users. Write endpoints require Admin.
+All File Manager endpoints require Admin, including list, stat, read, download, and transfer endpoints.
| Method | Path | Description |
|--------|------|-------------|
diff --git a/apps/docs/content/docs/zh/api-reference.mdx b/apps/docs/content/docs/zh/api-reference.mdx
index e6e3e653b..f5dded46b 100644
--- a/apps/docs/content/docs/zh/api-reference.mdx
+++ b/apps/docs/content/docs/zh/api-reference.mdx
@@ -30,11 +30,14 @@ REST 成功响应统一包装为:
}
```
-错误响应格式:
+应用错误包含错误码和消息,还可能包含 `details`:
```json
{
- "error": "Error message describing what went wrong"
+ "error": {
+ "code": "BAD_REQUEST",
+ "message": "Bad request: description of what went wrong"
+ }
}
```
@@ -55,14 +58,14 @@ curl https://your-server/api/servers -b cookies.txt
### API Key
-自动化场景推荐使用 API Key。在 Settings → API Keys 创建。
+自动化场景推荐使用 API Key。Admin 可在 Settings → API Keys 创建。
```bash
curl https://your-server/api/servers \
-H "X-API-Key: serverbee_your-api-key-here"
```
-API Key 使用 `serverbee_` 前缀,创建时只显示一次。
+API Key 使用 `serverbee_` 前缀,创建时只显示一次。API Key 会以所属用户的身份完成认证,它是凭据,不是独立角色。请求继承所属用户当前的 Admin 或 Member 权限。只有 Admin 可以创建新的 API Key。
### Bearer Session Token
@@ -85,63 +88,67 @@ curl https://your-server/api/auth/me \
| POST | `/api/mobile/auth/refresh` | 刷新移动端会话 |
| POST | `/api/mobile/auth/pair` | 兑换移动端配对码 |
| POST | `/api/agent/register` | 使用 Agent 提议的 run token claim 已绑定的 enrollment offer |
-| GET | `/api/status` | 默认公开状态页数据 |
-| GET | `/api/status/{slug}` | 可配置公开状态页数据 |
+| GET | `/api/status/config` | 公开状态页配置 |
+| GET | `/api/status` | 公开状态页包含的服务器 |
+| GET | `/api/status/servers/{id}` | 已公开服务器的详情 |
+| GET | `/api/status/servers/{id}/metrics` | 已公开服务器的指标 |
+| GET | `/api/status/servers/{id}/uptime-daily` | 已公开服务器的每日可用性 |
+| GET | `/api/status/network`、`/api/status/network/{id}` | 公开网络概览和单台服务器详情 |
+| GET | `/api/status/ip-quality` | 公开 IP 质量概览 |
+| GET | `/api/status/incidents`、`/api/status/maintenances` | 公开事件公告和维护窗口 |
| GET | `/api/settings/brand` | 公开品牌设置 |
| GET | `/api/brand/logo` | 返回上传的 Logo |
| GET | `/api/brand/favicon` | 返回上传的 Favicon |
-## 已认证读取端点
+## 已认证用户端点
-除特别说明外,读取端点对 Admin 和 Member 均可用。
+这些端点需要用户凭据。Admin 和 Member 都可以使用下列读取及账户范围操作;API Key 的权限与所属用户相同。仅限 Admin 的管理端点单独列在下一节。
| 端点族 | 代表端点 |
|--------|----------|
-| 当前用户和 API Key | `GET /api/auth/me`、`PUT /api/auth/password`、`GET/POST /api/auth/api-keys`、`DELETE /api/auth/api-keys/{id}` |
+| 当前用户和凭据 | `GET /api/auth/me`、`PUT /api/auth/password`、`GET /api/auth/api-keys`、`DELETE /api/auth/api-keys/{id}` |
| 2FA 和 OAuth 账号 | `/api/auth/2fa/*`、`GET/DELETE /api/auth/oauth/accounts/*` |
-| 移动端设备 | `POST /api/mobile/auth/logout`、`GET /api/mobile/auth/devices`、`DELETE /api/mobile/auth/devices/{id}` |
+| 移动端会话和设备 | `POST /api/mobile/auth/logout`、`GET /api/mobile/auth/devices`、`DELETE /api/mobile/auth/devices/{id}`、`POST /api/mobile/pair`、`POST /api/mobile/push/register`、`POST /api/mobile/push/unregister` |
| 服务器 | `GET /api/servers`、`GET /api/servers/{id}`、`GET /api/servers/{id}/records`、`GET /api/servers/{id}/gpu-records` |
| Agent Authority | `GET /api/servers/{id}/agent-authority`、`GET /api/agent-authority/events?server_id={id}` |
-| 分组和标签 | `GET /api/server-groups`、`GET /api/server-tags` |
+| 分组和标签 | `GET /api/server-groups`、`GET /api/servers/{id}/tags` |
| 可用性和流量 | `GET /api/servers/{id}/uptime-daily`、`GET /api/servers/{id}/traffic` |
| GeoIP | `GET /api/geoip/status` |
| Ping 任务 | `GET /api/ping-tasks`、`GET /api/ping-tasks/{id}/records` |
-| 网络探测 | `/api/network-probes/*`、`/api/servers/{id}/network-probes/*` |
+| 网络探测 | `GET /api/network-probes/targets`、`GET /api/network-probes/setting`、`GET /api/network-probes/overview` |
| Traceroute 结果 | `GET /api/servers/{id}/traceroute/{request_id}` |
-| 文件读取 | `POST /api/files/{server_id}/list`、`stat`、`read`、`GET /api/files/transfers`、`GET /api/files/download/{transfer_id}` |
| Docker 读取 | `GET /api/servers/{id}/docker/containers`、`stats`、`info`、`events`、`networks`、`volumes` |
| 服务监控 | `GET /api/service-monitors`、`GET /api/service-monitors/{id}`、`GET /api/service-monitors/{id}/records` |
-| 状态页配置 | `GET /api/status-pages` |
+| 状态页配置 | `GET /api/status-page` |
| 仪表盘 | `GET /api/dashboards`、`GET /api/dashboards/default`、`GET /api/dashboards/{id}` |
-| 主题 | `GET /api/themes/*` |
| 成本洞察 | `GET /api/cost/overview`、`GET /api/servers/{id}/cost-insights` |
-| 告警事件 | 仪表盘使用的告警事件读取端点 |
+| 告警事件 | `GET /api/alert-events`、`GET /api/alert-events/{alert_key}` |
## 管理员写入和管理端点
-写入操作和系统管理需要 Admin 角色。
+下列管理和主机控制操作需要 Admin 权限。上一节列出的账户范围操作仍可由已认证的 Member 使用。
| 端点族 | 代表端点 |
|--------|----------|
+| 创建 API Key | `POST /api/auth/api-keys` |
| 服务器管理 | `POST /api/servers`(幂等 onboarding)、`PUT/DELETE /api/servers/{id}`、`POST /api/servers/{id}/upgrade` |
| Agent Authority | `/api/servers/{id}/agent-authority/*` 下的重新接入、offer 发出/替换/吊销及 authority 吊销 |
-| 分组和标签 | CRUD `/api/server-groups/*`、CRUD `/api/server-tags/*` |
-| Ping 和网络探测 | CRUD `/api/ping-tasks/*`、`/api/network-probes/*` 下的写入端点 |
+| 分组和标签 | `POST /api/server-groups`、`PUT/DELETE /api/server-groups/{id}`、`PUT /api/servers/{id}/tags` |
+| Ping 和网络探测 | `POST /api/ping-tasks`、`PUT/DELETE /api/ping-tasks/{id}`、`POST /api/network-probes/targets`、`PUT/DELETE /api/network-probes/targets/{id}`、`PUT /api/network-probes/setting` |
| Traceroute | `POST /api/servers/{id}/traceroute` |
-| 文件管理 | `POST /api/files/{server_id}/write`、`delete`、`mkdir`、`move`、`download`、`upload`、`DELETE /api/files/transfers/{transfer_id}` |
+| 文件管理 | 所有 `/api/files/*` 端点,包括列目录、获取元数据、读取、下载、传输管理和文件修改 |
| Docker 操作 | `POST /api/servers/{id}/docker/containers/{cid}/action` |
-| 服务监控 | CRUD `/api/service-monitors/*`、`POST /api/service-monitors/{id}/check` |
+| 服务监控 | `POST /api/service-monitors`、`PUT/DELETE /api/service-monitors/{id}`、`POST /api/service-monitors/{id}/check` |
| 仪表盘 | `POST /api/dashboards`、`PUT/DELETE /api/dashboards/{id}` |
-| 主题和外观 | 主题写入端点、`PUT /api/settings/brand`、`POST /api/settings/brand/logo`、`POST /api/settings/brand/favicon` |
-| 状态页 | CRUD `/api/status-pages/*` |
-| 事件公告 | CRUD `/api/incidents/*`、`POST /api/incidents/{id}/updates` |
-| 维护窗口 | CRUD `/api/maintenances/*` |
-| 告警和通知 | CRUD `/api/alert-rules/*`、`/api/notifications/*`、`/api/notification-groups/*` |
+| 品牌外观 | `PUT /api/settings/brand`、`POST /api/settings/brand/logo`、`POST /api/settings/brand/favicon` |
+| 状态页 | `PUT /api/status-page` |
+| 事件公告 | `GET/POST /api/incidents`、`PUT/DELETE /api/incidents/{id}`、`POST /api/incidents/{id}/updates` |
+| 维护窗口 | `GET/POST /api/maintenances`、`PUT/DELETE /api/maintenances/{id}` |
+| 告警和通知 | 管理 `/api/alert-rules`、`/api/notifications`、`/api/notification-groups` 及其 `/{id}` 路由 |
| 任务 | `GET/POST /api/tasks`、`GET/PUT/DELETE /api/tasks/{id}`、`GET /api/tasks/{id}/results`、`POST /api/tasks/{id}/run` |
-| 用户 | CRUD `/api/users/*` |
-| 审计和设置 | `GET /api/audit-logs`、`/api/settings/*`、`POST /api/settings/backup`、`POST /api/settings/restore` |
+| 用户 | `GET/POST /api/users`、`GET/PUT/DELETE /api/users/{id}` |
+| 审计和设置 | `GET/DELETE /api/audit-logs`、`GET/PUT /api/settings`、`POST /api/settings/backup`、`POST /api/settings/restore` |
| GeoIP | `POST /api/geoip/download` |
-| 移动端推送 | `POST /api/mobile/pair`、`POST /api/mobile/push/register`、`POST /api/mobile/push/unregister` |
备份端点返回原始 SQLite 下载;恢复端点接受 `Content-Type: application/octet-stream` 的原始 SQLite body,并要求重启 Server。两者都是仅限 Admin 的高风险操作,已验证命令与保护措施见[备份与恢复](/zh/docs/deployment#备份与恢复)。
diff --git a/apps/docs/content/docs/zh/architecture.mdx b/apps/docs/content/docs/zh/architecture.mdx
index ea7e2d915..9c5ce1a25 100644
--- a/apps/docs/content/docs/zh/architecture.mdx
+++ b/apps/docs/content/docs/zh/architecture.mdx
@@ -90,8 +90,8 @@ icon: Boxes
|------|------|
| Collector | 使用 `sysinfo` 库周期性采集 CPU、内存、磁盘、网络等系统指标 |
| Reporter | 管理 WebSocket 连接、上报指标、处理 Server 下发的指令、断线重连 |
-| ProbeManager | 执行 Server 下发的 ICMP/TCP/HTTP 探测任务 |
-| Executor | 执行 Server 下发的远程 Shell 命令 |
+| PingManager | 执行 Server 下发的定时 ICMP/TCP/HTTP Ping 任务 |
+| NetworkProber | 执行定时网络质量探测并上报聚合结果 |
| Terminal | 管理 PTY 终端会话,转发终端输入输出 |
### Common(共享 crate,`crates/common`)
@@ -118,7 +118,7 @@ icon: Boxes
## 通信协议
-所有通信都使用 WebSocket。所有消息均为 JSON 文本帧;终端 I/O 数据放在消息的 `data` 字段中,以 base64 编码。当前协议版本为 `6`,连接时通过 `Welcome` 消息下发给 Agent。
+Agent 的指标上报和控制流量使用 WebSocket。浏览器通过 REST 获取初始数据、历史记录并执行管理操作,同时通过 WebSocket 接收实时更新。WebSocket 应用消息均为 JSON 文本帧;终端 I/O 数据放在消息的 `data` 字段中,以 base64 编码。当前 Agent 协议版本为 `6`,连接时通过 `Welcome` 消息下发。
Agent 连接到 `ws:///api/agent/ws`,并使用 `Authorization: Bearer ` 认证。Server 暂时兼容旧版 Agent 使用的 `?token=` 形式,以便其重新连接并完成升级。
@@ -180,7 +180,7 @@ Agent Server
|--- WebSocket 连接 + token ------>|
| | 验证 token,查找 server
|<-- Welcome { server_id, |
- | protocol_version: 4, |
+ | protocol_version: 6, |
| report_interval: 3 } -------|
| |
|--- SystemInfo { cpu_name, | 连接/重连后上报静态信息
@@ -296,15 +296,19 @@ Server 启动时通过 `tokio::spawn` 创建多个长期运行的后台任务:
## 安全模型
-### 三条认证路径
+### 认证路径
-ServerBee 支持三种认证机制,均在认证中间件中校验:
+面向用户的 REST 和 WebSocket 请求可通过 Session Cookie、API Key 或 Bearer Session Token 解析出用户,随后按该用户的当前角色进行授权。Agent WebSocket 连接使用独立的 Agent run token。
```
请求进入
+-- Cookie: session_token=xxx --> Session 认证(浏览器)
+-- Header: X-API-Key: serverbee_xxx --> API Key 认证(自动化)
- +-- Query: ?token=xxx --> Agent Token 认证(Agent WebSocket)
+ +-- Header: Authorization: Bearer xxx --> Session 认证(移动端)
+
+Agent WebSocket 连接
+ +-- Header: Authorization: Bearer xxx --> Agent run token 认证
+ +-- Query: ?token=xxx --> 仅用于兼容旧版 Agent
```
#### Session 认证(浏览器)
@@ -316,10 +320,11 @@ ServerBee 支持三种认证机制,均在认证中间件中校验:
#### API Key 认证(自动化)
-- 用户可创建具名 API 密钥用于程序化访问。
+- Admin 可创建具名 API 密钥用于程序化访问。
- Key 格式:`serverbee_` + 32 字节随机 base64url。
- 以 argon2 哈希存储,并保存 8 位明文前缀用于标识。
- 校验时先用前缀缩小查询范围,再用 argon2 验证。
+- API Key 会以所属用户的身份认证,并继承该用户当前的 `admin` 或 `member` 角色。API Key 是凭据类型,不是第三种角色。
#### Agent Token 认证(Agent)
@@ -332,22 +337,23 @@ ServerBee 支持三种认证机制,均在认证中间件中校验:
### 基于角色的访问控制(RBAC)
-存在两种角色:`admin`(完全访问)和 `member`(只读仪表盘访问)。
+用户只有两种角色:`admin`(完全访问)和 `member`(只读仪表盘访问)。Session Cookie、Bearer Session Token 和 API Key 都是这些用户的凭据,不会形成额外角色。
面向浏览器的 WebSocket 会在连接期间重新检查持久化 Session 或 API Key 以及用户角色。登出、删除 API Key、密码重置导致的 Session 吊销、删除账号或变更角色后,现有连接会在两秒内关闭,不会继续保留握手时的权限。
-| 资源 | Admin | Member | API Key | Agent |
-|------|-------|--------|---------|-------|
-| 服务器列表/详情 | 允许 | 允许(受限) | 允许 | - |
-| 服务器增删改 | 允许 | 禁止 | 允许 | - |
-| 告警规则管理 | 允许 | 禁止 | 允许 | - |
-| 通知配置 | 允许 | 禁止 | 禁止 | - |
-| 用户管理 | 允许 | 禁止 | 禁止 | - |
-| 系统设置 | 允许 | 禁止 | 禁止 | - |
-| 实时 WebSocket | 允许 | 允许 | 允许 | - |
-| Web 终端 | 允许 | 禁止 | 禁止 | - |
-| 远程命令 | 允许 | 禁止 | 允许 | - |
-| 指标上报 | - | - | - | 允许 |
+| 资源 | Admin 用户 | Member 用户 | Agent |
+|------|------------|-------------|-------|
+| 服务器列表/详情 | 允许 | 允许(受限) | - |
+| 服务器增删改 | 允许 | 禁止 | - |
+| 告警规则管理 | 允许 | 禁止 | - |
+| 通知配置 | 允许 | 禁止 | - |
+| 用户管理 | 允许 | 禁止 | - |
+| 系统设置 | 允许 | 禁止 | - |
+| 实时服务器更新 | 允许 | 允许 | - |
+| 文件管理器 | 允许 | 禁止 | - |
+| Web 终端 | 允许 | 禁止 | - |
+| 远程命令 | 允许 | 禁止 | - |
+| 指标上报 | - | - | 允许 |
### OAuth
@@ -392,15 +398,24 @@ ServerBee/
entity/ # sea-orm Entity(每表一个模块)
migration/ # 数据库迁移
middleware/ # 认证、日志中间件
- agent/ # Agent 二进制(collector、reporter、terminal)
+ agent/ # Agent 二进制(采集、上报、控制)
src/
main.rs
config.rs
collector/ # 各指标采集器
- reporter.rs # WebSocket 上报 + 重连
- probe/ # ICMP/TCP/HTTP 探测
- executor.rs # 远程命令执行
+ reporter/ # WebSocket 上报、重连、指令和文件操作
+ pinger.rs # ICMP/TCP/HTTP Ping 任务
+ probe_utils.rs # 共用探测校验和网络辅助逻辑
+ network_prober.rs # 定时网络探测
+ traceroute.rs # Traceroute 执行
terminal.rs # PTY 终端
+ file_manager.rs # 受限远程文件操作
+ docker/ # Docker 检查、操作和日志流
+ security/ # 主机安全事件检测
+ firewall/ # 防火墙拦截管理
+ ip_quality/ # IP 质量检测
+ upgrade.rs # 自升级协调逻辑
+ upgrade/ # 事务式自升级支持
apps/
web/ # React SPA 前端(Vite、TanStack、shadcn/ui)
docs/ # 文档站点(TanStack Start + Fumadocs)
diff --git a/apps/docs/content/docs/zh/file-manager.mdx b/apps/docs/content/docs/zh/file-manager.mdx
index 592448f00..c62e17017 100644
--- a/apps/docs/content/docs/zh/file-manager.mdx
+++ b/apps/docs/content/docs/zh/file-manager.mdx
@@ -6,6 +6,8 @@ icon: FolderOpen
文件管理器通过 ServerBee Agent 提供受控的远程文件系统访问能力,适合查看日志、编辑小型配置文件、传输文件等运维场景,无需打开完整终端。
+文件管理器仅限 Admin 使用。除修改文件的操作外,浏览、获取元数据、读取、下载和传输管理同样需要 Admin 权限。
+
文件管理器属于高风险功能。请仅在可信服务器上启用,并将 `root_paths` 限制到最小必要目录。
@@ -57,10 +59,7 @@ SERVERBEE_FILE__MAX_FILE_SIZE=1073741824
## 权限
-| 角色 | 允许操作 |
-|------|----------|
-| Admin | 浏览、stat、读取、写入、上传、下载、删除、移动、新建目录、取消传输 |
-| Member | 浏览、stat、读取、下载、查看自己的传输 |
+文件管理器仅限 Admin 使用。所有文件 API 端点都经过 Admin 权限检查,因为即使是读取操作,也可能暴露受管主机上的敏感文件。
所有高风险文件操作都会写入审计日志,包括因 capability 关闭而被拒绝的尝试。
@@ -101,7 +100,7 @@ Agent 在访问文件系统前会执行路径安全检查:
## API
-读取类端点对 Admin 和 Member 均可用。写入类端点需要 Admin。
+所有文件管理端点都需要 Admin 权限,包括列目录、获取元数据、读取、下载和传输管理端点。
| 方法 | 路径 | 说明 |
|------|------|------|
From 71065a324de463b326a599d0ce0cfcc399df263c Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:20:19 +0800
Subject: [PATCH 04/11] docs: correct agent configuration facts
---
apps/docs/content/docs/en/agent.mdx | 17 ++++++-----
apps/docs/content/docs/en/capabilities.mdx | 2 +-
apps/docs/content/docs/en/configuration.mdx | 32 +++++++++++----------
apps/docs/content/docs/en/ip-quality.mdx | 20 ++++++-------
apps/docs/content/docs/en/ping.mdx | 10 +++++--
apps/docs/content/docs/zh/agent.mdx | 17 ++++++-----
apps/docs/content/docs/zh/capabilities.mdx | 2 +-
apps/docs/content/docs/zh/configuration.mdx | 32 +++++++++++----------
apps/docs/content/docs/zh/ip-quality.mdx | 20 ++++++-------
apps/docs/content/docs/zh/ping.mdx | 10 +++++--
apps/docs/scripts/check-contracts.ts | 14 +++++++++
11 files changed, 104 insertions(+), 72 deletions(-)
diff --git a/apps/docs/content/docs/en/agent.mdx b/apps/docs/content/docs/en/agent.mdx
index 7799f1864..f29b8bdcc 100644
--- a/apps/docs/content/docs/en/agent.mdx
+++ b/apps/docs/content/docs/en/agent.mdx
@@ -8,7 +8,7 @@ The ServerBee agent is a lightweight Rust binary that runs on each server you wa
## What the Agent Does
-- Collects system metrics every 3 seconds (configurable), including disk I/O throughput on all platforms
+- Collects system metrics every 3 seconds, including disk I/O throughput on all platforms
- Reports metrics to the server over WebSocket
- Executes ping probes (ICMP, TCP, HTTP) assigned by the server
- Provides a PTY shell for web terminal access
@@ -151,7 +151,7 @@ token = ""
- Receive only `server_id`; the Server hashes the proposed token while consuming the offer
- Connect via WebSocket using the token for all future sessions -- the enrollment code is no longer needed
-On subsequent runs (token present), the agent connects directly over WebSocket, sends its static system info, and reports metrics on the configured interval.
+On subsequent runs (token present), the agent connects directly over WebSocket, sends its static system info, and reports metrics on the interval assigned by the server.
If the HTTP result is ambiguous, the Agent first tries WebSocket authentication with the already-staged token. Success proves the claim committed; rejection permits retrying the same code and token. If a code was lost, use the exact Outstanding offer shown on the existing Server to replace it. Replacement is compare-and-swap by offer ID; it never silently replaces an unknown newer offer.
@@ -196,8 +196,7 @@ token = ""
enrollment_code = ""
[collector]
-interval = 3 # Metric collection interval in seconds (can be overridden by the server's Welcome message)
-enable_gpu = false # Enable NVIDIA GPU monitoring (requires nvidia-smi)
+enable_gpu = false # Enable NVIDIA GPU monitoring (requires a GPU-enabled build and NVIDIA NVML)
enable_temperature = true # Enable temperature sensor monitoring
[log]
@@ -210,7 +209,6 @@ file = "" # Log file path (empty = stdout only)
| `server_url` | string | required | URL of your ServerBee server |
| `enrollment_code` | string | `""` | One-time enrollment code, needed only for first registration; consumed on success and unused once a token is present |
| `token` | string | Agent-generated | Agent run token, atomically written before the claim request; the Server stores only its hash |
-| `collector.interval` | int | `3` | Metric collection interval in seconds; can be overridden by the server's Welcome message |
| `collector.enable_gpu` | bool | `false` | Enable GPU metric collection |
| `collector.enable_temperature` | bool | `true` | Enable temperature collection |
| `log.level` | string | `"info"` | Log level |
@@ -223,10 +221,13 @@ Like the server, all options support `SERVERBEE_` prefixed environment variables
```bash
export SERVERBEE_SERVER_URL="http://your-server-ip:9527"
export SERVERBEE_TOKEN="your-agent-token"
-export SERVERBEE_COLLECTOR__INTERVAL=5
export SERVERBEE_COLLECTOR__ENABLE_GPU=true
```
+
+The server currently sends a 3-second `report_interval` in its `Welcome` message, and the Agent uses that value for its reporting loop. `collector.interval` and `SERVERBEE_COLLECTOR__INTERVAL` are still accepted for backward compatibility but do not change the active cadence.
+
+
## Local Capability Locks
Capability policy is owned entirely by the Agent host and can be changed with local configuration or CLI flags:
@@ -250,7 +251,7 @@ NVIDIA GPU monitoring is disabled by default and requires all three of the follo
```bash
cargo build --release -p serverbee-agent --features gpu
```
-2. **Runtime** -- NVIDIA drivers and the NVML library installed on the host
+2. **Runtime** -- an NVIDIA driver that provides the NVML shared library on the host
3. **Config** -- set `enable_gpu = true`:
```toml
[collector]
@@ -273,6 +274,8 @@ These metrics appear in the server dashboard and can be used in alert rules.
Only NVIDIA GPUs are supported (via the `nvml-wrapper` library). AMD and Intel GPU support is planned for a future release.
+The Agent calls NVML through `nvml-wrapper`; it does not run `nvidia-smi`.
+
## Running as a Systemd Service
For production deployments, run the agent as a systemd service so it starts automatically on boot. The install script creates this service automatically; to configure it manually, create `/etc/systemd/system/serverbee-agent.service`:
diff --git a/apps/docs/content/docs/en/capabilities.mdx b/apps/docs/content/docs/en/capabilities.mdx
index 2af484237..8b466448a 100644
--- a/apps/docs/content/docs/en/capabilities.mdx
+++ b/apps/docs/content/docs/en/capabilities.mdx
@@ -41,7 +41,7 @@ File Manager requires additional agent-side configuration (`root_paths`, `deny_p
| **HTTP Probe** | `ping_http` | `CAP_PING_HTTP` (32) | Allow HTTP probe tasks |
| **Security Events** | `security_events` | `CAP_SECURITY_EVENTS` (256) | Allow agent to report SSH login / brute-force / port-scan events (see [Security Events](/en/docs/security-events)) |
| **Firewall Blocklist** | `firewall_block` | `CAP_FIREWALL_BLOCK` (512) | Allow agent to apply the server-pushed nftables blocklist. Requires root or `CAP_NET_ADMIN` plus the `nft` CLI on the host. See [Firewall Blocklist](/en/docs/firewall) |
-| **IP Quality** | `ip_quality` | `CAP_IP_QUALITY` (1024) | Allow agent to query third-party IP quality APIs for outbound IP scoring |
+| **IP Quality** | `ip_quality` | `CAP_IP_QUALITY` (1024) | Allow the Agent to run service-unlock probes and report results; optional metadata and risk enrichment runs on the Server |
An agent with no `[capabilities]` overrides defaults to `1852` (auto upgrade + the three ping probes + security events + firewall blocklist + IP quality), which keeps the high-risk terminal, exec, file, and Docker capabilities off.
diff --git a/apps/docs/content/docs/en/configuration.mdx b/apps/docs/content/docs/en/configuration.mdx
index 7acbae765..16e732a5f 100644
--- a/apps/docs/content/docs/en/configuration.mdx
+++ b/apps/docs/content/docs/en/configuration.mdx
@@ -8,15 +8,17 @@ ServerBee loads configuration with [Figment](https://github.com/SergioBenitez/Fi
## Configuration Loading Priority
-Values are merged from the sources below. Later sources override earlier ones, so environment variables always win:
+Server values are merged in this order, with later sources overriding earlier ones:
1. Built-in defaults
-2. `/etc/serverbee/server.toml` or `/etc/serverbee/agent.toml`
-3. `/opt/serverbee/etc/server.toml` or `/opt/serverbee/etc/agent.toml` (the path used by the install script)
-4. `server.toml` or `agent.toml` in the working directory
+2. `/etc/serverbee/server.toml`
+3. `/opt/serverbee/etc/server.toml` (the path used by the install script)
+4. `server.toml` in the working directory
5. Environment variables prefixed with `SERVERBEE_`
-This lets you override any single value at runtime without editing the TOML file.
+The Agent uses built-in defaults, then `/etc/serverbee/agent.toml`, `agent.toml` in its working directory, and finally `SERVERBEE_` environment variables. The managed service uses `/opt/serverbee/etc` as its working directory, so its third source is `/opt/serverbee/etc/agent.toml`.
+
+This lets you override effective settings at runtime without editing the TOML file. Compatibility-only fields, such as the Agent's `collector.interval`, are called out below.
## Environment Variable Mapping
@@ -128,13 +130,13 @@ Tier-2 guardrail for the [Firewall Blocklist](/en/docs/firewall) feature. CIDRs
#### IP Quality
-Default risk-scoring works out of the box via [ipapi.is](https://ipapi.is) (no API key required, ~1000 requests/day per source IP). On primary failure the server falls back to [ip-api.com](https://ip-api.com), which provides geo + proxy/hosting flags but no risk score. See [IP Quality](/en/docs/ip-quality) for feature details.
+By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonymously (100 requests per client IP per UTC day with a minimal response). A free account/API key provides 1,000 requests per day and the full response. On primary failure, the Server falls back to [ip-api.com](https://ip-api.com), which provides proxy/hosting flags and a derived IP type but no numeric risk score. See [IP Quality](/en/docs/ip-quality) for details and the fallback's non-commercial/HTTP warning.
| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER` | `"ipapi_is"` | Primary risk provider. One of: `none`, `ipapi_is`, `ip-api`. |
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER_FALLBACK` | `"ip-api"` | Fallback provider triggered on primary failure. Set to `none` to disable. |
-| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | Optional. Configure for higher per-account rate limits. |
+| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | Optional. A free account key provides 1,000 requests per day and the full response. |
| `SERVERBEE_IP_QUALITY__IPAPI_IS__ENDPOINT` | `""` | Override for self-hosted mirrors or testing. Empty falls back to the built-in default `https://api.ipapi.is`. |
@@ -164,6 +166,7 @@ Average-latency cutoffs used to classify network-probe records returned by `/api
| `SERVERBEE_RATE_LIMIT__REGISTER_MAX` | `10` | Max agent registrations per IP within 15-minute window. Admins can clear an active window from Settings → Rate limits |
| `SERVERBEE_UPGRADE__RELEASE_BASE_URL` | `https://github.com/ZingerLittleBee/ServerBee/releases` | Base URL for agent upgrade release assets |
| `SERVERBEE_UPGRADE__LATEST_VERSION_URL` | `""` | Optional custom URL for latest version API. If empty, uses GitHub API |
+| `SERVERBEE_UPGRADE__CHANNEL` | `stable` | Release channel used by the dashboard version check: `stable` or `beta` |
| `SERVERBEE_FILE__MAX_UPLOAD_SIZE` | `104857600` | Maximum file upload size in bytes (default 100 MB) |
### Agent Environment Variables
@@ -181,8 +184,8 @@ Agent top-level keys use single underscore. Nested keys use `__` (double undersc
| Environment Variable | Default | Description |
|---------------------|---------|-------------|
-| `SERVERBEE_COLLECTOR__INTERVAL` | `3` | Metric report interval in seconds |
-| `SERVERBEE_COLLECTOR__ENABLE_GPU` | `false` | Enable NVIDIA GPU monitoring (requires the NVIDIA driver / NVML) |
+| `SERVERBEE_COLLECTOR__INTERVAL` | `3` | Compatibility-only field. The Agent currently ignores it and uses the Server's `Welcome.report_interval` value, which is fixed at 3 seconds |
+| `SERVERBEE_COLLECTOR__ENABLE_GPU` | `false` | Enable NVIDIA GPU monitoring (requires a build with the `gpu` feature and an NVIDIA driver that provides NVML) |
| `SERVERBEE_COLLECTOR__ENABLE_TEMPERATURE` | `true` | Enable CPU temperature monitoring |
| `SERVERBEE_FILE__ENABLED` | `false` | Enable file management on this agent |
| `SERVERBEE_FILE__ROOT_PATHS` | `[]` | Allowed root paths (comma-separated, e.g. `/home,/var/log`). Empty rejects all file operations |
@@ -380,7 +383,7 @@ The log level can also be set via the `RUST_LOG` environment variable, which tak
### `[ip_quality]` -- IP Quality Risk Scoring
-Default risk-scoring works out of the box via [ipapi.is](https://ipapi.is) (no API key required, ~1000 requests/day per source IP). On primary failure the server falls back to [ip-api.com](https://ip-api.com). Baseline IP metadata (country, ASN, IP type) is always derived from the local GeoIP MMDB. See [IP Quality](/en/docs/ip-quality) for feature details.
+By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonymously (100 requests per client IP per UTC day with a minimal response). A free account/API key provides 1,000 requests per day and the full response. On primary failure, the Server falls back to [ip-api.com](https://ip-api.com). Local baseline metadata is available only when a usable GeoIP MMDB is loaded. See [IP Quality](/en/docs/ip-quality) for details and the fallback's non-commercial/HTTP warning.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
@@ -391,7 +394,7 @@ Default risk-scoring works out of the box via [ipapi.is](https://ipapi.is) (no A
| Key | Type | Default | Description |
|-----|------|---------|-------------|
-| `api_key` | string | -- | Optional. Configure for higher per-account rate limits. |
+| `api_key` | string | -- | Optional. A free account key provides 1,000 requests per day and the full response. |
| `endpoint` | string | `""` | Override for self-hosted mirrors or testing. Empty falls back to the built-in default `https://api.ipapi.is`. |
### `[network_probe]` -- Anomaly Thresholds
@@ -417,8 +420,8 @@ Default risk-scoring works out of the box via [ipapi.is](https://ipapi.is) (no A
| Key | Type | Default | Description |
|-----|------|---------|-------------|
-| `interval` | u32 | `3` | Collection interval in seconds |
-| `enable_gpu` | bool | `false` | Enable NVIDIA GPU monitoring (requires the NVIDIA driver / NVML) |
+| `interval` | u32 | `3` | Compatibility-only field. The Agent currently ignores it and uses the Server's `Welcome.report_interval`, fixed at 3 seconds |
+| `enable_gpu` | bool | `false` | Enable NVIDIA GPU monitoring (requires a build with the `gpu` feature and an NVIDIA driver that provides NVML) |
| `enable_temperature` | bool | `true` | Enable CPU temperature sensor monitoring |
### `[file]` -- File Management
@@ -563,8 +566,7 @@ server_url = "https://monitor.example.com"
token = ""
[collector]
-interval = 3
-enable_gpu = true
+enable_gpu = true # Requires a GPU-enabled build and an NVIDIA driver that provides NVML
enable_temperature = true
[file]
diff --git a/apps/docs/content/docs/en/ip-quality.mdx b/apps/docs/content/docs/en/ip-quality.mdx
index 52a19dc1c..dee922cde 100644
--- a/apps/docs/content/docs/en/ip-quality.mdx
+++ b/apps/docs/content/docs/en/ip-quality.mdx
@@ -14,7 +14,7 @@ Results appear on a dedicated **IP Quality** sidebar route (global overview), a
## Requirements
- **`CAP_IP_QUALITY`** (bit `1024`) must be enabled on the agent. It is **on by default** (part of `CAP_DEFAULT`).
-- A GeoIP MMDB database is recommended for complete IP metadata. Without it, only basic location data is available. See the [Configuration](/en/docs/configuration) page for `geoip.mmdb_path`.
+- A usable GeoIP MMDB is required for local baseline metadata. If no database is loaded, those fields remain unavailable; third-party enrichment can still provide supported risk and network-type fields. See [Configuration](/en/docs/configuration) for `geoip.mmdb_path`.
## Capability (agent-owned)
@@ -79,10 +79,10 @@ Check schedules are agent-side. The server only pushes the service catalog and f
After receiving unlock results from an agent, the server:
-1. **Derives baseline metadata** from the local GeoIP MMDB — country, region, city, ASN, AS organization, and IP type (residential / datacenter / hosting / mobile / ISP / unknown). This always works, with no external call required.
+1. **Derives available baseline metadata** from a loaded local GeoIP MMDB, without an external request. The downloadable DB-IP Lite Country database provides country and region. If the MMDB is missing, unreadable, or does not cover the address, local baseline fields remain empty.
2. **Optionally queries a third-party risk provider** (if configured) to obtain a 0–100 fraud risk score, risk level (`low` / `medium` / `high`), and proxy/VPN/hosting flags. Results are cached by IP for 24 hours, so repeated checks of the same IP do not generate repeated API calls.
-If no risk provider is configured, `risk_score` and `risk_level` are `null` / `unknown`, and the UI shows only the GeoIP-derived metadata.
+If no provider returns a numeric score, `risk_score` and `risk_level` are `null` / `unknown`. Local metadata appears only when a usable MMDB is loaded.
### Configuring a Risk Provider
@@ -90,13 +90,13 @@ Set `ip_quality.risk_provider` in `server.toml` (or `SERVERBEE_IP_QUALITY__RISK_
| Provider | Value | Notes |
|----------|-------|-------|
-| ipapi.is (default) | `ipapi_is` | ~1000 requests/day per source IP at no charge. Optional paid API key for higher limits. |
-| ip-api.com (fallback) | `ip-api` | **Free tier, non-commercial use only. HTTP-only endpoint.** No API key. Geo + proxy flags only — no risk score. |
-| Disabled | `none` | GeoIP metadata only, no risk score. |
+| ipapi.is (default) | `ipapi_is` | Anonymous: 100 requests per client IP per UTC day with a minimal response. Free account/API key: 1,000 requests per day with the full response. |
+| ip-api.com (fallback) | `ip-api` | **Free tier, non-commercial use only. HTTP-only endpoint.** No API key. Proxy/hosting flags and a derived IP type, but no numeric risk score. |
+| Disabled | `none` | Local GeoIP metadata only when a usable MMDB is loaded; no risk score. |
-**Default behavior** (no configuration needed): the server uses `ipapi_is` as the primary provider and `ip-api` as the automatic fallback when the primary fails. No API key is required to get started.
+**Default behavior** (no configuration needed): the Server calls ipapi.is anonymously. Anonymous access is limited to 100 requests per client IP per UTC day and returns a minimal response. If that request fails, including after quota exhaustion, the Server tries `ip-api`. The fallback can supply proxy/hosting flags and a derived IP type, but not a numeric risk score. See the official [ipapi.is developer limits](https://ipapi.is/developers.html) and [pricing](https://ipapi.is/pricing.html).
-To use an optional ipapi.is API key for higher rate limits:
+Create a free ipapi.is account and configure its API key for 1,000 requests per day and the full response:
```toml
[ip_quality]
@@ -156,7 +156,7 @@ For unauthenticated visitors, the egress IP is masked as `*.*.*.*`. Authenticate
## Retention
-Status-change history events (the `unlock_event` log) are retained for **90 days** by default. Tune with `retention.ip_quality_event_days` (env: `SERVERBEE_RETENTION__IP_QUALITY_EVENT_DAYS`). The daily cleanup task purges expired rows automatically.
+Status-change history events (the `unlock_event` log) are retained for **90 days** by default. Tune with `retention.ip_quality_event_days` (env: `SERVERBEE_RETENTION__IP_QUALITY_EVENT_DAYS`). The hourly cleanup task purges expired rows automatically.
The latest unlock result and IP quality snapshot per server are kept indefinitely (they are replaced on each run, not accumulated).
@@ -164,7 +164,7 @@ The latest unlock result and IP quality snapshot per server are kept indefinitel
```text
Agent
- │ (CAP_IP_QUALITY effective + --allow-cap ip_quality)
+ │ (CAP_IP_QUALITY effective)
├─ runs unlock checks every interval_hours / on IP change / on RunNow
│ AgentMessage::UnlockResults (WebSocket)
▼
diff --git a/apps/docs/content/docs/en/ping.mdx b/apps/docs/content/docs/en/ping.mdx
index 59f602613..ff72a4a89 100644
--- a/apps/docs/content/docs/en/ping.mdx
+++ b/apps/docs/content/docs/en/ping.mdx
@@ -12,8 +12,8 @@ Three probe types are available. Each measures round-trip latency and reports a
| Type | What it does | Target format | Timeout | Privileges |
|------|--------------|---------------|---------|------------|
-| **ICMP** | Standard ICMP echo request/reply | IP or hostname (e.g., `8.8.8.8`, `google.com`) | 5 s | `CAP_NET_RAW` on Linux |
-| **TCP** | TCP connection test (handshake) | `host:port` (e.g., `google.com:443`) | 5 s | None |
+| **ICMP** | Standard ICMP echo request/reply | IP or hostname (e.g., `8.8.8.8`, `google.com`) | 10 s | `CAP_NET_RAW` on Linux |
+| **TCP** | TCP connection test (handshake) | `host:port` (e.g., `google.com:443`) | 10 s | None |
| **HTTP** | HTTP(S) GET request and response time | Full URL (e.g., `https://example.com/health`) | 10 s | None |
## Creating Ping Tasks
@@ -111,7 +111,11 @@ In the web dashboard, ping task create/delete/enable/disable actions show locali
The `server_ids_json` field controls which agents execute the probe:
- **Specific servers** -- Provide an array of server IDs: `["srv-1", "srv-2"]`
-- **All servers** -- Use an empty array `[]` or the special value `["*"]` to assign to all connected agents
+- **All servers** -- Use an empty array `[]`
+
+
+`["*"]` is not a wildcard. It is treated as a literal server ID and normally assigns the task to no agents. Use `[]` when every server should run the probe, or provide IDs copied from the server list.
+
Running the same probe from multiple agents lets you:
diff --git a/apps/docs/content/docs/zh/agent.mdx b/apps/docs/content/docs/zh/agent.mdx
index e5b31f3d1..1b2281286 100644
--- a/apps/docs/content/docs/zh/agent.mdx
+++ b/apps/docs/content/docs/zh/agent.mdx
@@ -8,7 +8,7 @@ Agent 是部署在被监控服务器上的轻量级 Rust 二进制程序,运
## Agent 的职责
-- 每 3 秒(可配置)采集一次系统指标并上报至 Server,全平台支持磁盘 I/O 吞吐量采集
+- 每 3 秒采集一次系统指标并上报至 Server,全平台支持磁盘 I/O 吞吐量采集
- 通过 WebSocket 将指标上报至 Server
- 执行 Server 下发的 Ping 探测任务(ICMP、TCP、HTTP)
- 提供 PTY Shell 终端会话供 Web 终端远程操作
@@ -151,7 +151,7 @@ token = ""
- 只接收 `server_id`;Server 在消费 Offer 的同一事务中保存 token 哈希
- 后续所有会话都使用 token 通过 WebSocket 连接——注册码不再需要
-后续运行(已有 token)时,Agent 直接通过 WebSocket 连接,发送静态系统信息,并按配置的间隔周期上报指标。
+后续运行(已有 token)时,Agent 直接通过 WebSocket 连接,发送静态系统信息,并按 Server 指定的间隔上报指标。
若 HTTP 结果不明确,Agent 会先用已落盘的 token 尝试 WebSocket。成功即可证明 claim 已提交;被拒绝时仍可用同一 code/token 重试。若注册码丢失,应在既有 Server 上按可见的精确 Offer ID 替换 Outstanding Offer,不会发生无条件覆盖。
@@ -196,8 +196,7 @@ token = ""
enrollment_code = ""
[collector]
-interval = 3 # 指标采集间隔,单位秒(可被 Server 的 Welcome 消息覆盖)
-enable_gpu = false # 启用 NVIDIA GPU 监控(需要 nvidia-smi)
+enable_gpu = false # 启用 NVIDIA GPU 监控(需要启用 GPU 特性的构建和 NVIDIA NVML)
enable_temperature = true # 启用温度传感器监控
[log]
@@ -210,7 +209,6 @@ file = "" # 日志文件路径(留空仅输出到 stdout
| `server_url` | string | 必填 | ServerBee Server 的地址 |
| `enrollment_code` | string | `""` | 一次性注册码,仅首次注册时需要;注册成功后即被消费,拥有 token 后无需再填 |
| `token` | string | Agent 生成 | Agent 在 claim 前原子写入的 run token;Server 仅保存哈希 |
-| `collector.interval` | int | `3` | 指标采集间隔,单位秒;可被 Server 的 Welcome 消息覆盖 |
| `collector.enable_gpu` | bool | `false` | 是否启用 GPU 指标采集 |
| `collector.enable_temperature` | bool | `true` | 是否启用温度采集 |
| `log.level` | string | `"info"` | 日志级别 |
@@ -223,10 +221,13 @@ file = "" # 日志文件路径(留空仅输出到 stdout
```bash
export SERVERBEE_SERVER_URL="http://your-server-ip:9527"
export SERVERBEE_TOKEN="your-agent-token"
-export SERVERBEE_COLLECTOR__INTERVAL=5
export SERVERBEE_COLLECTOR__ENABLE_GPU=true
```
+
+Server 当前在 `Welcome` 消息中下发 3 秒的 `report_interval`,Agent 使用该值启动上报循环。`collector.interval` 和 `SERVERBEE_COLLECTOR__INTERVAL` 仍为向后兼容而保留,但不会改变实际的上报周期。
+
+
## Agent 本地功能锁定
能力策略完全由 Agent 主机拥有,可通过本地配置或 CLI 参数调整:
@@ -250,7 +251,7 @@ NVIDIA GPU 指标采集默认关闭,需同时满足以下三个条件:
```bash
cargo build --release -p serverbee-agent --features gpu
```
-2. **运行时**:宿主机安装了 NVIDIA 驱动和 NVML 库
+2. **运行时**:宿主机安装了可提供 NVML 共享库的 NVIDIA 驱动
3. **配置中**:设置 `enable_gpu = true`
```toml
[collector]
@@ -273,6 +274,8 @@ NVIDIA GPU 指标采集默认关闭,需同时满足以下三个条件:
目前仅支持 NVIDIA GPU(通过 `nvml-wrapper` 库)。AMD 和 Intel GPU 的支持计划在后续版本中加入。
+Agent 通过 `nvml-wrapper` 直接调用 NVML,不会执行 `nvidia-smi`。
+
## 作为 systemd 服务运行
生产环境建议将 Agent 作为 systemd 服务运行,以便开机自启。安装脚本会自动创建该服务;如需手动配置,创建 `/etc/systemd/system/serverbee-agent.service`:
diff --git a/apps/docs/content/docs/zh/capabilities.mdx b/apps/docs/content/docs/zh/capabilities.mdx
index 3b5b38471..f871daf5b 100644
--- a/apps/docs/content/docs/zh/capabilities.mdx
+++ b/apps/docs/content/docs/zh/capabilities.mdx
@@ -41,7 +41,7 @@ ServerBee 定义了 11 个功能位,分为两个风险等级,有效掩码为
| **HTTP 探测** | `ping_http` | `CAP_PING_HTTP` (32) | 允许 HTTP 探测任务 |
| **安全事件** | `security_events` | `CAP_SECURITY_EVENTS` (256) | 允许 Agent 上报 SSH 登录 / 暴力破解 / 端口扫描事件(见 [安全事件](/zh/docs/security-events)) |
| **防火墙封禁** | `firewall_block` | `CAP_FIREWALL_BLOCK` (512) | 允许 Agent 应用 Server 下发的 nftables 封禁列表。需要 root 或 `CAP_NET_ADMIN` 及主机上的 `nft` 命令。见 [防火墙封禁](/zh/docs/firewall) |
-| **IP 质量** | `ip_quality` | `CAP_IP_QUALITY` (1024) | 允许 Agent 调用第三方 IP 质量 API 对出口 IP 评分 |
+| **IP 质量** | `ip_quality` | `CAP_IP_QUALITY` (1024) | 允许 Agent 运行服务解锁探测并上报结果;可选的元数据与风险评分由 Server 完成 |
没有任何 `[capabilities]` 覆盖的 Agent 默认值为 `1852`(自动升级 + 三种 ping 探测 + 安全事件 + 防火墙封禁 + IP 质量),即高风险的终端、执行、文件和 Docker 能力保持关闭。
diff --git a/apps/docs/content/docs/zh/configuration.mdx b/apps/docs/content/docs/zh/configuration.mdx
index 69c07534d..012f3a9b9 100644
--- a/apps/docs/content/docs/zh/configuration.mdx
+++ b/apps/docs/content/docs/zh/configuration.mdx
@@ -8,15 +8,17 @@ ServerBee 使用 [Figment](https://github.com/SergioBenitez/Figment) 加载配
## 配置加载优先级
-配置值从以下来源合并,后者覆盖前者,因此环境变量始终具有最高优先级:
+Server 按以下顺序合并配置,后者覆盖前者:
1. 内置默认值
-2. `/etc/serverbee/server.toml` 或 `/etc/serverbee/agent.toml`
-3. `/opt/serverbee/etc/server.toml` 或 `/opt/serverbee/etc/agent.toml`(安装脚本使用的路径)
-4. 工作目录下的 `server.toml` 或 `agent.toml`
+2. `/etc/serverbee/server.toml`
+3. `/opt/serverbee/etc/server.toml`(安装脚本使用的路径)
+4. 工作目录下的 `server.toml`
5. 以 `SERVERBEE_` 为前缀的环境变量
-这样无需修改 TOML 文件即可在运行时覆盖任意单个配置项。
+Agent 依次使用内置默认值、`/etc/serverbee/agent.toml`、工作目录下的 `agent.toml`,最后应用 `SERVERBEE_` 环境变量。托管服务将 `/opt/serverbee/etc` 设为工作目录,因此其第三个来源是 `/opt/serverbee/etc/agent.toml`。
+
+这样无需修改 TOML 文件即可在运行时覆盖会生效的配置项。仅为兼容保留的字段(例如 Agent 的 `collector.interval`)会在下文明确标注。
## 环境变量映射规则
@@ -128,13 +130,13 @@ ServerBee 使用 [Figment](https://github.com/SergioBenitez/Figment) 加载配
#### IP 质量检测(IP Quality)
-默认开箱即用,通过 [ipapi.is](https://ipapi.is)(无需 API Key,按源 IP 限约 1000 次 / 天)获取风险评分。主 Provider 失败时自动回退到 [ip-api.com](https://ip-api.com)(提供地理 + 代理 / 托管标志,无风险评分)。功能详情见 [IP 质量检测](/zh/docs/ip-quality)。
+Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html)(每个客户端 IP 每 UTC 日 100 次,返回精简响应)。免费账号/API Key 提供每日 1,000 次请求和完整响应。主 Provider 失败时,Server 回退到 [ip-api.com](https://ip-api.com),后者提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。功能详情及兜底服务的非商业用途/HTTP 警告见 [IP 质量检测](/zh/docs/ip-quality)。
| 环境变量 | 默认值 | 说明 |
|----------|--------|------|
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER` | `"ipapi_is"` | 主风险评分 Provider。可选:`none`、`ipapi_is`、`ip-api`。 |
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER_FALLBACK` | `"ip-api"` | 主 Provider 失败时的兜底。设为 `none` 关闭。 |
-| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | 可选。配置后享受更高的账户级速率限制。 |
+| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | 可选。免费账号的 Key 提供每日 1,000 次请求和完整响应。 |
| `SERVERBEE_IP_QUALITY__IPAPI_IS__ENDPOINT` | `""` | 自建镜像或测试时覆盖。留空则回退到内置默认 `https://api.ipapi.is`。 |
@@ -164,6 +166,7 @@ ServerBee 使用 [Figment](https://github.com/SergioBenitez/Figment) 加载配
| `SERVERBEE_RATE_LIMIT__REGISTER_MAX` | `10` | 15 分钟窗口内每 IP 最大 Agent 注册次数。管理员可在「设置 → 速率限制」中清除活跃窗口 |
| `SERVERBEE_UPGRADE__RELEASE_BASE_URL` | `https://github.com/ZingerLittleBee/ServerBee/releases` | Agent 升级 Release 资产的基础 URL |
| `SERVERBEE_UPGRADE__LATEST_VERSION_URL` | `""` | 可选的自定义最新版本 API URL,留空则使用 GitHub API |
+| `SERVERBEE_UPGRADE__CHANNEL` | `stable` | 控制台版本检测使用的发布渠道:`stable` 或 `beta` |
| `SERVERBEE_FILE__MAX_UPLOAD_SIZE` | `104857600` | 文件上传最大大小(字节),默认 100 MB |
### Agent 环境变量
@@ -181,8 +184,8 @@ Agent 顶层键使用单下划线,嵌套键使用 `__`(双下划线)。
| 环境变量 | 默认值 | 说明 |
|----------|--------|------|
-| `SERVERBEE_COLLECTOR__INTERVAL` | `3` | 指标上报间隔(秒) |
-| `SERVERBEE_COLLECTOR__ENABLE_GPU` | `false` | 启用 NVIDIA GPU 监控(需要 NVIDIA 驱动 / NVML) |
+| `SERVERBEE_COLLECTOR__INTERVAL` | `3` | 仅为兼容保留。Agent 当前忽略此值,使用 Server 下发且固定为 3 秒的 `Welcome.report_interval` |
+| `SERVERBEE_COLLECTOR__ENABLE_GPU` | `false` | 启用 NVIDIA GPU 监控(需要启用 `gpu` 特性的构建,以及提供 NVML 的 NVIDIA 驱动) |
| `SERVERBEE_COLLECTOR__ENABLE_TEMPERATURE` | `true` | 启用 CPU 温度监控 |
| `SERVERBEE_FILE__ENABLED` | `false` | 在该 Agent 上启用文件管理 |
| `SERVERBEE_FILE__ROOT_PATHS` | `[]` | 允许的根路径(逗号分隔,如 `/home,/var/log`)。留空则拒绝所有文件操作 |
@@ -380,7 +383,7 @@ Agent 顶层键使用单下划线,嵌套键使用 `__`(双下划线)。
### `[ip_quality]` —— IP 质量风险评分
-默认开箱即用,通过 [ipapi.is](https://ipapi.is)(无需 API Key,按源 IP 限约 1000 次 / 天)获取风险评分。主 Provider 失败时自动回退到 [ip-api.com](https://ip-api.com)。基础 IP 元数据(国家、ASN、IP 类型)始终来自本地 GeoIP MMDB。功能详情见 [IP 质量检测](/zh/docs/ip-quality)。
+Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html)(每个客户端 IP 每 UTC 日 100 次,返回精简响应)。免费账号/API Key 提供每日 1,000 次请求和完整响应。主 Provider 失败时,Server 回退到 [ip-api.com](https://ip-api.com)。只有加载了可用的 GeoIP MMDB,才会提供本地基础元数据。功能详情及兜底服务的非商业用途/HTTP 警告见 [IP 质量检测](/zh/docs/ip-quality)。
| 键 | 类型 | 默认值 | 说明 |
|----|------|--------|------|
@@ -391,7 +394,7 @@ Agent 顶层键使用单下划线,嵌套键使用 `__`(双下划线)。
| 键 | 类型 | 默认值 | 说明 |
|----|------|--------|------|
-| `api_key` | string | -- | 可选。配置后享受更高的账户级速率限制。 |
+| `api_key` | string | -- | 可选。免费账号的 Key 提供每日 1,000 次请求和完整响应。 |
| `endpoint` | string | `""` | 自建镜像或测试时覆盖。留空则回退到内置默认 `https://api.ipapi.is`。 |
### `[network_probe]` —— 异常阈值
@@ -417,8 +420,8 @@ Agent 顶层键使用单下划线,嵌套键使用 `__`(双下划线)。
| 键 | 类型 | 默认值 | 说明 |
|----|------|--------|------|
-| `interval` | u32 | `3` | 采集间隔(秒) |
-| `enable_gpu` | bool | `false` | 启用 NVIDIA GPU 监控(需要 NVIDIA 驱动 / NVML) |
+| `interval` | u32 | `3` | 仅为兼容保留。Agent 当前忽略此值,使用 Server 下发且固定为 3 秒的 `Welcome.report_interval` |
+| `enable_gpu` | bool | `false` | 启用 NVIDIA GPU 监控(需要启用 `gpu` 特性的构建,以及提供 NVML 的 NVIDIA 驱动) |
| `enable_temperature` | bool | `true` | 启用 CPU 温度传感器监控 |
### `[file]` —— 文件管理
@@ -563,8 +566,7 @@ server_url = "https://monitor.example.com"
token = ""
[collector]
-interval = 3
-enable_gpu = true
+enable_gpu = true # 需要启用 GPU 特性的构建,以及提供 NVML 的 NVIDIA 驱动
enable_temperature = true
[file]
diff --git a/apps/docs/content/docs/zh/ip-quality.mdx b/apps/docs/content/docs/zh/ip-quality.mdx
index e5d2a3466..0db341713 100644
--- a/apps/docs/content/docs/zh/ip-quality.mdx
+++ b/apps/docs/content/docs/zh/ip-quality.mdx
@@ -14,7 +14,7 @@ IP 质量检测让每台 Agent 评估自身 VPS 出口 IP,并将结果上报
## 前置条件
- Agent 必须启用 **`CAP_IP_QUALITY`**(位值 `1024`)。**默认开启**(属于 `CAP_DEFAULT`)。
-- 建议配置 GeoIP MMDB 数据库以获得完整的 IP 元数据。未配置时只有基本地理位置信息。详见[配置](/zh/docs/configuration)页中的 `geoip.mmdb_path`。
+- 本地基础元数据需要加载可用的 GeoIP MMDB。未加载数据库时,这些字段不可用;第三方富化仍可提供其支持的风险与网络类型字段。详见[配置](/zh/docs/configuration)页中的 `geoip.mmdb_path`。
## 能力(由 Agent 拥有)
@@ -79,10 +79,10 @@ Agent 在以下情况运行检测:
收到 Agent 上报的解锁结果后,Server 会:
-1. **通过本地 GeoIP MMDB 获取基础元数据** — 国家、地区、城市、ASN、AS 机构名、IP 类型(住宅 / 数据中心 / 托管 / 移动 / ISP / 未知)。无需外部请求,始终可用。
+1. **从已加载的本地 GeoIP MMDB 获取可用的基础元数据**,无需外部请求。可下载的 DB-IP Lite Country 数据库提供国家和地区。若 MMDB 缺失、无法读取或不包含该地址,本地基础字段将保持为空。
2. **可选地查询第三方风险提供商**(如已配置),获取 0–100 的欺诈风险分、风险等级(`low` / `medium` / `high`)及代理/VPN/托管标记。结果按 IP 缓存 24 小时,相同 IP 的多次检测不会重复调用 API。
-未配置风险提供商时,`risk_score` 和 `risk_level` 分别为 `null` / `unknown`,UI 仅显示 GeoIP 元数据。
+若没有提供商返回数值评分,`risk_score` 和 `risk_level` 分别为 `null` / `unknown`。只有加载了可用的 MMDB,才会显示本地元数据。
### 配置风险提供商
@@ -90,13 +90,13 @@ Agent 在以下情况运行检测:
| 提供商 | 值 | 说明 |
|--------|-----|------|
-| ipapi.is(默认)| `ipapi_is` | 每个源 IP 每天约 1000 次免费请求。可选付费 API Key 提升配额。|
-| ip-api.com(兜底)| `ip-api` | **免费版仅限非商业用途,接口为 HTTP(非 HTTPS)。** 无需 API Key。仅提供地理位置和代理标记,无风险评分。|
-| 禁用 | `none` | 仅 GeoIP 元数据,无风险评分。|
+| ipapi.is(默认)| `ipapi_is` | 匿名访问:每个客户端 IP 每 UTC 日 100 次,返回精简响应。免费账号/API Key:每日 1,000 次,返回完整响应。|
+| ip-api.com(兜底)| `ip-api` | **免费版仅限非商业用途,接口为 HTTP(非 HTTPS)。** 无需 API Key。提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。|
+| 禁用 | `none` | 仅在加载可用 MMDB 时提供本地 GeoIP 元数据;无风险评分。|
-**默认行为**(无需任何配置):Server 使用 `ipapi_is` 作为主提供商,`ip-api` 作为主提供商失败时的自动兜底。无需 API Key 即可开箱即用。
+**默认行为**(无需任何配置):Server 匿名调用 ipapi.is。匿名访问限制为每个客户端 IP 每 UTC 日 100 次,且只返回精简响应。请求失败时(包括配额耗尽后),Server 尝试 `ip-api`;该兜底可提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。详见 ipapi.is 官方的[开发者限额](https://ipapi.is/developers.html)和[定价页](https://ipapi.is/pricing.html)。
-若需使用 ipapi.is 的 API Key 以获取更高配额:
+创建免费 ipapi.is 账号并配置 API Key 后,可获得每日 1,000 次请求及完整响应:
```toml
[ip_quality]
@@ -156,7 +156,7 @@ risk_provider = "none"
## 数据保留
-状态变更历史事件(`unlock_event` 日志)默认保留 **90 天**。可通过 `retention.ip_quality_event_days`(环境变量 `SERVERBEE_RETENTION__IP_QUALITY_EVENT_DAYS`)调整。每日清理任务自动删除过期记录。
+状态变更历史事件(`unlock_event` 日志)默认保留 **90 天**。可通过 `retention.ip_quality_event_days`(环境变量 `SERVERBEE_RETENTION__IP_QUALITY_EVENT_DAYS`)调整。每小时清理任务自动删除过期记录。
每台服务器最新的解锁结果和 IP 质量快照会持续保留(每次运行后覆盖更新,不会累积)。
@@ -164,7 +164,7 @@ risk_provider = "none"
```text
Agent
- │ (CAP_IP_QUALITY 有效 + --allow-cap ip_quality)
+ │ (CAP_IP_QUALITY 有效)
├─ 按 interval_hours / IP 变化 / 手动触发 运行解锁检测
│ AgentMessage::UnlockResults (WebSocket)
▼
diff --git a/apps/docs/content/docs/zh/ping.mdx b/apps/docs/content/docs/zh/ping.mdx
index 5cf955259..f419c380d 100644
--- a/apps/docs/content/docs/zh/ping.mdx
+++ b/apps/docs/content/docs/zh/ping.mdx
@@ -12,8 +12,8 @@ ServerBee 支持三种探测类型。每种都会测量往返延迟并上报成
| 类型 | 探测内容 | 目标格式 | 超时 | 权限要求 |
|------|----------|----------|------|----------|
-| **ICMP** | 标准 ICMP Echo 请求 / 应答 | IP 或域名(如 `1.1.1.1`、`google.com`) | 5 秒 | Linux 下需 `CAP_NET_RAW` |
-| **TCP** | TCP 连接测试(握手) | `host:port`(如 `google.com:443`) | 5 秒 | 无 |
+| **ICMP** | 标准 ICMP Echo 请求 / 应答 | IP 或域名(如 `1.1.1.1`、`google.com`) | 10 秒 | Linux 下需 `CAP_NET_RAW` |
+| **TCP** | TCP 连接测试(握手) | `host:port`(如 `google.com:443`) | 10 秒 | 无 |
| **HTTP** | HTTP(S) GET 请求与响应时间 | 完整 URL(如 `https://example.com/health`) | 10 秒 | 无 |
## 创建 Ping 任务
@@ -111,7 +111,11 @@ Server 在以下时机会向 Agent 同步探测任务:
`server_ids_json` 字段控制哪些 Agent 执行探测:
- **指定节点**:填写服务器 ID 数组,如 `["srv-1", "srv-2"]`
-- **全部节点**:使用空数组 `[]` 或特殊值 `["*"]`,分配给所有在线 Agent
+- **全部节点**:使用空数组 `[]`
+
+
+`["*"]` 不是通配符。Server 会把它当作字面量服务器 ID,因此通常不会把任务分配给任何 Agent。需要所有服务器执行时请使用 `[]`;指定服务器时,请使用服务器列表中的真实 ID。
+
从多个 Agent 执行同一探测可以:
diff --git a/apps/docs/scripts/check-contracts.ts b/apps/docs/scripts/check-contracts.ts
index 4f94af550..3111380e3 100644
--- a/apps/docs/scripts/check-contracts.ts
+++ b/apps/docs/scripts/check-contracts.ts
@@ -136,6 +136,20 @@ const allDocumentation = (
locales.flatMap((locale) => [...pagesFor(locale)].map((page) => text(join(contentRoot, locale, `${page}.mdx`))))
)
).join('\n')
+
+const envReference = await text(join(repository, 'ENV.md'))
+const referencedEnvVars = new Set(
+ [...envReference.matchAll(/`(SERVERBEE_[A-Z0-9_]+)`/g)].map((match) => match[1])
+)
+for (const locale of locales) {
+ const configuration = await text(join(contentRoot, locale, 'configuration.mdx'))
+ const documentedEnvVars = new Set(
+ [...configuration.matchAll(/`(SERVERBEE_[A-Z0-9_]+)`/g)].map((match) => match[1])
+ )
+ const missingEnvVars = [...referencedEnvVars].filter((variable) => !documentedEnvVars.has(variable))
+ invariant(missingEnvVars.length === 0, `${locale}/configuration.mdx omits env vars: ${missingEnvVars.join(', ')}`)
+}
+
invariant(
!/ghcr\.io\/zingerlittlebee\/serverbee-(?:server|agent):latest/.test(allDocumentation),
'User documentation still deploys the potentially stale GHCR :latest tag'
From da0c54d6bb740a697168c3e0b44c56288496e637 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:23:44 +0800
Subject: [PATCH 05/11] docs: clarify sessions and aggregation lifecycle
---
apps/docs/content/docs/en/mobile.mdx | 2 +-
apps/docs/content/docs/en/monitoring.mdx | 4 ++--
apps/docs/content/docs/zh/mobile.mdx | 2 +-
apps/docs/content/docs/zh/monitoring.mdx | 4 ++--
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/apps/docs/content/docs/en/mobile.mdx b/apps/docs/content/docs/en/mobile.mdx
index 98c92eccf..306305b0a 100644
--- a/apps/docs/content/docs/en/mobile.mdx
+++ b/apps/docs/content/docs/en/mobile.mdx
@@ -114,7 +114,7 @@ To enable push notifications, configure APNs credentials in the web app:
- **Token Rotation**: Refresh tokens are rotated on every use (single-use). Old refresh tokens cannot be reused.
- **Fixed Expiry**: Access tokens have fixed 15-minute expiry (no sliding renewal) to limit exposure if stolen
- **Secure Storage**: Tokens are stored in iOS Keychain with appropriate accessibility levels
-- **Automatic Cleanup**: The server's background task checks for expired sessions hourly
+- **Session Expiry**: Expired access-token sessions are removed hourly. Paired-device refresh sessions are not deleted by that task: the device list hides them after expiry, refresh rotation replaces them, and logout or revoke deletes them.
## Troubleshooting
diff --git a/apps/docs/content/docs/en/monitoring.mdx b/apps/docs/content/docs/en/monitoring.mdx
index 5005dbf40..9798dc31d 100644
--- a/apps/docs/content/docs/en/monitoring.mdx
+++ b/apps/docs/content/docs/en/monitoring.mdx
@@ -192,7 +192,7 @@ Each server has a detail page showing:
The server detail page defaults to **Real-time mode**. In this mode, charts display live data streamed from WebSocket updates:
-- **Data source**: Accumulated from `BrowserMessage::Update` events via the `['servers']` TanStack Query cache
+- **Data source**: Accumulated from `BrowserMessage::Update` events through the live server catalog
- **Update interval**: ~3 seconds (matches the agent report interval)
- **Buffer size**: Grows to 250 data points, then trims back to the newest 200 points
- **Deduplication**: Uses the server-side `last_active` timestamp to filter duplicate events
@@ -237,7 +237,7 @@ ServerBee automatically cleans up expired data via a background task that runs o
| Audit logs | 180 days | `retention.audit_logs_days` |
-Aggregation and cleanup run in separate hourly background tasks. Cleanup starts with a 60-second offset to reduce overlap with aggregation, but the tasks do not form one fixed-order transaction. Keep enough raw-data retention for aggregation to run reliably, and monitor task errors if long-term history matters.
+Aggregation and cleanup run as separate hourly background tasks with no fixed ordering. Cleanup starts with a 60-second offset, which only staggers their work. Each aggregation run rolls up only the previous completed hour, so a missed hour is not backfilled; monitor task errors if long-term hourly history matters.
Example of overriding the retention policy:
diff --git a/apps/docs/content/docs/zh/mobile.mdx b/apps/docs/content/docs/zh/mobile.mdx
index b5d59be4c..f98948f47 100644
--- a/apps/docs/content/docs/zh/mobile.mdx
+++ b/apps/docs/content/docs/zh/mobile.mdx
@@ -114,7 +114,7 @@ refresh_ttl = 2592000 # 刷新令牌有效期(秒),默认 30 天
- **令牌轮换**:每次使用时轮换刷新令牌(一次性使用),旧刷新令牌不能重用
- **固定过期**:访问令牌固定 15 分钟过期(无滑动续期),以限制被盗后的暴露时间
- **安全存储**:令牌使用适当的可访问性级别存储在 iOS Keychain 中
-- **自动清理**:服务器后台任务每小时检查并清理过期会话
+- **会话过期**:过期的访问令牌会话每小时清理一次。该任务不会删除配对设备的刷新会话:设备列表会在刷新会话过期后将其隐藏;刷新令牌轮换会替换对应记录,登出或撤销会将其删除。
## 故障排除
diff --git a/apps/docs/content/docs/zh/monitoring.mdx b/apps/docs/content/docs/zh/monitoring.mdx
index d41fc4e18..09f8856dd 100644
--- a/apps/docs/content/docs/zh/monitoring.mdx
+++ b/apps/docs/content/docs/zh/monitoring.mdx
@@ -192,7 +192,7 @@ Agent 上报 (每 3 秒)
服务器详情页默认为**实时模式**,图表展示 WebSocket 推送的实时数据流:
-- **数据来源**:通过 `['servers']` TanStack Query 缓存订阅 `BrowserMessage::Update` 事件,自动累积数据点
+- **数据来源**:通过实时服务器目录订阅 `BrowserMessage::Update` 事件,自动累积数据点
- **更新频率**:约 3 秒一次(与 Agent 上报间隔一致)
- **缓冲区大小**:增长到 250 个数据点后,裁剪为最新的 200 个点
- **去重机制**:基于服务端 `last_active` 时间戳过滤重复事件
@@ -237,7 +237,7 @@ ServerBee 自动清理过期数据,由后台任务每小时执行一次:
| 审计日志 | 180 天 | `retention.audit_logs_days` |
-聚合和清理是两个独立的每小时后台任务。清理任务以 60 秒偏移启动,以减少与聚合任务重叠,但两者并非按固定顺序执行的同一事务。若长期历史数据很重要,请为聚合保留足够的原始数据,并监控后台任务错误。
+聚合和清理是两个独立的每小时后台任务,没有固定先后顺序。清理任务以 60 秒偏移启动,这只会错开两者的工作。每次聚合只汇总上一个完整小时,漏掉的小时不会补算;若长期小时历史数据很重要,请监控后台任务错误。
修改保留策略示例:
From 2e6bc1315cf371d2dde6251d157d7e745bada302 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:27:19 +0800
Subject: [PATCH 06/11] docs: address api authentication review findings
---
apps/docs/content/docs/en/api-reference.mdx | 10 +++++-----
apps/docs/content/docs/en/architecture.mdx | 2 +-
apps/docs/content/docs/zh/api-reference.mdx | 10 +++++-----
apps/docs/content/docs/zh/architecture.mdx | 2 +-
4 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/apps/docs/content/docs/en/api-reference.mdx b/apps/docs/content/docs/en/api-reference.mdx
index e02dfeaaf..5f4a9bc64 100644
--- a/apps/docs/content/docs/en/api-reference.mdx
+++ b/apps/docs/content/docs/en/api-reference.mdx
@@ -102,7 +102,7 @@ curl https://your-server/api/auth/me \
## Authenticated User Endpoints
-These endpoints require a user credential. Admin and Member users can use the listed read and account-scoped operations. API keys receive the same access as their owning users. Admin-only management endpoints are listed separately below.
+These endpoints require a user credential. Admin and Member users can use the listed read operations, plus operations that apply only to the signed-in account. API keys receive the same access as their owning users. Admin-only management endpoints are listed separately below.
| Family | Representative endpoints |
|--------|--------------------------|
@@ -115,7 +115,7 @@ These endpoints require a user credential. Admin and Member users can use the li
| Uptime and traffic | `GET /api/servers/{id}/uptime-daily`, `GET /api/servers/{id}/traffic` |
| GeoIP | `GET /api/geoip/status` |
| Ping tasks | `GET /api/ping-tasks`, `GET /api/ping-tasks/{id}/records` |
-| Network probes | `GET /api/network-probes/targets`, `GET /api/network-probes/setting`, `GET /api/network-probes/overview` |
+| Network probes | `GET /api/network-probes/targets`, `GET /api/network-probes/setting`, `GET /api/network-probes/overview`, `GET /api/servers/{id}/network-probes/targets`, `GET /api/servers/{id}/network-probes/records`, `GET /api/servers/{id}/network-probes/summary`, `GET /api/servers/{id}/network-probes/anomalies` |
| Traceroute results | `GET /api/servers/{id}/traceroute/{request_id}` |
| Docker, read-only | `GET /api/servers/{id}/docker/containers`, `stats`, `info`, `events`, `networks`, `volumes` |
| Service monitors | `GET /api/service-monitors`, `GET /api/service-monitors/{id}`, `GET /api/service-monitors/{id}/records` |
@@ -126,7 +126,7 @@ These endpoints require a user credential. Admin and Member users can use the li
## Admin Write and Management Endpoints
-The following management and host-control operations require Admin. Account-scoped operations listed above remain available to authenticated Member users.
+The following management and host-control operations require Admin. Operations listed above that apply only to the signed-in account remain available to authenticated Member users.
| Family | Representative endpoints |
|--------|--------------------------|
@@ -134,7 +134,7 @@ The following management and host-control operations require Admin. Account-scop
| Server management | `POST /api/servers` (idempotent onboarding), `PUT/DELETE /api/servers/{id}`, `POST /api/servers/{id}/upgrade` |
| Agent Authority | re-enrollment, offer issue/replace/revoke, and authority revocation under `/api/servers/{id}/agent-authority/*` |
| Groups and tags | `POST /api/server-groups`, `PUT/DELETE /api/server-groups/{id}`, `PUT /api/servers/{id}/tags` |
-| Ping and network probes | `POST /api/ping-tasks`, `PUT/DELETE /api/ping-tasks/{id}`, `POST /api/network-probes/targets`, `PUT/DELETE /api/network-probes/targets/{id}`, `PUT /api/network-probes/setting` |
+| Ping and network probes | `POST /api/ping-tasks`, `PUT/DELETE /api/ping-tasks/{id}`, `POST /api/network-probes/targets`, `PUT/DELETE /api/network-probes/targets/{id}`, `PUT /api/network-probes/setting`, `PUT /api/servers/{id}/network-probes/targets` |
| Traceroute | `POST /api/servers/{id}/traceroute` |
| Files | All `/api/files/*` endpoints, including list, stat, read, download, transfer management, and file changes |
| Docker actions | `POST /api/servers/{id}/docker/containers/{cid}/action` |
@@ -203,7 +203,7 @@ Only one offer may be outstanding per Server. Its terminal outcome is exactly on
| `/api/agent/ws` | Agent Bearer token (`?token=` accepted for legacy Agents) | Agent metrics, commands, pings, files, Docker, traceroute |
| `/api/ws/servers` | Session cookie, API key, or Bearer token | Browser/mobile real-time server updates |
| `/api/ws/terminal/{server_id}` | Authenticated Admin + `CAP_TERMINAL` | Web terminal proxy; JSON text messages with terminal data base64-encoded |
-| `/api/ws/docker/logs/{server_id}` | Authenticated + `CAP_DOCKER` | Per-container Docker log streaming |
+| `/api/ws/docker/logs/{server_id}` | Authenticated Admin + `CAP_DOCKER` | Per-container Docker log streaming |
## Common Status Codes
diff --git a/apps/docs/content/docs/en/architecture.mdx b/apps/docs/content/docs/en/architecture.mdx
index ba86f3c60..6f5b9b525 100644
--- a/apps/docs/content/docs/en/architecture.mdx
+++ b/apps/docs/content/docs/en/architecture.mdx
@@ -160,7 +160,7 @@ The agent connects to `ws:///api/agent/ws` and authenticates with `Autho
### Server to Browser (`BrowserMessage`)
-Browsers obtain data two ways: the REST API for initial load and historical queries, and a WebSocket at `ws:///api/ws/servers` for real-time pushes. The WebSocket is one-way (Server -> Browser). Both share the TanStack Query cache key `['servers']` for seamless updates.
+Browsers obtain data two ways: the REST API for initial load and historical queries, and a WebSocket at `ws:///api/ws/servers` for real-time pushes. The WebSocket is one-way (Server -> Browser). REST results and WebSocket messages are projected into the same live server catalog so views stay current.
| Message Type | Purpose |
|-------------|---------|
diff --git a/apps/docs/content/docs/zh/api-reference.mdx b/apps/docs/content/docs/zh/api-reference.mdx
index f5dded46b..959e05dee 100644
--- a/apps/docs/content/docs/zh/api-reference.mdx
+++ b/apps/docs/content/docs/zh/api-reference.mdx
@@ -102,7 +102,7 @@ curl https://your-server/api/auth/me \
## 已认证用户端点
-这些端点需要用户凭据。Admin 和 Member 都可以使用下列读取及账户范围操作;API Key 的权限与所属用户相同。仅限 Admin 的管理端点单独列在下一节。
+这些端点需要用户凭据。Admin 和 Member 都可以使用下列读取操作,以及只作用于当前账号的操作;API Key 的权限与所属用户相同。仅限 Admin 的管理端点单独列在下一节。
| 端点族 | 代表端点 |
|--------|----------|
@@ -115,7 +115,7 @@ curl https://your-server/api/auth/me \
| 可用性和流量 | `GET /api/servers/{id}/uptime-daily`、`GET /api/servers/{id}/traffic` |
| GeoIP | `GET /api/geoip/status` |
| Ping 任务 | `GET /api/ping-tasks`、`GET /api/ping-tasks/{id}/records` |
-| 网络探测 | `GET /api/network-probes/targets`、`GET /api/network-probes/setting`、`GET /api/network-probes/overview` |
+| 网络探测 | `GET /api/network-probes/targets`、`GET /api/network-probes/setting`、`GET /api/network-probes/overview`、`GET /api/servers/{id}/network-probes/targets`、`GET /api/servers/{id}/network-probes/records`、`GET /api/servers/{id}/network-probes/summary`、`GET /api/servers/{id}/network-probes/anomalies` |
| Traceroute 结果 | `GET /api/servers/{id}/traceroute/{request_id}` |
| Docker 读取 | `GET /api/servers/{id}/docker/containers`、`stats`、`info`、`events`、`networks`、`volumes` |
| 服务监控 | `GET /api/service-monitors`、`GET /api/service-monitors/{id}`、`GET /api/service-monitors/{id}/records` |
@@ -126,7 +126,7 @@ curl https://your-server/api/auth/me \
## 管理员写入和管理端点
-下列管理和主机控制操作需要 Admin 权限。上一节列出的账户范围操作仍可由已认证的 Member 使用。
+下列管理和主机控制操作需要 Admin 权限。上一节中只作用于当前账号的操作仍可由已认证的 Member 使用。
| 端点族 | 代表端点 |
|--------|----------|
@@ -134,7 +134,7 @@ curl https://your-server/api/auth/me \
| 服务器管理 | `POST /api/servers`(幂等 onboarding)、`PUT/DELETE /api/servers/{id}`、`POST /api/servers/{id}/upgrade` |
| Agent Authority | `/api/servers/{id}/agent-authority/*` 下的重新接入、offer 发出/替换/吊销及 authority 吊销 |
| 分组和标签 | `POST /api/server-groups`、`PUT/DELETE /api/server-groups/{id}`、`PUT /api/servers/{id}/tags` |
-| Ping 和网络探测 | `POST /api/ping-tasks`、`PUT/DELETE /api/ping-tasks/{id}`、`POST /api/network-probes/targets`、`PUT/DELETE /api/network-probes/targets/{id}`、`PUT /api/network-probes/setting` |
+| Ping 和网络探测 | `POST /api/ping-tasks`、`PUT/DELETE /api/ping-tasks/{id}`、`POST /api/network-probes/targets`、`PUT/DELETE /api/network-probes/targets/{id}`、`PUT /api/network-probes/setting`、`PUT /api/servers/{id}/network-probes/targets` |
| Traceroute | `POST /api/servers/{id}/traceroute` |
| 文件管理 | 所有 `/api/files/*` 端点,包括列目录、获取元数据、读取、下载、传输管理和文件修改 |
| Docker 操作 | `POST /api/servers/{id}/docker/containers/{cid}/action` |
@@ -203,7 +203,7 @@ curl https://your-server/api/auth/me \
| `/api/agent/ws` | Agent Bearer token(兼容旧 Agent 的 `?token=`) | Agent 指标、命令、Ping、文件、Docker、Traceroute |
| `/api/ws/servers` | Session cookie、API Key 或 Bearer token | 浏览器/移动端实时服务器更新 |
| `/api/ws/terminal/{server_id}` | 已认证 Admin + `CAP_TERMINAL` | Web 终端代理;JSON 文本消息,终端数据 base64 编码 |
-| `/api/ws/docker/logs/{server_id}` | 已认证 + `CAP_DOCKER` | 按容器流式传输 Docker 日志 |
+| `/api/ws/docker/logs/{server_id}` | 已认证 Admin + `CAP_DOCKER` | 按容器流式传输 Docker 日志 |
## 常见状态码
diff --git a/apps/docs/content/docs/zh/architecture.mdx b/apps/docs/content/docs/zh/architecture.mdx
index 9c5ce1a25..e7964c228 100644
--- a/apps/docs/content/docs/zh/architecture.mdx
+++ b/apps/docs/content/docs/zh/architecture.mdx
@@ -160,7 +160,7 @@ Agent 连接到 `ws:///api/agent/ws`,并使用 `Authorization: Bearer
### Server -> Browser(`BrowserMessage`)
-浏览器通过两种方式获取数据:REST API 用于初始加载和历史查询,WebSocket(地址 `ws:///api/ws/servers`)用于实时推送。该 WebSocket 为单向推送(Server -> 浏览器)。二者共享 TanStack Query 的 cache key `['servers']`,实现无缝更新。
+浏览器通过两种方式获取数据:REST API 用于初始加载和历史查询,WebSocket(地址 `ws:///api/ws/servers`)用于实时推送。该 WebSocket 为单向推送(Server -> 浏览器)。REST 结果和 WebSocket 消息会写入同一份实时服务器目录,使各页面保持同步。
| 消息类型 | 说明 |
|----------|------|
From 1d207dda8a9a4ff3dc7c8485c6219585d4b5ec5a Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:27:27 +0800
Subject: [PATCH 07/11] docs: address testing review feedback
---
apps/docs/content/docs/en/index.mdx | 2 +-
apps/docs/content/docs/zh/alerts.mdx | 2 +-
apps/docs/scripts/check-contracts.ts | 107 +++++++++++++++++++++++++--
3 files changed, 103 insertions(+), 8 deletions(-)
diff --git a/apps/docs/content/docs/en/index.mdx b/apps/docs/content/docs/en/index.mdx
index cb07e766d..a5c9682a6 100644
--- a/apps/docs/content/docs/en/index.mdx
+++ b/apps/docs/content/docs/en/index.mdx
@@ -51,7 +51,7 @@ Sign in via GitHub, Google, or any generic OIDC provider. Mobile sessions, devic
### Tested and CI-enforced
-The project ships with more than 3,800 automated tests, including integration suites that drive the real Axum router over HTTP and WebSocket through a mock-agent harness. Whenever the main CI workflow runs, it enforces frontend and installer checks; Rust-relevant pull requests and pushes that trigger the workflow also run zero-warning Clippy and the Rust test jobs. See [Testing & Quality](/en/docs/testing).
+The project ships with more than 3,800 automated tests, including integration suites that drive the real Axum router over HTTP and WebSocket through a mock-agent harness. Whenever the main CI workflow runs, it enforces frontend and installer checks. Zero-warning Clippy and the Rust test jobs also run for Rust-relevant pull requests and for every push that triggers the workflow. See [Testing & Quality](/en/docs/testing).
## Tech Stack
diff --git a/apps/docs/content/docs/zh/alerts.mdx b/apps/docs/content/docs/zh/alerts.mdx
index e4f651c0e..c97956329 100644
--- a/apps/docs/content/docs/zh/alerts.mdx
+++ b/apps/docs/content/docs/zh/alerts.mdx
@@ -197,7 +197,7 @@ Alerts 页面为 `capability_grant_detected`(「临时授予能力」)提供
## 阻断源 IP
-安全事件类规则(`ssh_brute_force_detected`、`port_scan_detected`)可以开启**阻断源 IP**。触发时,ServerBee 会指示受影响 Agent 的防火墙阻断攻击源 IP,把检测变为自动处置。这要求 Agent 上报 `firewall_block` 能力(`CAP_FIREWALL_BLOCK`)。该能力默认启用,但只能在 Agent 主机的 `[capabilities]` 配置中禁用,Server 端不能切换。详见 [安全事件检测](/zh/docs/security-events) 和 [防火墙管理](/zh/docs/firewall)。
+安全事件类规则(`ssh_brute_force_detected`、`port_scan_detected`)可以开启**阻断源 IP**。触发时,ServerBee 会指示受影响 Agent 的防火墙阻断攻击源 IP,把检测变为自动处置。这要求 Agent 上报 `firewall_block` 能力(`CAP_FIREWALL_BLOCK`)。该能力默认启用,可在 Agent 主机的 `[capabilities]` 配置中禁用,Server 端不能切换。详见 [安全事件检测](/zh/docs/security-events) 和 [防火墙管理](/zh/docs/firewall)。
## 通知渠道
diff --git a/apps/docs/scripts/check-contracts.ts b/apps/docs/scripts/check-contracts.ts
index 0a0e6c3f9..f17489d06 100644
--- a/apps/docs/scripts/check-contracts.ts
+++ b/apps/docs/scripts/check-contracts.ts
@@ -6,6 +6,8 @@ const docsApp = resolve(fileURLToPath(new URL('..', import.meta.url)))
const repository = resolve(docsApp, '../..')
const contentRoot = join(docsApp, 'content/docs')
const locales = ['en', 'zh'] as const
+const markdownTableDivider = /^\|(?:\s*:?-+:?\s*\|)+$/
+const numericTableCell = /^[\d,+~\s]+$/
function invariant(condition: unknown, message: string): asserts condition {
if (!condition) {
@@ -17,6 +19,34 @@ function text(path: string): Promise {
return readFile(path, 'utf8')
}
+function normalizedProse(markdown: string): string {
+ return markdown.replace(/[`*]/g, '').replace(/\s+/g, ' ').trim()
+}
+
+function firstMarkdownTable(markdown: string): string[][] {
+ const lines = markdown.split('\n')
+ for (let index = 0; index < lines.length - 1; index += 1) {
+ if (!(lines[index].startsWith('|') && markdownTableDivider.test(lines[index + 1]))) {
+ continue
+ }
+
+ const rows: string[][] = []
+ for (let row = index; row < lines.length && lines[row].startsWith('|'); row += 1) {
+ if (row === index + 1) {
+ continue
+ }
+ rows.push(
+ lines[row]
+ .slice(1, -1)
+ .split('|')
+ .map((cell) => cell.trim())
+ )
+ }
+ return rows
+ }
+ return []
+}
+
function headingSlugs(markdown: string): Set {
const counts = new Map()
const slugs = new Set()
@@ -182,26 +212,91 @@ for (const [locale, testing] of [
['en', enTesting],
['zh', zhTesting]
] as const) {
- invariant(!/\b891\b|2,223|89 test files|89 个测试文件/.test(testing), `${locale}/testing.mdx has stale test counts`)
+ const overview = firstMarkdownTable(testing)
+ invariant(overview.length > 1, `${locale}/testing.mdx does not have a test overview table`)
+ invariant(
+ overview.every((row) => row.length === 2),
+ `${locale}/testing.mdx test overview must not contain a per-area census column`
+ )
+ invariant(
+ !/^(?:tests?|test count|测试数|测试数量)$/i.test(overview[0][1] ?? ''),
+ `${locale}/testing.mdx test overview must describe coverage instead of snapshotting counts`
+ )
+ invariant(
+ overview.slice(1).every((row) => !numericTableCell.test(row[1] ?? '')),
+ `${locale}/testing.mdx test overview contains a per-area census`
+ )
+}
+
+const enTestingProse = normalizedProse(enTesting)
+for (const fact of [
+ /pull requests?.{0,100}rust(?:-relevant| related) changes?/i,
+ /(?:(?:skip|omit|do not run).{0,60}rust (?:jobs|gates|checks|tests)|rust (?:jobs|gates|checks|tests).{0,60}(?:skipped|omitted|not run))/i,
+ /push(?:es)?.{0,100}trigger.{0,100}(?:run|enable).{0,60}rust (?:jobs|gates|checks|tests)/i
+]) {
+ invariant(fact.test(enTestingProse), 'en/testing.mdx does not explain the path-aware Rust CI boundary')
}
+
+const zhTestingProse = normalizedProse(zhTesting)
+for (const fact of [
+ /(?:Pull Request.{0,100}Rust.{0,30}(?:相关)?变更|Rust.{0,30}(?:相关)?变更.{0,100}Pull Request)/i,
+ /(?:跳过|不(?:会)?运行).{0,40}Rust (?:任务|质量门槛|检查|测试)/i,
+ /(?:push.{0,100}触发|触发.{0,100}push).{0,100}(?:运行|执行|启用).{0,40}Rust (?:任务|质量门槛|检查|测试)/i
+]) {
+ invariant(fact.test(zhTestingProse), 'zh/testing.mdx does not explain the path-aware Rust CI boundary')
+}
+
+const ciWorkflow = await text(join(repository, '.github/workflows/ci.yml'))
invariant(
- enTesting.includes('Pull requests without Rust-relevant changes skip the Rust jobs.'),
- 'en/testing.mdx does not explain the path-aware Rust CI boundary'
+ /EVENT_NAME[\s\S]{0,100}!= "pull_request"[\s\S]{0,100}rust_changed=true/.test(ciWorkflow),
+ 'CI no longer enables Rust jobs for every triggering push; update the testing documentation'
)
invariant(
- zhTesting.includes('没有 Rust 相关变更的 Pull Request 会跳过 Rust 任务。'),
- 'zh/testing.mdx does not explain the path-aware Rust CI boundary'
+ /git diff --quiet[\s\S]{0,400}rust_changed=false/.test(ciWorkflow),
+ 'CI no longer skips Rust jobs for pull requests without Rust-relevant changes; update the testing documentation'
)
const enIndex = await text(join(contentRoot, 'en/index.mdx'))
const zhIndex = await text(join(contentRoot, 'zh/index.mdx'))
invariant(!enIndex.includes('every change passes'), 'en/index.mdx overstates CI coverage')
invariant(!zhIndex.includes('每次改动都要通过'), 'zh/index.mdx overstates CI coverage')
+const enIndexProse = normalizedProse(enIndex)
+for (const fact of [
+ /rust-relevant pull requests?/i,
+ /every push.{0,100}trigger/i,
+ /(?:run|enable).{0,60}rust (?:(?:test )?jobs|gates|checks|tests)|rust (?:(?:test )?jobs|gates|checks|tests).{0,60}(?:run|enabled)/i
+]) {
+ invariant(
+ fact.test(enIndexProse),
+ 'en/index.mdx does not distinguish path-gated pull requests from triggering pushes'
+ )
+}
+
+const zhIndexProse = normalizedProse(zhIndex)
+for (const fact of [
+ /Rust.{0,30}相关变更.{0,100}Pull Request/i,
+ /(?:触发.{0,100}push|push.{0,100}触发)/i,
+ /(?:运行|执行).{0,40}Rust (?:任务|质量门槛|检查|测试)/i
+]) {
+ invariant(
+ fact.test(zhIndexProse),
+ 'zh/index.mdx does not distinguish path-gated pull requests from triggering pushes'
+ )
+}
const zhAlerts = await text(join(contentRoot, 'zh/alerts.mdx'))
+const zhAlertsProse = normalizedProse(zhAlerts)
invariant(
- /Agent 上报 `firewall_block` 能力(`CAP_FIREWALL_BLOCK`).*Agent 主机.*Server 端不能切换/.test(zhAlerts),
+ /Agent.{0,100}(?:上报|拥有|具备).{0,80}(?:firewall_block|CAP_FIREWALL_BLOCK)/i.test(zhAlertsProse),
'zh/alerts.mdx does not identify the agent-owned firewall capability'
)
+invariant(
+ /(?:Server|服务端).{0,60}(?:不能|无法|不可).{0,40}(?:切换|修改|配置|授予)/i.test(zhAlertsProse),
+ 'zh/alerts.mdx implies that the server can change agent capabilities'
+)
+invariant(
+ !/(?:服务器|Server).{0,30}(?:具备|拥有).{0,40}CAP_FIREWALL_BLOCK/i.test(zhAlertsProse),
+ 'zh/alerts.mdx assigns CAP_FIREWALL_BLOCK ownership to the server'
+)
console.log('PASS: documentation contracts')
From f3528c5e2658929cdc7bb4cb79c1823e7aeaeb99 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:28:29 +0800
Subject: [PATCH 08/11] docs: correct agent report interval guidance
---
apps/docs/content/docs/en/monitoring.mdx | 2 +-
apps/docs/content/docs/zh/monitoring.mdx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/docs/content/docs/en/monitoring.mdx b/apps/docs/content/docs/en/monitoring.mdx
index 9798dc31d..2bf88a333 100644
--- a/apps/docs/content/docs/en/monitoring.mdx
+++ b/apps/docs/content/docs/en/monitoring.mdx
@@ -41,7 +41,7 @@ The dashboard therefore updates in real time -- no page refresh or polling inter
## Metric Types
-The agent collects the following metrics at a configurable interval (default: every 3 seconds, set via `collector.interval`; the server can dynamically adjust it through the `Welcome` message):
+The agent collects and reports the following metrics every 3 seconds. The server sends this fixed `report_interval` in the `Welcome` message:
### System Resources
diff --git a/apps/docs/content/docs/zh/monitoring.mdx b/apps/docs/content/docs/zh/monitoring.mdx
index 09f8856dd..33a96d21f 100644
--- a/apps/docs/content/docs/zh/monitoring.mdx
+++ b/apps/docs/content/docs/zh/monitoring.mdx
@@ -41,7 +41,7 @@ ServerBee 通过统一的 Web 面板实时监控所有已接入的服务器。
## 指标类型
-Agent 按可配置的间隔采集以下指标(默认每 3 秒,通过 `collector.interval` 设置;Server 可经 `Welcome` 消息动态调整):
+Agent 每 3 秒采集并上报以下指标。Server 在 `Welcome` 消息中下发这一固定的 `report_interval`:
### 系统资源
From 2f0298212cc315479ff60946960633149616f1b2 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:30:06 +0800
Subject: [PATCH 09/11] docs: restore api reference anchors
---
apps/docs/content/docs/en/api-reference.mdx | 2 +-
apps/docs/content/docs/zh/api-reference.mdx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/docs/content/docs/en/api-reference.mdx b/apps/docs/content/docs/en/api-reference.mdx
index 5f4a9bc64..94b5938a0 100644
--- a/apps/docs/content/docs/en/api-reference.mdx
+++ b/apps/docs/content/docs/en/api-reference.mdx
@@ -100,7 +100,7 @@ curl https://your-server/api/auth/me \
| GET | `/api/brand/logo` | Serve uploaded logo |
| GET | `/api/brand/favicon` | Serve uploaded favicon |
-## Authenticated User Endpoints
+## Authenticated Read Endpoints
These endpoints require a user credential. Admin and Member users can use the listed read operations, plus operations that apply only to the signed-in account. API keys receive the same access as their owning users. Admin-only management endpoints are listed separately below.
diff --git a/apps/docs/content/docs/zh/api-reference.mdx b/apps/docs/content/docs/zh/api-reference.mdx
index 959e05dee..f421963c3 100644
--- a/apps/docs/content/docs/zh/api-reference.mdx
+++ b/apps/docs/content/docs/zh/api-reference.mdx
@@ -100,7 +100,7 @@ curl https://your-server/api/auth/me \
| GET | `/api/brand/logo` | 返回上传的 Logo |
| GET | `/api/brand/favicon` | 返回上传的 Favicon |
-## 已认证用户端点
+## 已认证读取端点
这些端点需要用户凭据。Admin 和 Member 都可以使用下列读取操作,以及只作用于当前账号的操作;API Key 的权限与所属用户相同。仅限 Admin 的管理端点单独列在下一节。
From 91ad8f34f368f09121e97c8ee1c49ab0cb670149 Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:30:17 +0800
Subject: [PATCH 10/11] docs: clarify ip quality provider behavior
---
apps/docs/content/docs/en/configuration.mdx | 8 ++++----
apps/docs/content/docs/en/ip-quality.mdx | 10 +++++-----
apps/docs/content/docs/zh/configuration.mdx | 10 +++++-----
apps/docs/content/docs/zh/ip-quality.mdx | 10 +++++-----
4 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/apps/docs/content/docs/en/configuration.mdx b/apps/docs/content/docs/en/configuration.mdx
index 16e732a5f..178e35ad3 100644
--- a/apps/docs/content/docs/en/configuration.mdx
+++ b/apps/docs/content/docs/en/configuration.mdx
@@ -130,13 +130,13 @@ Tier-2 guardrail for the [Firewall Blocklist](/en/docs/firewall) feature. CIDRs
#### IP Quality
-By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonymously (100 requests per client IP per UTC day with a minimal response). A free account/API key provides 1,000 requests per day and the full response. On primary failure, the Server falls back to [ip-api.com](https://ip-api.com), which provides proxy/hosting flags and a derived IP type but no numeric risk score. See [IP Quality](/en/docs/ip-quality) for details and the fallback's non-commercial/HTTP warning.
+By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonymously (100 requests per client IP per UTC day). ServerBee uses the minimal response's proxy, VPN, datacenter, Tor, and abuse flags and derives an IP type, but no numeric risk score. A free account/API key provides 1,000 requests per day and the full response required for the 0–100 score. The Server falls back to [ip-api.com](https://ip-api.com) only when the primary request fails, including HTTP 429, not when a successful response lacks a score. See [IP Quality](/en/docs/ip-quality) for details and the fallback's non-commercial/HTTP warning.
| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER` | `"ipapi_is"` | Primary risk provider. One of: `none`, `ipapi_is`, `ip-api`. |
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER_FALLBACK` | `"ip-api"` | Fallback provider triggered on primary failure. Set to `none` to disable. |
-| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | Optional. A free account key provides 1,000 requests per day and the full response. |
+| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | Optional. A free account key provides 1,000 requests per day and the full response required for the 0–100 score. |
| `SERVERBEE_IP_QUALITY__IPAPI_IS__ENDPOINT` | `""` | Override for self-hosted mirrors or testing. Empty falls back to the built-in default `https://api.ipapi.is`. |
@@ -383,7 +383,7 @@ The log level can also be set via the `RUST_LOG` environment variable, which tak
### `[ip_quality]` -- IP Quality Risk Scoring
-By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonymously (100 requests per client IP per UTC day with a minimal response). A free account/API key provides 1,000 requests per day and the full response. On primary failure, the Server falls back to [ip-api.com](https://ip-api.com). Local baseline metadata is available only when a usable GeoIP MMDB is loaded. See [IP Quality](/en/docs/ip-quality) for details and the fallback's non-commercial/HTTP warning.
+By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonymously (100 requests per client IP per UTC day). ServerBee uses the minimal response's proxy, VPN, datacenter, Tor, and abuse flags and derives an IP type, but no numeric risk score. A free account/API key provides 1,000 requests per day and the full response required for the 0–100 score. The Server falls back to [ip-api.com](https://ip-api.com) only when the primary request fails, including HTTP 429, not when a successful response lacks a score. Local baseline metadata is available only when a usable GeoIP MMDB is loaded. See [IP Quality](/en/docs/ip-quality) for details and the fallback's non-commercial/HTTP warning.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
@@ -394,7 +394,7 @@ By default, the Server calls [ipapi.is](https://ipapi.is/developers.html) anonym
| Key | Type | Default | Description |
|-----|------|---------|-------------|
-| `api_key` | string | -- | Optional. A free account key provides 1,000 requests per day and the full response. |
+| `api_key` | string | -- | Optional. A free account key provides 1,000 requests per day and the full response required for the 0–100 score. |
| `endpoint` | string | `""` | Override for self-hosted mirrors or testing. Empty falls back to the built-in default `https://api.ipapi.is`. |
### `[network_probe]` -- Anomaly Thresholds
diff --git a/apps/docs/content/docs/en/ip-quality.mdx b/apps/docs/content/docs/en/ip-quality.mdx
index dee922cde..82d7e0547 100644
--- a/apps/docs/content/docs/en/ip-quality.mdx
+++ b/apps/docs/content/docs/en/ip-quality.mdx
@@ -7,7 +7,7 @@ icon: Globe
IP Quality lets each agent assess its VPS egress IP and report the results back to the server. It does two things:
1. **Service unlock detection** — the agent issues HTTP requests from its egress IP to determine the unlock status of popular streaming, AI, and social services.
-2. **IP metadata and risk scoring** — the server derives country, ASN, and IP type from its local GeoIP database and, when a third-party provider is configured, computes a fraud risk score.
+2. **IP metadata and risk signals** — the server derives country and region from its local GeoIP database, then uses the configured third-party provider for supported network-type flags and, when available, a fraud risk score.
Results appear on a dedicated **IP Quality** sidebar route (global overview), a per-server detail tab, and — when enabled per status page — on the public status page.
@@ -80,7 +80,7 @@ Check schedules are agent-side. The server only pushes the service catalog and f
After receiving unlock results from an agent, the server:
1. **Derives available baseline metadata** from a loaded local GeoIP MMDB, without an external request. The downloadable DB-IP Lite Country database provides country and region. If the MMDB is missing, unreadable, or does not cover the address, local baseline fields remain empty.
-2. **Optionally queries a third-party risk provider** (if configured) to obtain a 0–100 fraud risk score, risk level (`low` / `medium` / `high`), and proxy/VPN/hosting flags. Results are cached by IP for 24 hours, so repeated checks of the same IP do not generate repeated API calls.
+2. **Runs the configured third-party provider unless `risk_provider` is `none`.** Anonymous ipapi.is supplies proxy, VPN, datacenter, Tor, and abuse flags, which ServerBee also uses to derive IP type, but its minimal response does not contain the nested score fields. A numeric 0–100 score and risk level (`low` / `medium` / `high`) require an ipapi.is API key and its full response. Results are cached by IP for 24 hours, so repeated checks of the same IP do not generate repeated API calls.
If no provider returns a numeric score, `risk_score` and `risk_level` are `null` / `unknown`. Local metadata appears only when a usable MMDB is loaded.
@@ -90,13 +90,13 @@ Set `ip_quality.risk_provider` in `server.toml` (or `SERVERBEE_IP_QUALITY__RISK_
| Provider | Value | Notes |
|----------|-------|-------|
-| ipapi.is (default) | `ipapi_is` | Anonymous: 100 requests per client IP per UTC day with a minimal response. Free account/API key: 1,000 requests per day with the full response. |
+| ipapi.is (default) | `ipapi_is` | Anonymous: 100 requests per client IP per UTC day with flags but no numeric score. Free account/API key: 1,000 requests per day with the full response required for the 0–100 score. |
| ip-api.com (fallback) | `ip-api` | **Free tier, non-commercial use only. HTTP-only endpoint.** No API key. Proxy/hosting flags and a derived IP type, but no numeric risk score. |
| Disabled | `none` | Local GeoIP metadata only when a usable MMDB is loaded; no risk score. |
-**Default behavior** (no configuration needed): the Server calls ipapi.is anonymously. Anonymous access is limited to 100 requests per client IP per UTC day and returns a minimal response. If that request fails, including after quota exhaustion, the Server tries `ip-api`. The fallback can supply proxy/hosting flags and a derived IP type, but not a numeric risk score. See the official [ipapi.is developer limits](https://ipapi.is/developers.html) and [pricing](https://ipapi.is/pricing.html).
+**Default behavior** (no configuration needed): the Server calls ipapi.is anonymously. Anonymous access is limited to 100 requests per client IP per UTC day and returns flags that ServerBee uses for risk signals and a derived IP type, but no numeric risk score. Only a failed primary request, including an HTTP 429 after quota exhaustion, triggers the `ip-api` fallback; a successful response without a score does not. The fallback can supply proxy/hosting flags and a derived IP type, but not a numeric risk score. See the official [ipapi.is developer limits](https://ipapi.is/developers.html) and [pricing](https://ipapi.is/pricing.html).
-Create a free ipapi.is account and configure its API key for 1,000 requests per day and the full response:
+Create a free ipapi.is account and configure its API key for 1,000 requests per day and the full response required for the 0–100 score:
```toml
[ip_quality]
diff --git a/apps/docs/content/docs/zh/configuration.mdx b/apps/docs/content/docs/zh/configuration.mdx
index 012f3a9b9..4cdca9508 100644
--- a/apps/docs/content/docs/zh/configuration.mdx
+++ b/apps/docs/content/docs/zh/configuration.mdx
@@ -18,7 +18,7 @@ Server 按以下顺序合并配置,后者覆盖前者:
Agent 依次使用内置默认值、`/etc/serverbee/agent.toml`、工作目录下的 `agent.toml`,最后应用 `SERVERBEE_` 环境变量。托管服务将 `/opt/serverbee/etc` 设为工作目录,因此其第三个来源是 `/opt/serverbee/etc/agent.toml`。
-这样无需修改 TOML 文件即可在运行时覆盖会生效的配置项。仅为兼容保留的字段(例如 Agent 的 `collector.interval`)会在下文明确标注。
+这样无需修改 TOML 文件即可在运行时覆盖实际生效的配置项。仅为兼容保留的字段(例如 Agent 的 `collector.interval`)会在下文明确标注。
## 环境变量映射规则
@@ -130,13 +130,13 @@ Agent 依次使用内置默认值、`/etc/serverbee/agent.toml`、工作目录
#### IP 质量检测(IP Quality)
-Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html)(每个客户端 IP 每 UTC 日 100 次,返回精简响应)。免费账号/API Key 提供每日 1,000 次请求和完整响应。主 Provider 失败时,Server 回退到 [ip-api.com](https://ip-api.com),后者提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。功能详情及兜底服务的非商业用途/HTTP 警告见 [IP 质量检测](/zh/docs/ip-quality)。
+Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html),每个客户端 IP 每天 100 次(按 UTC 日计)。ServerBee 使用精简响应中的代理、VPN、数据中心、Tor 和滥用标记,并据此派生 IP 类型,但不提供数值风险分。免费账号/API Key 每日可请求 1,000 次,并返回计算 0–100 风险分所需的完整响应。只有主请求失败时(包括 HTTP 429)才会回退到 [ip-api.com](https://ip-api.com);成功响应即使没有评分也不会触发兜底。功能详情及兜底服务的非商业用途/HTTP 警告见 [IP 质量检测](/zh/docs/ip-quality)。
| 环境变量 | 默认值 | 说明 |
|----------|--------|------|
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER` | `"ipapi_is"` | 主风险评分 Provider。可选:`none`、`ipapi_is`、`ip-api`。 |
| `SERVERBEE_IP_QUALITY__RISK_PROVIDER_FALLBACK` | `"ip-api"` | 主 Provider 失败时的兜底。设为 `none` 关闭。 |
-| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | 可选。免费账号的 Key 提供每日 1,000 次请求和完整响应。 |
+| `SERVERBEE_IP_QUALITY__IPAPI_IS__API_KEY` | -- | 可选。免费账号的 Key 提供每日 1,000 次请求,以及计算 0–100 风险分所需的完整响应。 |
| `SERVERBEE_IP_QUALITY__IPAPI_IS__ENDPOINT` | `""` | 自建镜像或测试时覆盖。留空则回退到内置默认 `https://api.ipapi.is`。 |
@@ -383,7 +383,7 @@ Agent 顶层键使用单下划线,嵌套键使用 `__`(双下划线)。
### `[ip_quality]` —— IP 质量风险评分
-Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html)(每个客户端 IP 每 UTC 日 100 次,返回精简响应)。免费账号/API Key 提供每日 1,000 次请求和完整响应。主 Provider 失败时,Server 回退到 [ip-api.com](https://ip-api.com)。只有加载了可用的 GeoIP MMDB,才会提供本地基础元数据。功能详情及兜底服务的非商业用途/HTTP 警告见 [IP 质量检测](/zh/docs/ip-quality)。
+Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html),每个客户端 IP 每天 100 次(按 UTC 日计)。ServerBee 使用精简响应中的代理、VPN、数据中心、Tor 和滥用标记,并据此派生 IP 类型,但不提供数值风险分。免费账号/API Key 每日可请求 1,000 次,并返回计算 0–100 风险分所需的完整响应。只有主请求失败时(包括 HTTP 429)才会回退到 [ip-api.com](https://ip-api.com);成功响应即使没有评分也不会触发兜底。只有加载了可用的 GeoIP MMDB,才会提供本地基础元数据。功能详情及兜底服务的非商业用途/HTTP 警告见 [IP 质量检测](/zh/docs/ip-quality)。
| 键 | 类型 | 默认值 | 说明 |
|----|------|--------|------|
@@ -394,7 +394,7 @@ Server 默认匿名调用 [ipapi.is](https://ipapi.is/developers.html)(每个
| 键 | 类型 | 默认值 | 说明 |
|----|------|--------|------|
-| `api_key` | string | -- | 可选。免费账号的 Key 提供每日 1,000 次请求和完整响应。 |
+| `api_key` | string | -- | 可选。免费账号的 Key 提供每日 1,000 次请求,以及计算 0–100 风险分所需的完整响应。 |
| `endpoint` | string | `""` | 自建镜像或测试时覆盖。留空则回退到内置默认 `https://api.ipapi.is`。 |
### `[network_probe]` —— 异常阈值
diff --git a/apps/docs/content/docs/zh/ip-quality.mdx b/apps/docs/content/docs/zh/ip-quality.mdx
index 0db341713..599aee256 100644
--- a/apps/docs/content/docs/zh/ip-quality.mdx
+++ b/apps/docs/content/docs/zh/ip-quality.mdx
@@ -7,7 +7,7 @@ icon: Globe
IP 质量检测让每台 Agent 评估自身 VPS 出口 IP,并将结果上报到 Server。它做两件事:
1. **服务解锁检测** — Agent 从自身出口 IP 发送 HTTP 请求,判断流媒体、AI、社交等热门服务的解锁状态。
-2. **IP 元数据与风险评分** — Server 通过本地 GeoIP 数据库获取国家、ASN 和 IP 类型,并在配置了第三方提供商时计算欺诈风险分。
+2. **IP 元数据与风险信号** — Server 通过本地 GeoIP 数据库获取国家和地区,再使用已配置的第三方提供商补充其支持的网络类型标记,并在响应包含评分字段时计算欺诈风险分。
检测结果展示在专属的 **IP 质量** 侧边栏路由(全局概览)、各服务器详情 Tab,以及——在每个状态页单独开启后——公共状态页。
@@ -80,7 +80,7 @@ Agent 在以下情况运行检测:
收到 Agent 上报的解锁结果后,Server 会:
1. **从已加载的本地 GeoIP MMDB 获取可用的基础元数据**,无需外部请求。可下载的 DB-IP Lite Country 数据库提供国家和地区。若 MMDB 缺失、无法读取或不包含该地址,本地基础字段将保持为空。
-2. **可选地查询第三方风险提供商**(如已配置),获取 0–100 的欺诈风险分、风险等级(`low` / `medium` / `high`)及代理/VPN/托管标记。结果按 IP 缓存 24 小时,相同 IP 的多次检测不会重复调用 API。
+2. **除非将 `risk_provider` 设为 `none`,否则查询已配置的第三方提供商。** 匿名 ipapi.is 会返回代理、VPN、数据中心、Tor 和滥用标记,ServerBee 也据此派生 IP 类型,但精简响应不含嵌套的评分字段。数值型 0–100 风险分及风险等级(`low` / `medium` / `high`)需要配置 ipapi.is API Key 并使用其完整响应。结果按 IP 缓存 24 小时,相同 IP 的多次检测不会重复调用 API。
若没有提供商返回数值评分,`risk_score` 和 `risk_level` 分别为 `null` / `unknown`。只有加载了可用的 MMDB,才会显示本地元数据。
@@ -90,13 +90,13 @@ Agent 在以下情况运行检测:
| 提供商 | 值 | 说明 |
|--------|-----|------|
-| ipapi.is(默认)| `ipapi_is` | 匿名访问:每个客户端 IP 每 UTC 日 100 次,返回精简响应。免费账号/API Key:每日 1,000 次,返回完整响应。|
+| ipapi.is(默认)| `ipapi_is` | 匿名访问:每个客户端 IP 每天 100 次(按 UTC 日计),返回标记但不提供数值评分。免费账号/API Key:每日 1,000 次,返回计算 0–100 风险分所需的完整响应。|
| ip-api.com(兜底)| `ip-api` | **免费版仅限非商业用途,接口为 HTTP(非 HTTPS)。** 无需 API Key。提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。|
| 禁用 | `none` | 仅在加载可用 MMDB 时提供本地 GeoIP 元数据;无风险评分。|
-**默认行为**(无需任何配置):Server 匿名调用 ipapi.is。匿名访问限制为每个客户端 IP 每 UTC 日 100 次,且只返回精简响应。请求失败时(包括配额耗尽后),Server 尝试 `ip-api`;该兜底可提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。详见 ipapi.is 官方的[开发者限额](https://ipapi.is/developers.html)和[定价页](https://ipapi.is/pricing.html)。
+**默认行为**(无需任何配置):Server 匿名调用 ipapi.is。匿名访问限制为每个客户端 IP 每天 100 次(按 UTC 日计),返回的标记可用于风险信号和派生 IP 类型,但不提供数值风险分。只有主请求失败时(包括配额耗尽后的 HTTP 429)才会触发 `ip-api` 兜底;成功响应即使没有评分也不会触发兜底。该兜底可提供代理/托管标记及派生的 IP 类型,但不提供数值风险分。详见 ipapi.is 官方的[开发者限额](https://ipapi.is/developers.html)和[定价页](https://ipapi.is/pricing.html)。
-创建免费 ipapi.is 账号并配置 API Key 后,可获得每日 1,000 次请求及完整响应:
+创建免费 ipapi.is 账号并配置 API Key 后,可获得每日 1,000 次请求及计算 0–100 风险分所需的完整响应:
```toml
[ip_quality]
From 8eb19e61dfd2c668598480ab663e56ccc940c0ea Mon Sep 17 00:00:00 2001
From: ZingerLittleBee <6970999@gmail.com>
Date: Mon, 24 Aug 2026 00:32:06 +0800
Subject: [PATCH 11/11] style(docs): format environment contract checks
---
apps/docs/scripts/check-contracts.ts | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/apps/docs/scripts/check-contracts.ts b/apps/docs/scripts/check-contracts.ts
index 3111380e3..7f36c1889 100644
--- a/apps/docs/scripts/check-contracts.ts
+++ b/apps/docs/scripts/check-contracts.ts
@@ -138,14 +138,10 @@ const allDocumentation = (
).join('\n')
const envReference = await text(join(repository, 'ENV.md'))
-const referencedEnvVars = new Set(
- [...envReference.matchAll(/`(SERVERBEE_[A-Z0-9_]+)`/g)].map((match) => match[1])
-)
+const referencedEnvVars = new Set([...envReference.matchAll(/`(SERVERBEE_[A-Z0-9_]+)`/g)].map((match) => match[1]))
for (const locale of locales) {
const configuration = await text(join(contentRoot, locale, 'configuration.mdx'))
- const documentedEnvVars = new Set(
- [...configuration.matchAll(/`(SERVERBEE_[A-Z0-9_]+)`/g)].map((match) => match[1])
- )
+ const documentedEnvVars = new Set([...configuration.matchAll(/`(SERVERBEE_[A-Z0-9_]+)`/g)].map((match) => match[1]))
const missingEnvVars = [...referencedEnvVars].filter((variable) => !documentedEnvVars.has(variable))
invariant(missingEnvVars.length === 0, `${locale}/configuration.mdx omits env vars: ${missingEnvVars.join(', ')}`)
}