Files
HarborForge.Backend/docs/OPENCLAW_PLUGIN_DEV_PLAN.md
zhi 929a722c66 docs: add OpenClaw Plugin development plan
- docs/OPENCLAW_PLUGIN_DEV_PLAN.md: Complete development plan
  * Backend capability assessment
  * Security analysis (current HTTP heartbeat lacks validation)
  * Three implementation options (enhanced HTTP / API Key / encrypted payload)
  * Phased development plan (Phase 1-3)
  * API specifications
  * Data models
  * Sequence diagrams

- docs/examples/monitor_heartbeat_secure.py: Reference implementation
  for secure HTTP heartbeat with challenge validation
2026-03-19 14:19:46 +00:00

495 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenClaw Plugin 开发计划
**文档版本**: 0.1.0
**日期**: 2026-03-19
**状态**: 开发中
---
## 1. 概述
本文档定义 HarborForge.OpenclawPlugin 的开发计划,以及 Backend 需要提供的接口支持。
### 1.1 目标
开发一个 OpenClaw 插件,将服务器遥测数据(系统指标 + OpenClaw 状态)实时传输到 HarborForge Monitor。
### 1.2 架构关系
```
┌─────────────────────────────────────────────────────────────┐
│ 远程服务器 (VPS) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ OpenClaw Gateway │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ HarborForge.OpenclawPlugin │ │ │
│ │ │ - 生命周期管理 (随 Gateway 启动/停止) │ │ │
│ │ │ - 启动 sidecar 进程 │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ 启动/管理 │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ Sidecar (独立 Node 进程) │ │ │
│ │ │ - 收集系统指标 (CPU/内存/磁盘) │ │ │
│ │ │ - 读取 OpenClaw 状态 (agents) │ │ │
│ │ │ - HTTP/WebSocket 上报到 Monitor │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ HTTP / WebSocket
┌─────────────────────────┐
│ HarborForge.Backend │
│ - /monitor/* 接口 │
│ - 数据存储 │
└─────────────────────────┘
```
---
## 2. Backend 当前能力评估
### 2.1 已实现接口 ✅
| 接口 | 功能 | 完整度 | 说明 |
|------|------|--------|------|
| `GET /monitor/public/server-public-key` | 获取 RSA 公钥 | ✅ 100% | 用于插件加密 |
| `POST /admin/servers` | 注册服务器 | ✅ 100% | 返回 server_id |
| `POST /admin/servers/{id}/challenge` | 生成 challenge | ✅ 100% | 10分钟有效期 |
| `WS /monitor/server/ws` | WebSocket 连接 | ✅ 100% | 完整验证逻辑 |
| `POST /monitor/server/heartbeat` | HTTP 心跳 | ⚠️ 50% | 缺少安全验证 |
### 2.2 当前 HTTP Heartbeat 问题 🔴
```python
# 当前实现 (app/api/routers/monitor.py:191-207)
@router.post('/server/heartbeat')
def server_heartbeat(payload: ServerHeartbeat, db: Session = Depends(get_db)):
server = db.query(MonitoredServer).filter(
MonitoredServer.identifier == payload.identifier
).first()
# 问题:只验证 identifier 存在,不验证 challenge
# 任何人知道 identifier 就可以伪造数据
```
**对比 WebSocket 实现**
```python
# WebSocket 有完整验证
ch = db.query(ServerChallenge).filter(
ServerChallenge.challenge_uuid == challenge_uuid,
ServerChallenge.server_id == server.id
).first()
if not ch or ch.used_at is not None or ch.expires_at < now():
await websocket.close(code=4401) # 验证失败
```
---
## 3. Backend 需要补充的接口
### 3.1 方案 A增强 HTTP Heartbeat推荐短期方案
添加 challenge_uuid 验证:
```python
@router.post('/server/heartbeat')
def server_heartbeat(
payload: ServerHeartbeatSecure, # 包含 challenge_uuid
db: Session = Depends(get_db)
):
# 1. 验证服务器
server = db.query(MonitoredServer).filter(...).first()
if not server:
raise HTTPException(404, 'unknown server')
# 2. 验证 challenge
ch = db.query(ServerChallenge).filter(
ServerChallenge.challenge_uuid == payload.challenge_uuid,
ServerChallenge.server_id == server.id
).first()
if not ch or ch.expires_at < now():
raise HTTPException(401, 'invalid or expired challenge')
# 3. 存储数据...
```
**优点**: 与现有 WebSocket 验证逻辑一致
**缺点**: Challenge 10分钟过期需要定期重新注册
### 3.2 方案 BAPI Key 模式(推荐长期方案)
添加长期有效的 API Key
```python
# 1. 模型添加 api_key 字段
class MonitoredServer(Base):
...
api_key = Column(String(64), nullable=True, unique=True, index=True)
# 2. 新增接口:生成/重置 API Key
@router.post('/admin/servers/{id}/api-key')
def generate_api_key(server_id: int, ...):
api_key = secrets.token_urlsafe(32)
# 存储并返回 (仅显示一次)
# 3. 心跳接口验证 API Key
@router.post('/server/heartbeat-v2')
def server_heartbeat_v2(
payload: ServerHeartbeat,
x_api_key: str = Header(...),
db: Session = Depends(get_db)
):
server = db.query(MonitoredServer).filter(
MonitoredServer.identifier == payload.identifier,
MonitoredServer.api_key == x_api_key
).first()
if not server:
raise HTTPException(401, 'invalid credentials')
```
**优点**: 长期有效,适合自动化 Agent
**缺点**: 需要新增数据库字段和接口
### 3.3 方案 C加密 Payload最高安全
参考 WebSocket 的 encrypted_payload
```python
@router.post('/server/heartbeat')
def server_heartbeat(
encrypted_payload: str = Body(...), # RSA-OAEP 加密
db: Session = Depends(get_db)
):
# 1. 解密
data = decrypt_payload_b64(encrypted_payload)
# 2. 验证时间戳 (防重放)
if not ts_within(data['ts'], max_minutes=10):
raise HTTPException(401, 'expired')
# 3. 验证 challenge
ch = db.query(ServerChallenge).filter(
challenge_uuid=data['challenge_uuid']
).first()
...
```
**优点**: 最高安全性
**缺点**: 客户端实现复杂,需要 RSA 加密
---
## 4. OpenclawPlugin 开发计划
### Phase 1: 基础功能开发2-3天
**目标**: 可运行的基础版本(开发环境)
| 任务 | 说明 | 依赖 |
|------|------|------|
| 1.1 Sidecar 基础架构 | Node.js 项目结构,配置加载 | 无 |
| 1.2 系统指标收集 | CPU/内存/磁盘/运行时间 | 无 |
| 1.3 OpenClaw 状态读取 | 读取 agents.json版本信息 | 无 |
| 1.4 HTTP 心跳上报 | 使用当前 /heartbeat 接口 | ⚠️ 不安全,仅开发 |
| 1.5 Plugin 生命周期 | 随 Gateway 启动/停止 Sidecar | 无 |
**验收标准**:
- [ ] 可以收集系统指标
- [ ] 可以上报到 Backend
- [ ] 可以在 Monitor 面板看到数据
### Phase 2: 安全增强2-3天
**目标**: 生产环境可用的安全版本
| 任务 | 说明 | 依赖 |
|------|------|------|
| 2.1 WebSocket 支持 | 实现 WS 连接和加密握手 | Backend WS 接口 ✅ |
| 2.2 或:等待 HTTP 增强 | Backend 添加 challenge 验证 | Backend 更新 |
| 2.3 重试/退避逻辑 | 连接失败时指数退避 | 无 |
| 2.4 离线缓存 | 暂时存储,恢复后批量上报 | 无 |
**验收标准**:
- [ ] 连接需要验证WebSocket 或增强 HTTP
- [ ] 网络中断后自动恢复
- [ ] 数据不丢失
### Phase 3: 生产就绪1-2天
**目标**: 稳定可靠的监控系统
| 任务 | 说明 | 依赖 |
|------|------|------|
| 3.1 日志和诊断 | 结构化日志,调试接口 | 无 |
| 3.2 性能优化 | 减少资源占用 | 无 |
| 3.3 安装脚本完善 | 参考 PaddedCell 格式 | 无 |
| 3.4 文档编写 | 部署指南,故障排查 | 无 |
**验收标准**:
- [ ] 长时间稳定运行7天+
- [ ] 资源占用 < 1% CPU< 50MB 内存
- [ ] 安装脚本一键部署
---
## 5. 接口规格详细定义
### 5.1 当前可用接口
#### GET /monitor/public/server-public-key
```yaml
Response:
public_key_pem: string # RSA 公钥 (PEM 格式)
key_id: string # 公钥指纹
```
#### POST /admin/servers
```yaml
Headers:
Authorization: Bearer {admin_token}
Body:
identifier: string # 唯一标识 (如 "vps.t1")
display_name: string # 显示名称
Response:
id: int
identifier: string
challenge_uuid: string # 10分钟有效
expires_at: ISO8601
```
#### WS /monitor/server/ws
```yaml
连接流程:
1. Client -> Server: GET /monitor/server/ws (Upgrade)
2. Client -> Server: {
"encrypted_payload": "base64_rsa_encrypted_json"
}
# 或明文(向后兼容):
# {
# "identifier": "vps.t1",
# "challenge_uuid": "...",
# "nonce": "...",
# "ts": "2026-03-19T14:00:00Z"
# }
3. Server -> Client: { "ok": true, "server_id": 1 }
4. Client -> Server: {
"event": "server.metrics",
"payload": { "cpu_pct": 12.5, "mem_pct": 41.2, ... }
}
```
#### POST /monitor/server/heartbeat当前版本不安全
```yaml
Body:
identifier: string
openclaw_version: string
agents: [{id, name, status}]
cpu_pct: float
mem_pct: float
disk_pct: float
swap_pct: float
Response:
ok: true
server_id: int
last_seen_at: ISO8601
```
### 5.2 建议新增接口
#### POST /server/heartbeat-secure增强版
```yaml
Body:
identifier: string
challenge_uuid: string # 新增:必填
openclaw_version: string
agents: [...]
cpu_pct: float
mem_pct: float
disk_pct: float
swap_pct: float
timestamp: ISO8601 # 可选:防重放
Response:
ok: true
server_id: int
last_seen_at: ISO8601
challenge_expires_at: ISO8601
Error:
401: { detail: "invalid or expired challenge" }
```
---
## 6. 数据模型
### 6.1 当前 Backend 模型
```python
# app/models/monitor.py
class MonitoredServer:
id: int
identifier: str # 唯一标识
display_name: str
is_enabled: bool
created_by: int
created_at: datetime
# 建议添加:
# api_key: str # 长期有效的 API Key
class ServerChallenge:
id: int
server_id: int
challenge_uuid: str # 10分钟有效
expires_at: datetime
used_at: datetime # 首次使用时间
created_at: datetime
class ServerState:
id: int
server_id: int
openclaw_version: str
agents_json: str # JSON 序列化
cpu_pct: float
mem_pct: float
disk_pct: float
swap_pct: float
last_seen_at: datetime
updated_at: datetime
```
### 6.2 Plugin 配置模型
```typescript
// ~/.openclaw/openclaw.json
{
"plugins": {
"harborforge-monitor": {
"enabled": true,
"backendUrl": "https://monitor.hangman-lab.top",
"identifier": "vps.t1", // 服务器标识
"challengeUuid": "uuid-here", // 从 /admin/servers/{id}/challenge 获取
"apiKey": "key-here", // 如果使用 API Key 模式(可选)
"reportIntervalSec": 30,
"httpFallbackIntervalSec": 60,
"logLevel": "info"
}
}
}
```
---
## 7. 开发时序图
### 7.1 首次部署流程
```mermaid
sequenceDiagram
participant Admin
participant Backend
participant Plugin
participant Server as Server State
Admin->>Backend: POST /admin/servers<br/>{identifier: "vps.t1"}
Backend->>Admin: {id: 1, identifier: "vps.t1"}
Admin->>Backend: POST /admin/servers/1/challenge
Backend->>Admin: {challenge_uuid: "abc-123", expires_at: "..."}
Admin->>Server: 配置 challenge_uuid
Note over Server: ~/.openclaw/openclaw.json
Server->>Backend: openclaw gateway restart
Plugin->>Backend: GET /monitor/public/server-public-key
Backend->>Plugin: {public_key_pem: "..."}
alt WebSocket 模式
Plugin->>Backend: WS /monitor/server/ws
Plugin->>Backend: {challenge_uuid, nonce, ts}
Backend->>Plugin: {ok: true}
loop 每 30 秒
Plugin->>Backend: {event: "server.metrics", payload: {...}}
end
else HTTP 模式
loop 每 30 秒
Plugin->>Backend: POST /server/heartbeat<br/>{challenge_uuid, ...}
Backend->>Plugin: {ok: true}
end
end
```
### 7.2 数据上报格式
```json
{
"identifier": "vps.t1",
"challenge_uuid": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-03-19T14:30:00Z",
"cpu_pct": 12.5,
"mem_pct": 41.2,
"mem_used_mb": 4096,
"mem_total_mb": 8192,
"disk_pct": 62.0,
"disk_used_gb": 500.5,
"disk_total_gb": 1000.0,
"swap_pct": 0.0,
"uptime_sec": 86400,
"load_avg_1m": 0.5,
"platform": "linux",
"hostname": "vps.t1",
"openclaw_version": "1.2.3",
"openclaw_agent_count": 2,
"openclaw_agents": [
{"id": "dev", "name": "Developer", "status": "running"},
{"id": "ops", "name": "Operator", "status": "idle"}
]
}
```
---
## 8. 风险与缓解
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| HTTP Heartbeat 无验证 | 数据伪造 | 使用 WebSocket 或等待 Backend 修复 |
| Challenge 10分钟过期 | 需要频繁更新 | Backend 添加 API Key 模式 |
| 网络中断 | 数据丢失 | Plugin 实现离线缓存 |
| 资源占用过高 | 影响业务 | 控制采集频率,优化实现 |
| Sidecar 崩溃 | 监控中断 | Plugin 自动重启 Sidecar |
---
## 9. 下一步行动
### Backend 团队
- [ ] 决定采用方案 A/B/C 增强 HTTP Heartbeat 安全
- [ ] 实现 `/server/heartbeat-secure` 或增强现有接口
- [ ] (可选)添加 API Key 支持
### Plugin 开发团队
- [ ] Phase 1: 基础功能开发(使用当前不安全 HTTP仅开发测试
- [ ] Phase 2: 集成 WebSocket立即可用最安全
- [ ] 等待 Backend 更新后,切换到安全 HTTP
---
## 10. 参考文档
- 原始设计文档: `docs/monitor-server-connector-plan.md`
- Backend 代码: `app/api/routers/monitor.py`
- Backend 模型: `app/models/monitor.py`
- 加密服务: `app/services/crypto_box.py`
- PaddedCell 安装脚本参考: `https://git.hangman-lab.top/nav/PaddedCell`
---
**文档维护者**: HarborForge Team
**更新频率**: 随开发进度更新