1126 lines
44 KiB
Markdown
1126 lines
44 KiB
Markdown
# STORAGE LABOS / FlashOps — AI 协作与系统操作指南
|
||
|
||
> 文档版本:2.0
|
||
>
|
||
> 核对日期:2026-07-27
|
||
>
|
||
> 适用对象:Codex、Claude Code、Cursor、GitHub Copilot Agent,以及参与项目的人类开发者
|
||
>
|
||
> 目标:只把本文件交给 AI,也能让它安全地理解、运行、修改、验证和交付本系统
|
||
|
||
本文件是可以单独分发给 AI 的完整系统说明书,不依赖仓库中的任何其他文档。它已经内嵌产品边界、
|
||
架构、数据模型、Run/Step 状态机、恢复阶梯、API、Agent 协议、开发流程、生产运维、Git 和安全规则。
|
||
即使 AI 只能看到这一份文件,也应能够正确查询线上系统、运行本地模拟、判断允许执行的动作,并在
|
||
获得仓库或服务器权限后安全开发和运维。
|
||
|
||
如果 AI 同时能看到源码,以实际代码和测试结果为准;如果生产状态与本文不一致,先只读核验并报告
|
||
差异,不要凭本文直接覆盖服务器。
|
||
|
||
---
|
||
|
||
## 0. 直接交给 AI 的任务开场词
|
||
|
||
把下面内容和具体任务一起发给 AI:
|
||
|
||
~~~text
|
||
你正在维护 STORAGE LABOS / FlashOps。
|
||
|
||
你收到的这份文档已经包含操作本系统所需的产品、架构、状态机、API、Agent、生产运维、
|
||
安全和 Git 规则。请完整阅读并只以本文作为任务上下文,不要要求用户再提供其他项目文档。
|
||
|
||
如果任务同时提供仓库,先检查状态并保护现有未提交改动;如果只有本文,先使用公开 GET 接口进行
|
||
只读核验。默认只操作本地开发环境;除非任务明确要求,不得修改生产服务器、生产数据、DNS、证书、
|
||
Gitea 设置或调用生产环境写接口。任何真实硬件、固件刷写、Format、Sanitize、Reset、断电或 OOB
|
||
动作都必须停下并取得明确授权。
|
||
|
||
任务:<在这里写具体目标>
|
||
|
||
完成后请报告:改动文件、关键决策、测试结果、未完成项、风险、提交号和远程分支。
|
||
~~~
|
||
|
||
---
|
||
|
||
## 1. AI 必须先记住的系统事实
|
||
|
||
| 项目 | 当前值 |
|
||
|---|---|
|
||
| 产品 | STORAGE LABOS,后端代号 FlashOps |
|
||
| 用途 | 存储固件回归测试的无人值守控制平面 |
|
||
| Git 网页 | <https://git.imagebrewing.com/yuanshuai/storage-labos> |
|
||
| 克隆地址 | <https://git.imagebrewing.com/yuanshuai/storage-labos.git> |
|
||
| 默认分支 | main |
|
||
| 公网预览 | <https://flashops.imagebrewing.com/> |
|
||
| 公网 API 文档 | <https://flashops.imagebrewing.com/docs> |
|
||
| 本地控制台 | <http://localhost:8000/console/Dashboard.dc.html> |
|
||
| 本地 API 文档 | <http://localhost:8000/docs> |
|
||
| 后端 | Python 3.9+、FastAPI、SQLAlchemy |
|
||
| 当前数据库 | SQLite,必须保持单 worker |
|
||
| 仓库代码能力 | 模拟 Run 与 Agent dry-run;不代表真实硬件动作已交付 |
|
||
| 当前线上能力 | API 版本 0.1.0,仅模拟 Run;Agent 路由尚未发布 |
|
||
| 当前测试基线 | 25 项 |
|
||
| 生产认证边界 | 预览站匿名开放,尚无应用级 RBAC |
|
||
| Gitea | 公开仓库匿名可读;注册账号需管理员批准 |
|
||
| CI | 生产服务器没有 Runner,测试结果必须由提交者本地提供 |
|
||
|
||
重要:公网预览虽然没有登录限制,但这不等于 AI 获得了生产写权限。当前部分 POST 接口在技术上
|
||
也能匿名调用,这是尚未补齐 RBAC 的原型边界,不应被当作授权。
|
||
|
||
---
|
||
|
||
## 2. 权限与行为边界
|
||
|
||
AI 应按以下顺序判断是否可以行动:
|
||
|
||
| 行为 | 默认是否允许 | 条件 |
|
||
|---|---:|---|
|
||
| 阅读仓库、文档、Git 历史 | 是 | 保持只读 |
|
||
| 查看公网页面、调用健康检查和 GET 查询 | 是 | 不产生业务状态变化 |
|
||
| 在本地新分支修改代码和文档 | 用户要求开发时允许 | 保护已有改动 |
|
||
| 本地运行测试和模拟演示 | 是 | 不连接真实设备 |
|
||
| 创建本地模拟 Run | 是 | 仅使用本地数据库 |
|
||
| 提交 Git | 用户要求开发或交付时允许 | 检查范围、测试和秘密后提交 |
|
||
| 推送功能分支 | 用户要求共享或交付时允许 | 不强推 main |
|
||
| 合并 Pull Request | 否 | 需要仓库负责人明确授权或正常评审 |
|
||
| 调用生产 POST、PATCH、PUT、DELETE | 否 | 必须得到针对该动作的明确授权 |
|
||
| 修改 DNS、Nginx、systemd、证书、Gitea 设置 | 否 | 必须明确要求部署或运维 |
|
||
| 操作真实 DUT、NVMe、固件或 OOB | 否 | 必须明确目标、批准范围和回滚方案 |
|
||
| Format、Sanitize、刷写、Reset、断电 | 绝不默认执行 | 需要明确授权、可牺牲 DUT、审批和证据链 |
|
||
|
||
遇到权限不清时,继续完成安全的本地分析、测试或方案设计,把真正需要授权的动作单独列出。
|
||
|
||
---
|
||
|
||
## 3. 决策依据与证据优先级
|
||
|
||
AI 不应只根据文件名或旧说明猜测当前能力,按下面顺序取证:
|
||
|
||
1. 当前用户的明确任务、授权与限制;
|
||
2. 本文件写明的安全边界和不可执行动作;
|
||
3. 线上健康检查、API 返回、服务状态和日志等实际证据;
|
||
4. 如果 AI 获得仓库:当前工作树、Git diff、源码和自动化测试;
|
||
5. 如果 AI 获得服务器:生效配置、运行进程、数据路径和备份;
|
||
6. 历史提交或历史部署记录;
|
||
7. 推测。
|
||
|
||
如果本文、代码、测试和生产状态互相矛盾,必须在交付说明中指出。不得把“规划”“草案”“静态
|
||
页面”描述成已经可用于真实硬件的能力,也不得因为拥有技术访问能力就推断拥有写入授权。
|
||
|
||
---
|
||
|
||
## 4. 第一次接手:安全启动流程
|
||
|
||
### 4.1 全新克隆
|
||
|
||
~~~bash
|
||
git clone https://git.imagebrewing.com/yuanshuai/storage-labos.git
|
||
cd storage-labos
|
||
git status -sb
|
||
|
||
cd flashops
|
||
make setup
|
||
make test
|
||
make demo
|
||
~~~
|
||
|
||
make setup 会初始化本地虚拟环境,并重建 flashops/var 下的本地 SQLite 数据。它只适合全新克隆
|
||
或确认可以重置的本地开发环境;如果工作目录里已有需要保留的本地演示数据,不要直接运行。
|
||
|
||
### 4.2 已存在的工作目录
|
||
|
||
~~~bash
|
||
git status -sb
|
||
git diff
|
||
git diff --cached
|
||
git log -5 --oneline
|
||
~~~
|
||
|
||
先识别用户已有的未提交改动。不得使用 reset --hard、checkout 丢弃文件、clean -fd 或其他会
|
||
抹掉用户工作的命令。不得把不相关改动混进自己的提交。
|
||
|
||
### 4.3 只有本文时
|
||
|
||
只有本文、没有仓库时,AI 仍可以:
|
||
|
||
- 使用第 6 节列出的公开 GET 接口查询健康、指标、Run、事件和 Host;
|
||
- 根据第 5 节解释状态、恢复、检查点和系统边界;
|
||
- 根据第 11 节指导获授权的人完成服务器检查、发布、备份和故障排查;
|
||
- 根据第 2 节拒绝未授权生产写入与危险硬件动作;
|
||
- 输出诊断结论、变更方案、验证清单和交接记录。
|
||
|
||
只有本文时,AI 不得声称已经修改代码、提交 Git、连接服务器或执行生产动作。只有在真正获得相应
|
||
工具和授权后,才能执行这些操作。
|
||
|
||
---
|
||
|
||
## 5. 系统结构与当前边界
|
||
|
||
~~~text
|
||
STORAGE LABOS/
|
||
├── CONTRIBUTING.md 本文:完整 AI 系统说明书
|
||
├── .gitea/ PR 模板与未来 CI 工作流
|
||
├── flashops/
|
||
│ ├── services/control-plane/ FastAPI 控制平面
|
||
│ ├── services/host-agent/ 拉模式 Host Agent
|
||
│ ├── deploy/ FlashOps 与 Gitea 部署模板
|
||
│ ├── var/ 本地数据库和证据,禁止提交
|
||
│ └── Makefile 开发和验证入口
|
||
└── 前端UI八页面完成/ FastAPI 同源挂载的静态控制台
|
||
~~~
|
||
|
||
当前已经具备:
|
||
|
||
- FastAPI 控制平面、14 张持久化表和 SQLite 数据库;
|
||
- Run 事件溯源、状态投影、状态机和安全门禁;
|
||
- 模拟回归、故障注入、L1–L3 恢复演示和 Evidence Bundle;
|
||
- 仓库代码已具备 Agent 注册、token、心跳、步骤租约、ACK、续租、事件、完成、超时回收和幂等链路,
|
||
但该增量尚未发布到当前公网环境;
|
||
- 公开预览控制台及部分真实 API 接入页面。
|
||
|
||
当前不应声称已经具备:
|
||
|
||
- 真实 nvme-cli 固件刷写、Format 或 Sanitize;
|
||
- 真实 JetKVM、PDU 或 AC 断电控制;
|
||
- 完整 SSO、RBAC、审批与审计授权;
|
||
- 多工位生产调度、PostgreSQL 高可用;
|
||
- 已验收的失败聚类自动决策。
|
||
|
||
### 5.1 五层架构与依赖方向
|
||
|
||
~~~text
|
||
浏览器控制台 ──HTTP──▶ FastAPI 控制平面 ◀──HTTP 长轮询── Host Agent
|
||
│ │
|
||
▼ ├──▶ 工具适配器
|
||
SQLite / 证据 └──▶ 独立 OOB 控制器
|
||
~~~
|
||
|
||
五个逻辑层:
|
||
|
||
1. 产品交互层:控制台页面和 HTTP API;
|
||
2. 控制平面:工作流物化、状态机、安全门禁、资源锁、事件和恢复决策;
|
||
3. 数据面:客户测试主机上的拉模式 Agent,只执行控制平面批准的步骤;
|
||
4. 带外平面:独立于测试主机的 OOB 观测与电源控制,主机死亡后仍应可用;
|
||
5. 证据与分析层:事件时间线、Evidence Bundle、失败签名和审计。
|
||
|
||
依赖必须单向:
|
||
|
||
- Agent 不依赖控制平面的 Python 模块,只通过 HTTP 契约通信;
|
||
- 控制平面不主动反连 Agent,Agent 必须主动注册、心跳和领取步骤;
|
||
- 状态机与恢复决策保持纯函数,不做数据库、网络或时钟 I/O;
|
||
- API 路由只校验请求并调用 service,不承载核心业务判断;
|
||
- 所有危险动作在下发 Agent 前先经过安全门禁;
|
||
- 带外通道不能依赖被测主机的 OS、网络栈或 Agent 进程。
|
||
|
||
### 5.2 核心数据模型
|
||
|
||
当前 SQLite 中的核心对象如下,AI 讨论数据时应使用这些名称:
|
||
|
||
| 对象 | 作用 |
|
||
|---|---|
|
||
| TestHost | 测试主机、Agent 身份、心跳和工具链能力 |
|
||
| OobController | 独立带外控制器、电源和观测能力 |
|
||
| Dut | 被测盘及序列号、系统盘保护、状态 |
|
||
| FirmwareArtifact | 固件版本、型号适配和校验信息 |
|
||
| Workflow | 版本化 SOP 与步骤定义 |
|
||
| Run | 一次工作流执行及当前状态投影 |
|
||
| RunStep | Run 物化后的顺序步骤、attempt、租约和结果 |
|
||
| RunEvent | append-only 统一事件时间线 |
|
||
| RecoveryAction | 恢复级别、触发原因、结果和证据 |
|
||
| ResourceLock | DUT、Host 等独占资源锁 |
|
||
| FailureSignature | 归一化失败签名 |
|
||
| RunFailure | Run 与失败签名的关联 |
|
||
| EvidenceBundle | 证据包 URI、完整率、大小和哈希 |
|
||
| AuditLog | 人工或系统管理动作的审计记录 |
|
||
|
||
RunEvent 是状态变化的权威事实;Run 和 RunStep 上的 state 是事件施加后的当前投影。修改投影时必须
|
||
与事件处于同一数据库事务,不能先改状态后补事件。
|
||
|
||
### 5.3 Run 状态机
|
||
|
||
~~~text
|
||
QUEUED ──资源锁成功──▶ PREFLIGHT ──六项门禁通过──▶ RUNNING
|
||
│ │ │
|
||
└──紧急停止──▶ ABORTED ├──门禁拒绝──▶ REJECTED ├──完成──▶ COMPLETED
|
||
└──紧急停止──▶ ABORTED ├──人工暂停──▶ PAUSED
|
||
│ │
|
||
│ └──继续──▶ RUNNING
|
||
├──故障──▶ RECOVERING
|
||
│ ├──恢复成功──▶ RUNNING
|
||
│ └──耗尽/不安全──▶ FROZEN
|
||
├──冻结──▶ FROZEN
|
||
└──紧急停止──▶ ABORTED
|
||
~~~
|
||
|
||
状态含义:
|
||
|
||
| 状态 | 含义 |
|
||
|---|---|
|
||
| QUEUED | 等待 DUT、Host 等独占资源 |
|
||
| PREFLIGHT | 执行安全门禁并冻结环境指纹 |
|
||
| RUNNING | 正在执行步骤或循环 |
|
||
| RECOVERING | 正在执行恢复阶梯 |
|
||
| PAUSED | 在安全检查点人工暂停 |
|
||
| COMPLETED | 正常结束,verdict 为 PASS 或 FAIL |
|
||
| FROZEN | 冻结现场,禁止覆盖性动作 |
|
||
| ABORTED | 紧急停止 |
|
||
| REJECTED | 安全门禁拒绝启动 |
|
||
|
||
终态为 COMPLETED、FROZEN、ABORTED、REJECTED,终态不可继续转移。任何非法转移都应被视为代码
|
||
缺陷,不能吞掉异常继续运行。任意非终态都可以因紧急停止进入 ABORTED;终态不能再次停止、暂停或继续。
|
||
|
||
### 5.4 Step 状态机与租约
|
||
|
||
~~~text
|
||
PENDING ──租给 Agent──▶ DISPATCHED ──ACK──▶ RUNNING
|
||
▲ ├──▶ SUCCEEDED
|
||
│ ├──▶ FAILED
|
||
└──仅幂等且可重试时 attempt+1 ◀─────────────├──▶ TIMED_OUT
|
||
├──▶ CANCELLED
|
||
└──▶ SKIPPED
|
||
~~~
|
||
|
||
- PENDING:等待调度;
|
||
- DISPATCHED:已分配 Agent 并创建 60 秒租约,等待 ACK;
|
||
- RUNNING:Agent 已确认接手,执行期间必须续租;
|
||
- SUCCEEDED、FAILED、TIMED_OUT、CANCELLED、SKIPPED:步骤终态;
|
||
- 每次派发增加 attempt,旧 attempt 的 ACK、续租、事件和完成上报必须返回 409;
|
||
- DISPATCHED 未 ACK 而租约到期时退回 PENDING;
|
||
- RUNNING 租约到期时,只有幂等且仍有重试额度的步骤能退回 PENDING;
|
||
- 非幂等步骤或重试耗尽时进入 TIMED_OUT,Run verdict 变为 INCONCLUSIVE 并冻结;
|
||
- retry 默认是 0;刷写、Format、Sanitize 等危险步骤默认非幂等且不得自动重试。
|
||
|
||
### 5.5 五级恢复阶梯
|
||
|
||
恢复动作根据可用通道逐级升级,不允许跳过安全判断:
|
||
|
||
| 级别 | 动作 | 使用前提 | 典型触发 |
|
||
|---|---|---|---|
|
||
| L1_AGENT_SOFT | Agent 优雅停止或软重启进程 | Agent 仍在线 | 脚本卡死、进程僵死 |
|
||
| L2_OS_REBOOT | 通过 OS 通道重启主机 | 主机网络仍在线 | Agent 崩溃、驱动异常 |
|
||
| L3_OOB_RESET | OOB 触发主板 Reset | OOB 在线 | 心跳丢失、蓝屏 |
|
||
| L4_OOB_ATX_POWER | OOB 模拟 ATX 长按关机再开机 | OOB 在线 | Reset 无效 |
|
||
| L5_OOB_AC_CYCLE | AC 断电、安全等待、重新上电 | OOB 在线 | ATX 无效、DUT 掉盘最后手段 |
|
||
| FREEZE | 冻结现场并停止自动恢复 | 无 | 数据完整性失败或阶梯耗尽 |
|
||
|
||
不可更改的恢复规则:
|
||
|
||
1. 数据完整性不一致直接 FREEZE,不能先重启或重跑;
|
||
2. OOB 离线时 L3–L5 不可用,应标记 INFRA_FAILURE 并冻结,不能误判 DUT_DEFECT;
|
||
3. 每次恢复必须记录 trigger、level、outcome、时间和 evidence_uri;
|
||
4. 恢复成功只表示系统重新可执行,不表示原步骤成功;
|
||
5. 每循环最多恢复 3 次,每 Run 最多恢复 20 次;
|
||
6. L5 断电后默认安全等待 10 秒再上电;
|
||
7. 真实 L3–L5 当前没有交付,现有演示只证明状态和证据链路。
|
||
|
||
失败分类必须区分:
|
||
|
||
- DUT_DEFECT:证据支持被测盘缺陷;
|
||
- INFRA_FAILURE:供电、网络、Host、OOB 等基础设施问题;
|
||
- SCRIPT_FAILURE:测试脚本或工具问题;
|
||
- DATA_INTEGRITY:数据校验失败,立即冻结;
|
||
- UNKNOWN:证据不足,不能强行归类。
|
||
|
||
### 5.6 检查点与断点续跑
|
||
|
||
检查点只在安全的步骤边界创建,不在无法证明幂等的步骤中间创建。
|
||
|
||
| 中断位置 | 恢复后的行为 |
|
||
|---|---|
|
||
| 步骤已 SUCCEEDED 且检查点已保存 | 从下一步继续 |
|
||
| RUNNING 步骤幂等 | 可重跑当前步骤 |
|
||
| RUNNING 步骤非幂等 | 不自动重跑,INCONCLUSIVE 并等待人工确认 |
|
||
| 循环体中的步骤 | 从当前 loop_index 的循环起点重跑整轮 |
|
||
|
||
检查点至少要能确定 loop_index、下一步骤、当前固件/环境指纹和最后事件序号。保存检查点必须先于
|
||
推进下一步骤;恢复时必须验证数据库投影、Agent attempt 和 DUT 当前状态一致。
|
||
|
||
### 5.7 心跳与双通道观测
|
||
|
||
~~~text
|
||
Agent 正常心跳:每 5 秒
|
||
15 秒未收到:Host → DEGRADED,只记事件,不立刻做危险动作
|
||
30 秒未收到:Host → OFFLINE,并结合 OOB 判断
|
||
├── OOB 在线且主机有电:可进入 L3
|
||
├── OOB 在线但主机无电:INFRA_FAILURE
|
||
└── OOB 也离线:INFRA_FAILURE + FREEZE
|
||
心跳恢复:核对 Run、Step、attempt、租约和 DUT 枚举后再决定续跑
|
||
~~~
|
||
|
||
单一心跳、网络 ping 或 UI 状态都不能独立证明 DUT 故障。涉及恢复和缺陷归因时必须组合 Agent、
|
||
Host 网络、OOB 电源/画面和 DUT 枚举至少两个通道的证据。
|
||
|
||
### 5.8 六项安全门禁
|
||
|
||
创建 Run 前至少检查:
|
||
|
||
1. discovered_serial 与登记 DUT 序列号完全一致;
|
||
2. 目标不是系统盘,且没有系统分区或根文件系统;
|
||
3. 破坏性动作已有明确授权且授权尚未过期;
|
||
4. 固件型号、目标设备和校验值匹配;
|
||
5. Host 在线、未被其他 Run 占用且环境指纹满足要求;
|
||
6. 需要 OOB 的工作流必须确认 OOB 在线且能力匹配。
|
||
|
||
任一门禁失败都进入 REJECTED,不得通过 UI、API 参数或直接数据库写入绕过。真实危险命令还必须
|
||
增加签名命令模板、参数 schema、allowlist、审批人、审批时间、可牺牲 DUT 和短期授权。
|
||
|
||
### 5.9 Evidence Bundle
|
||
|
||
最小证据包包含 manifest.json 与按 seq 排序的 events.ndjson,并记录:
|
||
|
||
- Run、Workflow、DUT、Firmware、Host 和环境指纹;
|
||
- 完整事件时间线和人工介入;
|
||
- 步骤结果、退出码、日志 URI;
|
||
- 恢复动作与恢复前后证据;
|
||
- 检查点、verdict、完整率、大小和哈希。
|
||
|
||
大日志和二进制产物存对象目录,数据库只保存索引、摘要和 URI。事件按控制平面分配的 seq 排序,
|
||
不能依赖不同主机的本地时间戳。任何人工暂停、继续、急停、审批或覆盖默认策略的动作都必须留下事件。
|
||
|
||
---
|
||
|
||
## 6. AI 如何安全使用正在运行的系统
|
||
|
||
### 6.0 控制台页面
|
||
|
||
| 页面 | URL | 当前能力 |
|
||
|---|---|---|
|
||
| 总览 | /console/Dashboard.dc.html | 产品看板,部分指标仍为静态展示 |
|
||
| 任务中心 | /console/Tasks.dc.html | 已接安全预检和创建 Run |
|
||
| 实时运行 | /console/LiveRun.dc.html | 已接 Run、事件、演示、暂停、继续和急停 |
|
||
| 证据浏览器 | /console/Evidence.dc.html | 主要为静态样例,真实下载待接 |
|
||
| 失败中心 | /console/Failures.dc.html | 静态原型,失败聚类执行待实现 |
|
||
| 资产中心 | /console/Assets.dc.html | 静态原型,资产 CRUD 待实现 |
|
||
| 版本比较 | /console/Compare.dc.html | 静态原型,真实 A/B 查询待实现 |
|
||
| 工作流模板 | /console/Workflows.dc.html | 静态原型,编辑与发布 API 待实现 |
|
||
|
||
控制台和 API 同源,动态请求使用 /api/v1 相对路径。页面展示不是后端能力证据;AI 必须通过 API
|
||
返回或测试确认功能是否真实接入。事件轮询使用 after_seq 增量读取。
|
||
|
||
### 6.1 公网只读检查
|
||
|
||
以下请求只读取公开信息,可用于健康诊断:
|
||
|
||
~~~bash
|
||
curl -fsS https://flashops.imagebrewing.com/api/v1/health
|
||
curl -fsS https://flashops.imagebrewing.com/api/v1/dashboard/summary
|
||
curl -fsS 'https://flashops.imagebrewing.com/api/v1/runs?limit=10'
|
||
~~~
|
||
|
||
不要在诊断任务中为了“试一下”调用生产 POST 接口。健康检查成功不代表业务能力或真实硬件已经验收。
|
||
当前公网 OpenAPI 版本为 0.1.0,Agent 路径尚未发布,访问 /api/v1/agent/hosts 返回 404 是已知部署
|
||
边界,不应误判成 Gitea、Nginx 或 Host 故障。
|
||
|
||
Run 查询的关键字段:
|
||
|
||
| 字段 | 含义 |
|
||
|---|---|
|
||
| id、name | Run 标识和名称 |
|
||
| state、verdict | 当前状态与 PASS / FAIL / INCONCLUSIVE |
|
||
| execution_mode | simulated 或 agent_dry_run |
|
||
| workflow_key、workflow_version | 固定的工作流版本 |
|
||
| dut_id、test_host_id | 绑定资产 |
|
||
| loop_target、loop_done、progress | 循环目标与完成进度 |
|
||
| checkpoint | 最近安全恢复点 |
|
||
| environment | 创建时冻结的环境指纹 |
|
||
| recovery_count | 已执行恢复次数 |
|
||
| unattended_completion | 是否无人干预完成 |
|
||
| human_touches | 人工介入次数 |
|
||
| freeze_reason | 冻结原因 |
|
||
| evidence | 详情接口返回的证据包索引 |
|
||
|
||
事件字段为 id、seq、ts、source、kind、severity、step_id、loop_index、message、payload。必须按 seq
|
||
消费;after_seq 传客户端最后确认的 seq。source 可能是 control_plane、agent、oob 或 human。
|
||
|
||
### 6.2 本地启动
|
||
|
||
~~~bash
|
||
cd flashops
|
||
make dev
|
||
~~~
|
||
|
||
另开终端检查:
|
||
|
||
~~~bash
|
||
curl -fsS http://127.0.0.1:8000/api/v1/health
|
||
curl -fsS http://127.0.0.1:8000/api/v1/dashboard/summary
|
||
~~~
|
||
|
||
### 6.3 本地创建安全的模拟 Run
|
||
|
||
只在 localhost 使用下面示例:
|
||
|
||
~~~bash
|
||
curl -fsS -X POST http://127.0.0.1:8000/api/v1/runs \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"name": "AI local verification",
|
||
"loops": 2,
|
||
"inject_failure": true,
|
||
"execution_mode": "simulated",
|
||
"created_by": "ai-local"
|
||
}'
|
||
~~~
|
||
|
||
返回 Run ID 后:
|
||
|
||
~~~bash
|
||
curl -fsS http://127.0.0.1:8000/api/v1/runs/RUN_ID
|
||
curl -fsS 'http://127.0.0.1:8000/api/v1/runs/RUN_ID/events?after_seq=0'
|
||
~~~
|
||
|
||
也可以直接执行 make demo;它会创建独立的本地模拟链路并输出 Evidence Bundle 路径。
|
||
|
||
### 6.4 API 快速索引
|
||
|
||
下表中的非 Agent 路径已在当前公网部署;Agent 路径属于仓库代码基线,当前只适合本地开发和测试。
|
||
|
||
| 方法与路径 | 含义 | 默认安全级别 |
|
||
|---|---|---|
|
||
| GET /api/v1/health | 服务健康 | 只读 |
|
||
| GET /api/v1/dashboard/summary | 汇总指标 | 只读 |
|
||
| GET /api/v1/runs | Run 列表 | 只读 |
|
||
| GET /api/v1/runs/{run_id} | Run 详情与证据 | 只读 |
|
||
| GET /api/v1/runs/{run_id}/events | 增量事件 | 只读 |
|
||
| GET /api/v1/agent/hosts | Agent Host 状态 | 只读 |
|
||
| POST /api/v1/preflight | 安全预检 | 计算型;生产调用仍需明确目的 |
|
||
| POST /api/v1/runs | 创建 Run | 写操作;生产默认禁止 |
|
||
| POST /api/v1/runs/demo | 创建演示 Run | 写操作;生产默认禁止 |
|
||
| POST /api/v1/runs/{id}/pause | 暂停 | 写操作 |
|
||
| POST /api/v1/runs/{id}/resume | 继续 | 写操作 |
|
||
| POST /api/v1/runs/{id}/emergency-stop | 紧急停止 | 高影响写操作 |
|
||
| POST /api/v1/agent/* | Agent 注册与执行协议 | 凭据操作;不得随意调用 |
|
||
|
||
核心请求体已经内嵌如下;运行时 OpenAPI 只用于核验部署版本是否发生变化,不是阅读本文的前置条件。
|
||
|
||
Preflight 请求:
|
||
|
||
~~~json
|
||
{
|
||
"dut_id": "可选 DUT ID",
|
||
"host_id": "可选 Host ID",
|
||
"firmware_id": "可选固件 ID",
|
||
"discovered_serial": "探针实际发现的序列号"
|
||
}
|
||
~~~
|
||
|
||
Create Run 请求:
|
||
|
||
~~~json
|
||
{
|
||
"name": "固件 A/B 无人值守回归",
|
||
"workflow_id": null,
|
||
"dut_id": null,
|
||
"host_id": null,
|
||
"firmware_a_id": null,
|
||
"firmware_b_id": null,
|
||
"loops": 8,
|
||
"inject_failure": true,
|
||
"execution_mode": "simulated",
|
||
"created_by": "console-demo"
|
||
}
|
||
~~~
|
||
|
||
- loops 范围 1–500;
|
||
- execution_mode 只能是 simulated 或 agent_dry_run;
|
||
- simulated 会由控制平面启动内置模拟执行器;
|
||
- agent_dry_run 会物化步骤并等待独立 Agent 领取,但不允许真实主机命令。
|
||
|
||
暂停、继续和紧急停止使用:
|
||
|
||
~~~json
|
||
{
|
||
"actor": "操作者或 AI 任务标识",
|
||
"reason": "可审计的具体原因"
|
||
}
|
||
~~~
|
||
|
||
常见状态码:
|
||
|
||
| 状态码 | 含义 |
|
||
|---:|---|
|
||
| 200 | 查询或动作成功 |
|
||
| 201 | Run、注册等资源创建成功 |
|
||
| 204 | Agent 当前没有可领取步骤 |
|
||
| 400 | 业务参数或资产关系无效 |
|
||
| 401 | Agent 注册口令或 bearer token 无效 |
|
||
| 404 | Run、Host、DUT 等对象不存在 |
|
||
| 409 | 非法状态、资源冲突、旧 attempt 或幂等键冲突 |
|
||
| 422 | 请求字段或 Idempotency-Key 校验失败 |
|
||
| 503 | 生产环境缺少 Agent 注册口令 |
|
||
|
||
### 6.5 Agent 使用规则
|
||
|
||
- 本节描述仓库已经实现但尚未发布到公网的 Agent 协议;不要向当前生产域名调用这些端点;
|
||
- Agent 是拉模式,控制平面永远不主动连接测试主机;
|
||
- 生产注册需要 X-FlashOps-Enrollment-Token;
|
||
- 注册成功返回的原始 bearer token 只出现一次,只能保存到权限 0600 的本机状态文件;
|
||
- 后续 Agent 请求使用 Authorization: Bearer;
|
||
- ACK、续租、事件和完成接口要求 UUID 格式的 Idempotency-Key;
|
||
- 不得把 enrollment token、bearer token 或 agent-state.json 输出到聊天、日志或 Git;
|
||
- 未经授权不得代表真实 Host 注册、轮换 token 或领取生产步骤;
|
||
- 日常开发优先使用 make dev-agent、make agent-demo 和测试夹具。
|
||
|
||
Agent 端点和顺序:
|
||
|
||
| 顺序 | 方法与路径 | 行为 |
|
||
|---:|---|---|
|
||
| 1 | POST /api/v1/agent/register | 注册或轮换 token,上报 Host 指纹 |
|
||
| 2 | POST /api/v1/agent/{agent_id}/heartbeat | 上报健康、工具链和设备探针 |
|
||
| 3 | POST /api/v1/agent/{agent_id}/lease | 领取步骤,最长等待 20 秒 |
|
||
| 4 | POST /api/v1/agent/{agent_id}/steps/{step_id}/ack | 确认当前 attempt |
|
||
| 5 | POST /api/v1/agent/{agent_id}/steps/{step_id}/renew | 续租,不超过步骤 timeout |
|
||
| 6 | POST /api/v1/agent/{agent_id}/steps/{step_id}/events | 批量上报 1–100 条事件 |
|
||
| 7 | POST /api/v1/agent/{agent_id}/steps/{step_id}/complete | 上报 SUCCEEDED 或 FAILED |
|
||
|
||
注册体至少需要 agent_version,可选 host_id 或 host_name,并可携带 os_family、os_version、kernel、
|
||
cpu、motherboard、bios_version、ip_address、toolchain 和 labels。心跳体可携带 healthy、
|
||
agent_version、ip_address、toolchain 和 device_probe。
|
||
|
||
租约请求为:
|
||
|
||
~~~json
|
||
{"wait_timeout_s": 0}
|
||
~~~
|
||
|
||
wait_timeout_s 范围 0–20。ACK 与 renew 请求为 {"attempt": 1}。事件请求示例:
|
||
|
||
~~~json
|
||
{
|
||
"attempt": 1,
|
||
"events": [
|
||
{
|
||
"message": "step started",
|
||
"severity": "INFO",
|
||
"payload": {}
|
||
}
|
||
]
|
||
}
|
||
~~~
|
||
|
||
完成请求示例:
|
||
|
||
~~~json
|
||
{
|
||
"attempt": 1,
|
||
"status": "SUCCEEDED",
|
||
"exit_code": 0,
|
||
"error_class": null,
|
||
"result": {},
|
||
"log_uri": null
|
||
}
|
||
~~~
|
||
|
||
ACK、renew、events、complete 都必须带新的 UUID 格式 Idempotency-Key。相同操作重放返回当前投影
|
||
并标记 replayed=true;同一个 key 用于其他步骤或其他动作时返回 409。重新注册会轮换 token,使旧
|
||
token 立即失效。控制平面只保存 token 的 SHA-256 摘要。
|
||
|
||
---
|
||
|
||
## 7. 三条不可破坏的架构红线
|
||
|
||
### 7.1 控制面是状态唯一真相
|
||
|
||
Agent 和测试主机只持有执行缓存。Run、Step、锁、检查点和证据索引的权威状态在控制平面。
|
||
不得把 Agent 本地状态反向当成数据库真相。
|
||
|
||
### 7.2 状态变化必须追加事件
|
||
|
||
所有 Run 状态变化必须经过 events/recorder.py 的 append_event。不得绕过事件记录直接 UPDATE
|
||
Run 状态。新增状态或事件时必须同步修改枚举、转移表、投影逻辑、测试和文档。
|
||
|
||
### 7.3 安全默认拒绝
|
||
|
||
序列号、破坏性授权、系统盘、固件型号、Host 和 OOB 任一条件不满足都必须拒绝下发。危险步骤
|
||
默认不可自动重试。数据完整性失败要冻结现场,不能为了完成率自动重跑并覆盖证据。
|
||
|
||
---
|
||
|
||
## 8. AI 修改代码的标准流程
|
||
|
||
### 8.1 开始前
|
||
|
||
1. 确认用户目标属于解释、诊断、开发还是部署;
|
||
2. 检查 Git 状态、当前分支和已有改动;
|
||
3. 阅读与任务直接相关的源码、测试和文档;
|
||
4. 找到当前行为的证据,不能只根据 UI 文案猜测;
|
||
5. 若涉及高影响选择,先说明方案和边界。
|
||
|
||
### 8.2 实现时
|
||
|
||
- 修改现有项目,不随意建立第二套平行架构;
|
||
- 优先小而完整的改动,不混入无关格式化;
|
||
- 修复缺陷时增加能复现问题的测试;
|
||
- API、状态、事件、环境变量或部署变化必须同步文档;
|
||
- 保持 Python 3.9 兼容,不在未修改运行基线前使用更高版本独占语法;
|
||
- SQLite 环境保持单 worker;
|
||
- 不绕过 service、event recorder、state machine 或 safety gate;
|
||
- 保留用户未提交的改动,不擅自覆盖或清理。
|
||
|
||
### 8.3 完成前
|
||
|
||
~~~bash
|
||
cd flashops
|
||
make test
|
||
make demo
|
||
~~~
|
||
|
||
回到仓库根目录:
|
||
|
||
~~~bash
|
||
git status -sb
|
||
git diff --check
|
||
git diff --stat
|
||
~~~
|
||
|
||
只暂存本次任务相关文件:
|
||
|
||
~~~bash
|
||
git add <明确的文件或目录>
|
||
git diff --cached --stat
|
||
git diff --cached --check
|
||
~~~
|
||
|
||
AI 还必须确认暂存区没有:
|
||
|
||
- .env、密码、Token、私钥或证书私钥;
|
||
- SQLite、日志、Evidence Bundle、Agent state;
|
||
- 虚拟环境、缓存、构建输出;
|
||
- 未脱敏的客户数据或真实设备标识;
|
||
- 无关的用户改动。
|
||
|
||
---
|
||
|
||
## 9. 测试与验收门禁
|
||
|
||
| 变更类型 | 最低验证 |
|
||
|---|---|
|
||
| 纯文档 | 链接和命令核对、git diff --check |
|
||
| Python 逻辑 | make test |
|
||
| 状态机、恢复、安全 | 对应单测 + make test + make demo |
|
||
| API | 正常、错误、幂等或冲突路径测试 |
|
||
| Agent | 控制面与 host-agent 测试,凭据权限检查 |
|
||
| 前端 | 真实浏览器检查受影响页面,确认控制台与 API 一致 |
|
||
| Nginx | nginx -t 后才能 reload |
|
||
| systemd | systemd-analyze verify 或目标机安全验证 |
|
||
| 生产部署 | 本地测试、备份、健康检查、HTTPS、日志和回滚检查 |
|
||
|
||
测试失败时不得把失败包装成完成。先判断是本次回归、环境差异还是已有问题,并保留原始证据。
|
||
|
||
---
|
||
|
||
## 10. Git 与 Gitea 交付规范
|
||
|
||
### 10.1 分支
|
||
|
||
- main:稳定、可测试、可发布,禁止强推;
|
||
- feat/<short-name>:功能;
|
||
- fix/<short-name>:缺陷;
|
||
- docs/<short-name>:文档;
|
||
- chore/<short-name>:工具、依赖和部署。
|
||
|
||
~~~bash
|
||
git switch main
|
||
git pull --ff-only
|
||
git switch -c docs/ai-operating-guide
|
||
~~~
|
||
|
||
### 10.2 提交
|
||
|
||
使用 Conventional Commits:
|
||
|
||
| 类型 | 示例 |
|
||
|---|---|
|
||
| feat | feat(agent): add signed command registry |
|
||
| fix | fix(events): preserve sequence under concurrent writes |
|
||
| docs | docs(ai): add system operating guide |
|
||
| test | test(safety): reject system disk target |
|
||
| refactor | refactor(runs): isolate checkpoint projection |
|
||
| perf | perf(api): reduce event timeline query cost |
|
||
| chore | chore(deploy): pin gitea image |
|
||
|
||
一次提交只表达一个完整意图。提交前检查暂存区,不能用一条模糊提交吞掉多项无关工作。
|
||
|
||
### 10.3 推送与 Pull Request
|
||
|
||
~~~bash
|
||
git commit -m 'docs(ai): add system operating guide'
|
||
git push -u origin docs/ai-operating-guide
|
||
~~~
|
||
|
||
在 Gitea 创建 Pull Request,说明:
|
||
|
||
- 改了什么、为什么;
|
||
- 影响的系统边界;
|
||
- 实际运行的测试及结果;
|
||
- 安全影响;
|
||
- 风险、回滚方法和未完成项。
|
||
|
||
至少一名团队成员评审后合并。优先 Squash merge,PR 标题使用 Conventional Commit。
|
||
生产服务器没有 Gitea Actions Runner;仓库中的工作流是未来隔离 Runner 的模板,不能把“工作流存在”
|
||
误报成“CI 已运行”。
|
||
|
||
### 10.4 凭据
|
||
|
||
公开仓库允许匿名读取和克隆。写操作需要经过批准的 Gitea 账号。管理员密码和访问令牌只允许存在于
|
||
系统钥匙串、密码管理器或被 .gitignore 覆盖的本机文件中;不得写进 remote URL、文档、脚本或提交。
|
||
|
||
---
|
||
|
||
## 11. 生产部署上下文
|
||
|
||
| 资源 | 当前值 |
|
||
|---|---|
|
||
| 云服务器 | 腾讯云 CVM,62.234.39.66 |
|
||
| 操作系统 | Ubuntu 24.04 |
|
||
| DNS | DNSPod A 记录,TTL 600 |
|
||
| FlashOps 域名 | flashops.imagebrewing.com |
|
||
| FlashOps 服务 | flashops.service |
|
||
| 当前公网 API | 0.1.0,仅模拟 Run,未发布 Agent 路由 |
|
||
| 应用用户 | ubuntu |
|
||
| 应用目录 | /data/wangzhan/app/storage-labos |
|
||
| 内部端口 | 127.0.0.1:18080 |
|
||
| 应用数据 | /data/wangzhan/app/storage-labos/flashops/var |
|
||
| 私有环境文件 | /etc/flashops/flashops.env |
|
||
| FlashOps Nginx | /www/server/panel/vhost/nginx/flashops.imagebrewing.com.conf |
|
||
| FlashOps 证书 | /etc/letsencrypt/live/flashops.imagebrewing.com/ |
|
||
| Gitea 域名 | git.imagebrewing.com |
|
||
| Gitea 容器 | gitea,镜像版本固定为 1.27.0 |
|
||
| Gitea 内部端口 | 127.0.0.1:13000 |
|
||
| Gitea 实时数据 | /data/gitea/data |
|
||
| Gitea Nginx | /www/server/panel/vhost/nginx/git.imagebrewing.com.conf |
|
||
| Gitea 证书 | /etc/letsencrypt/live/git.imagebrewing.com/ |
|
||
| COS 挂载 | /chucun |
|
||
| Gitea 备份 | /chucun/wangzhan-production/backups/gitea/ |
|
||
|
||
生产操作规则:
|
||
|
||
1. 只有用户明确要求部署或运维时才连接服务器;
|
||
2. 先核对主机、域名、当前服务、端口和目标文件;
|
||
3. 部署前运行本地测试并备份当前版本和数据;
|
||
4. 不直接在服务器编辑项目源码,应由 Git 提交或可核验发布包交付;
|
||
5. Nginx 修改必须先测试配置再 reload;
|
||
6. SQLite 继续使用单 worker,扩展并发前先迁移 PostgreSQL;
|
||
7. Gitea 实时仓库和 SQLite 不得放到 COSFS;
|
||
8. COS 只作备份目标,备份需要 ZIP 与 SHA-256 sidecar;
|
||
9. 不在输出中展示 SSH、Gitea、Agent、DNS 或云平台凭据;
|
||
10. 部署后验证内部 health、外部 HTTPS、最终跳转、日志和回滚点。
|
||
|
||
FlashOps 当前是匿名预览环境。接入真实硬件或非公开数据前,必须先完成应用级 SSO / RBAC、
|
||
审批、审计和命令模板签名;不能只依赖 Nginx Basic Auth。
|
||
|
||
### 11.1 FlashOps 服务管理与健康检查
|
||
|
||
只有获得服务器运维授权后才执行:
|
||
|
||
~~~bash
|
||
sudo systemctl status flashops --no-pager -l
|
||
sudo systemctl is-enabled flashops
|
||
curl -fsS http://127.0.0.1:18080/api/v1/health
|
||
curl -fsS https://flashops.imagebrewing.com/api/v1/health
|
||
~~~
|
||
|
||
需要重启时先确认没有不可中断的 Run,再执行:
|
||
|
||
~~~bash
|
||
sudo systemctl restart flashops
|
||
sudo systemctl status flashops --no-pager -l
|
||
curl -fsS http://127.0.0.1:18080/api/v1/health
|
||
~~~
|
||
|
||
服务必须只绑定 127.0.0.1:18080,不能把 Uvicorn 直接暴露公网。当前 SQLite 只允许运行一个
|
||
worker;增加 worker 前必须先迁移 PostgreSQL,并处理跨进程事件总线与锁。
|
||
|
||
关键环境变量:
|
||
|
||
| 变量 | 作用 |
|
||
|---|---|
|
||
| FLASHOPS_ENV | production 或 development |
|
||
| FLASHOPS_DATABASE_URL | 数据库 DSN |
|
||
| FLASHOPS_OBJECT_STORE_URL | 证据对象目录或对象存储 |
|
||
| FLASHOPS_BUS_URL | 外部事件总线,当前未启用 |
|
||
| FLASHOPS_AGENT_ENROLLMENT_TOKEN | Agent 首次注册口令 |
|
||
| FLASHOPS_API_HOST | API 监听地址 |
|
||
| FLASHOPS_API_PORT | API 端口 |
|
||
| FLASHOPS_CORS_ORIGINS | 允许的前端来源 |
|
||
| FLASHOPS_ENGINE_TICK_S | 引擎 tick |
|
||
| FLASHOPS_TIME_SCALE | 模拟时间倍率 |
|
||
|
||
生产秘密只放在 root-only、权限 0600 的 /etc/flashops/flashops.env。不得把值写进 systemd unit、
|
||
Nginx 注释、命令历史、聊天或 Git。
|
||
|
||
### 11.2 日志
|
||
|
||
~~~bash
|
||
sudo journalctl -u flashops -n 200 --no-pager
|
||
sudo journalctl -u flashops --since '30 minutes ago' --no-pager
|
||
sudo tail -n 100 /www/wwwlogs/flashops.imagebrewing.com.log
|
||
sudo tail -n 100 /www/wwwlogs/flashops.imagebrewing.com.error.log
|
||
~~~
|
||
|
||
持续跟踪日志只能用于短时诊断,结束后退出,避免长期占用会话。输出日志前先检查是否包含 token、
|
||
客户数据、设备序列号或其他敏感信息。
|
||
|
||
### 11.3 Nginx、DNS 与证书
|
||
|
||
任何配置修改都必须先测试,成功后才能 reload:
|
||
|
||
~~~bash
|
||
sudo nginx -t
|
||
sudo systemctl reload nginx
|
||
sudo certbot certificates
|
||
getent ahostsv4 flashops.imagebrewing.com
|
||
getent ahostsv4 git.imagebrewing.com
|
||
~~~
|
||
|
||
证书模拟续期:
|
||
|
||
~~~bash
|
||
sudo certbot renew --dry-run --non-interactive --no-random-sleep-on-renew
|
||
~~~
|
||
|
||
两个域名应指向 62.234.39.66;HTTP 应 301 到 HTTPS;证书 SAN 必须匹配访问域名。ACME challenge
|
||
路径必须保持可达。证书续期后由 deploy hook reload Nginx。
|
||
|
||
FlashOps 当前配置不应包含 auth_basic、allow 或 deny 访问限制。若匿名访问出现 401/403,先检查
|
||
浏览器旧认证缓存、Nginx 生效配置和上游安全产品;Agent API 因 token 无效返回 401 是另一回事。
|
||
|
||
### 11.4 标准发布流程
|
||
|
||
1. 确认目标 Git 提交、工作树和本次发布范围;
|
||
2. 在本地执行 make test 与 make demo;
|
||
3. 检查暂存和发布内容不含 .venv、var、缓存、凭据和运行产物;
|
||
4. 使用 SQLite 在线备份 API 备份当前生产数据库;
|
||
5. 保存当前可运行发布包、SHA-256 和回滚说明;
|
||
6. 将新版本上传到独立 incoming 目录,不覆盖正在运行的目录;
|
||
7. 校验上传包 SHA-256,安装或复用服务器虚拟环境;
|
||
8. 保留生产 flashops/var,绝不能用本地空 var 覆盖;
|
||
9. 校验 systemd unit 和私有环境文件权限;
|
||
10. 切换版本并重启 flashops;
|
||
11. 轮询 127.0.0.1:18080/api/v1/health;
|
||
12. 执行 nginx -t,必要时 reload;
|
||
13. 从服务器内部和外部各验证一次 HTTPS、最终跳转、health 和 dashboard JSON;
|
||
14. 检查最近日志无新 error,并记录提交号、时间、备份和回滚点。
|
||
|
||
不要在生产目录直接手工改源码。发布物必须能映射到 Git 提交。发布失败时优先切回已经验证的旧发布
|
||
目录,并保留失败目录和日志用于排查,不能边修边覆盖证据。
|
||
|
||
### 11.5 SQLite 备份与恢复边界
|
||
|
||
运行中的 SQLite 应使用在线备份 API,不要直接复制可能正在写入的数据库:
|
||
|
||
~~~bash
|
||
sudo /data/wangzhan/app/storage-labos/flashops/.venv/bin/python - <<'PY'
|
||
import datetime
|
||
import sqlite3
|
||
from pathlib import Path
|
||
|
||
source = Path('/data/wangzhan/app/storage-labos/flashops/var/flashops.db')
|
||
stamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
|
||
target = Path('/data/wangzhan/backups') / ('flashops-db-' + stamp + '.db')
|
||
target.parent.mkdir(parents=True, exist_ok=True)
|
||
with sqlite3.connect(source) as src, sqlite3.connect(target) as dst:
|
||
src.backup(dst)
|
||
print(target)
|
||
PY
|
||
~~~
|
||
|
||
备份后记录文件大小和 SHA-256。数据库恢复是高影响动作,本文不授权 AI 自动执行。获明确授权后也要:
|
||
|
||
1. 停止 FlashOps;
|
||
2. 核对恢复目标、备份时间和校验值;
|
||
3. 再备份当前数据库;
|
||
4. 替换后修复 ubuntu:ubuntu 所有权和文件权限;
|
||
5. 启动服务;
|
||
6. 验证 health、表数量、最新 Run 和事件 seq 连续性;
|
||
7. 保留恢复前数据库,直到验收完成。
|
||
|
||
### 11.6 Gitea 运维与 COS 备份
|
||
|
||
Gitea 公开仓库允许匿名查看和 HTTPS 克隆;账号注册后需要管理员人工批准。当前不开放独立 Git SSH
|
||
端口,也没有生产 Actions Runner。
|
||
|
||
获授权后的只读检查:
|
||
|
||
~~~bash
|
||
sudo docker ps --filter name=^gitea$
|
||
sudo docker logs --tail 100 gitea
|
||
curl -fsS http://127.0.0.1:13000/api/healthz
|
||
sudo systemctl status flashops-gitea-backup.timer --no-pager
|
||
sudo systemctl list-timers flashops-gitea-backup.timer --no-pager
|
||
~~~
|
||
|
||
手工触发一次可恢复的 Gitea dump:
|
||
|
||
~~~bash
|
||
sudo systemctl start flashops-gitea-backup.service
|
||
sudo systemctl status flashops-gitea-backup.service --no-pager
|
||
find /chucun/wangzhan-production/backups/gitea -maxdepth 1 -type f \
|
||
-name 'gitea-*.zip' -ls
|
||
~~~
|
||
|
||
备份脚本使用 Gitea 自身 dump,生成 ZIP 和 SHA-256 sidecar,成功复制到 COS 后清理本机临时包。
|
||
定时器每日约 03:20 运行并带随机延迟。实时仓库、Git 对象和 Gitea SQLite 必须留在
|
||
/data/gitea/data 本地文件系统;COSFS 不提供数据库依赖的完整锁与原子语义,只能用于备份。
|
||
|
||
Gitea 恢复或升级是高影响操作:必须先验证备份、阅读目标版本迁移说明、安排维护窗口,并保留旧容器
|
||
镜像和 /data/gitea/data 快照。镜像必须固定版本,不能使用 latest。
|
||
|
||
### 11.7 故障定位
|
||
|
||
公网 502:
|
||
|
||
~~~bash
|
||
sudo systemctl status flashops --no-pager -l
|
||
sudo journalctl -u flashops -n 200 --no-pager
|
||
curl -v http://127.0.0.1:18080/api/v1/health
|
||
sudo nginx -t
|
||
sudo tail -n 100 /www/wwwlogs/flashops.imagebrewing.com.error.log
|
||
~~~
|
||
|
||
常见原因是服务未启动、Python 依赖错误、18080 端口不一致、var 不可写或 systemd 安全策略阻止路径。
|
||
|
||
SQLite locked:
|
||
|
||
- 确认只有一个 Uvicorn worker;
|
||
- 检查是否有脚本绕过 session 直接写数据库;
|
||
- 所有状态写入应走同一事务和事件入口;
|
||
- 需要多进程时迁移 PostgreSQL,不能继续增加 SQLite 锁重试掩盖问题。
|
||
|
||
Gitea 页面不可访问:
|
||
|
||
~~~bash
|
||
sudo docker ps --filter name=^gitea$
|
||
sudo docker logs --tail 100 gitea
|
||
curl -fsS http://127.0.0.1:13000/api/healthz
|
||
getent ahostsv4 git.imagebrewing.com
|
||
sudo nginx -t
|
||
~~~
|
||
|
||
HTTPS 证书异常时核对 DNS、证书路径、证书域名、有效期和 Nginx 是否已 reload。不要在原因未确认时
|
||
重复签发证书或删除现有证书目录。
|
||
|
||
### 11.8 生产验收清单
|
||
|
||
- [ ] flashops.service 为 active;
|
||
- [ ] Gitea 容器为 running,loopback health 为 pass;
|
||
- [ ] Nginx active 且 nginx -t 成功;
|
||
- [ ] 两个域名解析到 62.234.39.66;
|
||
- [ ] HTTP 301 到 HTTPS;
|
||
- [ ] 两张证书域名正确、未过期、自动续期可用;
|
||
- [ ] 未登录访问 FlashOps 控制台最终返回 200;
|
||
- [ ] FlashOps health 返回 status=ok;
|
||
- [ ] dashboard summary 返回合法 JSON;
|
||
- [ ] 未登录可以查看并克隆 public Git 仓库;
|
||
- [ ] 生产 var 数据可写,但应用源码目录不被随意写入;
|
||
- [ ] 最新日志没有新增 error;
|
||
- [ ] FlashOps 数据库备份有 SHA-256;
|
||
- [ ] Gitea COS dump 有 ZIP 和有效 SHA-256 sidecar;
|
||
- [ ] 发布提交号、时间和回滚点已经记录。
|
||
|
||
---
|
||
|
||
## 12. 常见任务剧本
|
||
|
||
### 12.1 “看看项目并继续完善”
|
||
|
||
1. 以本文的能力边界、状态机和安全规则建立基线;
|
||
2. 若获得仓库,检查工作树、源码和测试;只有本文时先做线上只读核验;
|
||
3. 运行可用的测试与模拟演示;
|
||
4. 区分已实现、部分实现和待实现;
|
||
5. 优先选择一个能独立验收的纵向切片;
|
||
6. 实现、补测试、同步本文;
|
||
7. 报告实际证据,不把规划写成完成。
|
||
|
||
### 12.2 “排查网站打不开”
|
||
|
||
1. 从公网检查 DNS、TLS、重定向和最终状态码;
|
||
2. 检查 Nginx 生效配置和日志;
|
||
3. 检查本机内部端口和服务状态;
|
||
4. 区分 401、403、404、502 和浏览器缓存;
|
||
5. 诊断任务只报告原因,除非用户同时要求修复。
|
||
|
||
### 12.3 “修改 API”
|
||
|
||
1. 只有获得仓库后才能实施修改;先核对路由、schema、service 和测试;
|
||
2. 保持业务逻辑不堆在 route;
|
||
3. 状态写入走事件记录;
|
||
4. 为正常与错误路径增加测试;
|
||
5. 检查 OpenAPI;
|
||
6. 更新本文中的接口、状态码、权限和部署说明。
|
||
|
||
### 12.4 “增加真实硬件能力”
|
||
|
||
先停止直接实现危险命令。必须先确认:
|
||
|
||
- 使用可牺牲 DUT,不是系统盘;
|
||
- 有明确序列号和固件型号匹配;
|
||
- 已实现签名命令模板与 allowlist;
|
||
- 有审批、短期授权、审计和 Evidence Bundle;
|
||
- Agent 与控制面身份可信;
|
||
- 有恢复、急停和人工接管方案;
|
||
- 用户明确批准真实动作。
|
||
|
||
条件不完整时,只允许实现接口、模拟器、只读探针和测试。
|
||
|
||
### 12.5 “发布一个版本”
|
||
|
||
1. 确认工作树和目标提交;
|
||
2. 运行完整测试与演示;
|
||
3. 检查秘密和发布范围;
|
||
4. 备份服务器应用数据;
|
||
5. 部署可追溯提交;
|
||
6. 重启并验证内部与公网健康;
|
||
7. 检查日志、证书和备份;
|
||
8. 记录提交号、时间、验证结果和回滚点。
|
||
|
||
---
|
||
|
||
## 13. AI 交付时的固定报告格式
|
||
|
||
AI 最终回复至少包含:
|
||
|
||
~~~text
|
||
结果:
|
||
- 本次目标是否完成
|
||
|
||
改动:
|
||
- 文件与关键行为
|
||
|
||
验证:
|
||
- 实际运行的命令
|
||
- 通过/失败数量
|
||
- 浏览器或生产验收结果
|
||
|
||
Git:
|
||
- 分支
|
||
- 提交号
|
||
- Pull Request 或远程地址
|
||
|
||
风险与边界:
|
||
- 尚未实现或未验证的内容
|
||
- 是否涉及生产、真实设备或凭据
|
||
|
||
下一步:
|
||
- 仅列真正需要用户决定或后续开发的事项
|
||
~~~
|
||
|
||
不得只说“应该可以”“理论上完成”。必须区分已执行验证、静态检查和推断。
|
||
|
||
---
|
||
|
||
## 14. 完成定义
|
||
|
||
一项 AI 开发任务只有同时满足以下条件才算完成:
|
||
|
||
- 用户要求的行为已经实现,而不是只写方案;
|
||
- 原有未提交工作得到保护;
|
||
- 相关测试通过,或失败原因被明确记录;
|
||
- 安全门禁、事件写路径和状态机未被绕过;
|
||
- 文档与实际代码、API 和部署保持一致;
|
||
- 没有提交凭据、生产数据或运行产物;
|
||
- Git 提交范围清晰并可回滚;
|
||
- 如果要求部署,公网、内部健康、日志和备份均已验证;
|
||
- 最终报告包含证据、提交号、风险和边界。
|
||
|
||
当本文件中的域名、端口、路径、API、安全策略、测试数量或部署结构发生变化时,修改代码的 AI
|
||
必须在同一个 Pull Request 中同步更新本文件。
|