边缘 AI:模型签名仓 + Mender Artifact 实操手册
日期:约 2026-10-25(Asia/Shanghai)
定位:系统 OTA 与模型 OTA 分轨的可执行手册 —— Mender(或等价)管根文件系统/容器;自建签名模型仓管.rknn / .bmodel / .engine / .hef / .onnx+ manifest(model.ver= 内容哈希)。
衔接:edge-ai-gstreamer-ota-cheatsheet.md(目录与选型)、edge-ai-event-schema-acceptance.md(事件与model.ver)、edge-ai-license-ledger-template.md(AGPL/Balena 红线)、edge-ai-ppe-smoke-fire-model-shortlist.md(样例 PPE)、edge-ai-power-dev-roadmap.md(D0–30 / W4)。
实证:Mender 官方 Create a custom Update Module、Artifact 概览、Sign and verify;Update Modules v3 File API(/usr/share/mender/modules/v3)。不确定处标 unknown,勿当固化版本号。
1. 架构一句话
| 轨道 | 管什么 | 制品 | 回滚手段 |
|---|---|---|---|
| 系统轨(Mender) | 根文件系统、内核/BSP、推理 Agent、GStreamer 插件、模型公钥 | .mender rootfs-image 或容器 Update Module |
A/B 分区;启动/报到失败自动回旧分区 |
| 模型轨(自建签名仓) | 权重与 meta:.rknn/.bmodel/.engine/.hef/.onnx + manifest.json |
内容寻址包;可选再包一层 自定义 Update Module Artifact | current/previous 软链;健康检查失败切回上一 model.ver |
硬规则:未签名权重 禁止塞进 rootfs;百 MB 级引擎进 rootfs 会拖垮 delta、放大变砖面、并把日更绑死在整镜像发布上(见 OTA cheatsheet B.2 / B.3)。数据分区(如 /data)应独立于 A/B rootfs,保证系统回滚时不误删已激活模型;反之,模型回滚也绝不改写只读根分区。公钥随系统镜像预置,权重永不进镜像层。
┌─────────────┐ HTTPS/mTLS ┌──────────────────┐
│ Mender 服务 │ ──────────────► │ 设备:Mender Client│──► A/B rootfs
└─────────────┘ └──────────────────┘
┌─────────────┐ 签名包+index ┌──────────────────┐
│ 模型签名仓 │ ──────────────► │ Model Agent │──► /data/models/
│ (MinIO等) │ │ 验签→store→软链 │ current/previous
└─────────────┘ └──────────────────┘
│ SIGHUP / 本地 socket
▼
GStreamer / 推理进程
与 Balena 一句对比:系列更推 Mender + 自建仓 —— Mender 客户端/服务端以 Apache-2.0 为主、合规清晰;openBalena 含 AGPL-3.0 风险(许可台账 TL-003),闭源站端盒子标书勿默认「可闭源 OTA」。全容器团队若已沉没 Balena,可让其管应用容器,模型仍走数据分区签名仓,避免每次改 PPE/烟火权重都打整镜像。许可与选型细节以台账及 OTA cheatsheet B.1 为准,本文不重复展开 AGPL 传染边界。
2. 目录与 Manifest
2.1 设备落盘约定(与 cheatsheet 对齐)
/data/models/
store/<sha256>/ # 不可变:权重 + manifest.json + labels
current -> store/<sha>/ # 推理只认此软链
previous -> store/<sha>/ # 一键回滚
keys/model-verify.pub # 仅公钥;私钥永不落设备
2.2 manifest.json 示例(可复制)
model.ver 必须等于 主权重文件的内容哈希(SHA-256 hex),与事件 Schema 验收否决项一致。
{
"name": "ppe-yolox-s-int8",
"task": "ppe",
"schema_ver": "1.0.0",
"model.ver": "a3f1c9e8b2d0471a6e5f90c4d8b7a216e3f0c1d2a4b5e67890abcdef12345678",
"backend": "rknn",
"files": [
{
"path": "model.rknn",
"sha256": "a3f1c9e8b2d0471a6e5f90c4d8b7a216e3f0c1d2a4b5e67890abcdef12345678",
"bytes": 12456789
},
{
"path": "labels.txt",
"sha256": "11aa22bb33cc44dd55ee66ff77889900aabbccddeeff00112233445566778899",
"bytes": 128
}
],
"sig": {
"alg": "ed25519",
"key_id": "model-prod-2026q4",
"value_b64": "<DetachedSignatureBase64>"
},
"compat": {
"board": ["rk3588-evb", "rk3588-power-box-v2"],
"soc": "rk3588",
"runtime": {
"rknn_toolkit2": "2.x.x",
"librknnrt": ">=2.x",
"jetpack": null,
"tensorrt": null,
"hailort": null
},
"input": { "w": 640, "h": 640, "fmt": "RGB", "mean": [0,0,0], "std": [255,255,255] }
},
"rollback": {
"keep_n": 3,
"healthcheck": "http://127.0.0.1:9100/readyz",
"fail_threshold": 3,
"window_sec": 120,
"on_fail": "switch_previous_and_reload"
},
"license_ledger_id": "WT-00x",
"channel": "prod",
"built_at": "2026-10-25T08:00:00+08:00"
}
task 建议值 |
说明 |
|---|---|
ppe / smoke_fire |
对齐 PPE/烟火短名单与 D0–30 |
meter / thermal / oil_level |
表计/红外/油位 |
vib / us |
振动/超声;事件码见 SOP |
sig 覆盖对象:对「规范化后的 manifest 去掉 sig 字段 + 按路径排序的各文件 sha256」做摘要再签(实现自定,但设备与 CI 必须同一规范)。
3. 签名流程(两层密钥,勿混用)
3.1 模型仓签名(日更)——推荐 Ed25519
设备只持公钥;私钥离线或 HSM/隔离 CI 签名机。
# 一次性:离线生成(示例用 openssl ed25519;亦可用 minisign/cosign,择一并写入台账)
openssl genpkey -algorithm ED25519 -out model-sign.key
openssl pkey -in model-sign.key -pubout -out model-verify.pub
# CI:对包目录算哈希、写 manifest、分离签名
sha256sum model.rknn labels.txt > HASHES
# 将 HASHES + manifest 规范字节串签名 → 写入 manifest.sig.value_b64
# 发布:store/<model.ver>/ + 更新 index.json
校验失败:拒绝加载、不切 current,上送事件(对齐 Schema):
{
"schema_ver": "1.0.0",
"ts": "2026-10-25T08:30:00+08:00",
"device_id": "rk3588-cabinet-01",
"event": {
"code": "MODEL_VERIFY_FAIL",
"quality": "confirmed",
"detail": "sig_mismatch_or_sha256"
},
"model": { "ver": "offered-but-rejected", "path": "/data/models/store/..." }
}
等价码可映射为 MODEL_SIG_FAIL / MODEL_COMPAT_REJECT,但台账与 MQTT topic 须统一。
3.2 Mender Artifact 签名(系统轨)——官方支持 RSA≥3072 或 ECDSA P-256
官方 Sign and verify(核验日约 2026-10):不支持 Ed25519 Artifact 签名(社区讨论亦确认暂无计划)。模型轨可用 Ed25519;系统轨跟官方能力。
# RSA 3072(官方推荐下限)
openssl genpkey -algorithm RSA -out mender-private.key -pkeyopt rsa_keygen_bits:3072
openssl rsa -in mender-private.key -out artifact-verify-key.pem -pubout
# 签名 / 校验
mender-artifact sign artifact.mender -k mender-private.key -o artifact-signed.mender
mender-artifact validate artifact-signed.mender -k artifact-verify-key.pem
设备 mender.conf 配置 ArtifactVerifyKey 或 ArtifactVerifyKeys(轮换窗口可多钥);开启后拒收未签名 Artifact。
4. Mender Artifact:模型包如何「可选」进 Update Module
4.1 官方概念(核验摘要)
| 要点 | 官方口径 |
|---|---|
| Update Module 位置 | /usr/share/mender/modules/v3/<type>,可执行文件 |
| 调用约定 | 两参数:STATE、FILES(File API 根路径) |
| 必做状态 | 实践上至少实现 ArtifactInstall |
| 回滚 | SupportsRollback 打印 Yes;ArtifactInstall 先备份,ArtifactRollback 恢复 |
| 应用类 Artifact | mender-artifact write module-image -T <type> -n <name> -c <device_type> -f ... |
| 载荷位置 | $FILES/files/* |
| 是否重启 | NeedsArtifactReboot 可答 No(模型更新通常不必整机重启) |
推荐默认路径:不经 Mender 传大权重 —— 模型 Agent 直连签名仓(带宽、差分、日变更顺)。
可选路径:变电站弱网/离线柜,用自定义 Module(如 edge-model)把已签名整包写入 /data/models/store/,再由同一 Agent 验签激活。即便走 Module,验签逻辑仍在 Agent,Module 只负责落盘与触发。
4.2 最小 Update Module 骨架(示意,须幂等)
#!/bin/bash
# /usr/share/mender/modules/v3/edge-model
set -euo pipefail
STATE="$1"; FILES="$2"
STORE=/data/models/store
case "$STATE" in
NeedsArtifactReboot) echo "No" ;;
SupportsRollback) echo "Yes" ;;
ArtifactInstall)
# 期望 payload:manifest.json + 权重;先拷到 staging,再调 model-agent activate
STAGE=$(mktemp -d /data/models/.stage.XXXX)
cp -a "$FILES"/files/. "$STAGE"/
/usr/bin/model-agent activate --from-dir "$STAGE" --mender-context "$FILES"
;;
ArtifactRollback)
/usr/bin/model-agent rollback --to previous
;;
esac
exit 0
# 工作站打包(勿把未签名裸权重当最终产物)
DEVICE_TYPE=rk3588-power-box-v2
mender-artifact write module-image \
-T edge-model \
-n ppe-yolox-s-int8-20261025 \
-c "$DEVICE_TYPE" \
-o ppe-model.mender \
-f dist/manifest.json \
-f dist/model.rknn \
-f dist/labels.txt \
-f dist/manifest.sig
# 再对 .mender 做官方 Artifact 签名(RSA/ECDSA)
mender-artifact sign ppe-model.mender -k mender-private.key -o ppe-model-signed.mender
Standalone / 离线:mender-update install /path/ppe-model-signed.mender(mender-client 4.0.0 之前命令名为 mender,以现场版本为准;不确定标 unknown)。
5. 回滚:模型层 vs Mender A/B
| 故障 | 谁处理 | 动作 |
|---|---|---|
| 新 rootfs 起不来 / 报到失败 | Mender A/B | 自动回旧分区;模型软链原样保留在数据分区 |
| 新模型验签失败 | Model Agent | 不激活;事件 MODEL_VERIFY_FAIL |
| 激活后 ready 探针失败 N 次 | Model Agent | ln -sfn previous current → 通知推理重载;ack=rolled_back |
| 模型好、但 BSP/runtime 不兼容 | Agent compat 检查 |
rejected;需先发系统轨 Artifact 升 Toolkit/JetPack 再发模型 |
| 电源中断在 Module 安装中 | Mender 状态机 | Module 须幂等;ArtifactRollback 可恢复上一 store |
关系写清:A/B 保命(系统),软链保业务(模型)。禁止指望「回滚 rootfs」顺带走模型版本,也禁止「只回滚模型」修复内核崩溃。验收时分别出具两份演练记录:① 拔电或故意坏 rootfs Artifact → 回到旧分区且 current 模型仍可推理;② 投放坏签名/坏兼容模型 → MODEL_VERIFY_FAIL 或自动 rolled_back,rootfs 版本号不变。这与路线图 W4「系统镜像 / 模型包分离」门禁一致。
健康检查伪代码:
for i in $(seq 1 "$FAIL_THRESHOLD"); do
curl -fsS "$HEALTHCHECK" && exit 0
sleep $((WINDOW_SEC / FAIL_THRESHOLD))
done
model-agent rollback --reason healthcheck
# MQTT: ota/model/ack {"status":"rolled_back","model.ver":"<previous>"}
6. 安全边界
- 模型仓传输:HTTPS;优先 证书钉扎(SPKI pin)或设备 mTLS 客户端证;禁止「仅明文 HTTP + 信任任意 CA」。
- 网络分区:模型仓与 MQTT Broker 放站端/地市保护网或跳板区;禁止推理进程直连公网任意 URL。
- 权限:Agent 以专用用户跑;只写
/data/models;禁止 Update Module / Agent 执行远程下发的任意脚本(payload 白名单:权重 + json + 文本标签)。 - 密钥:模型私钥与 Mender 私钥分离;轮换用双公钥窗口;私钥不出签名机。
- 许可:发布前过许可台账;AGPL/NC 资产不得入
channel=prod(见短名单与台账红线)。 - 日志:不落私钥、不落完整预签名 URL 长时效链接;抓拍合规仍按 Schema 文档。
7. 两周落地清单(对齐路线图 W4)
| 日 | 动作 | 完成标准 |
|---|---|---|
| D1–2 | 离线生成 模型 Ed25519 + Mender RSA/ECDSA 密钥;设备预置两公钥 | 私钥不进 Git;台账登记 key_id |
| D3–4 | 搭 MinIO(或等价)+ index.json;内网 DNS/证书 |
一台开发机可 curl 下载并验签 |
| D5–6 | 实现 model-agent:下载→sha256→验签→compat→store→烟测→软链→SIGHUP |
单测覆盖错签/错板型 |
| D7–8 | 从 PPE 短名单 A 轨导出样例 → 打 manifest → 发布 |
model.ver==文件哈希;事件可带该 ver |
| D9–10 | 一台 RK3588 或 Orin 试点:直连仓激活成功;可选再打 edge-model Artifact |
推理读 /data/models/current |
| D11–12 | 断网:本地/U 盘安装签名包;错签:改一字节权重 → 必须 MODEL_VERIFY_FAIL 且不切换 |
录像/日志归档 |
| D13–14 | 健康检查失败自动回 previous;与 Mender rootfs canary 各演练一次 |
书面:系统轨/模型轨分离证据 |
7.1 Agent 状态机与 MQTT(与 cheatsheet B.3 对齐)
业务遥测与 OTA 控制建议分 topic 前缀,避免环路(见 GStreamer OTA cheatsheet A.6)。
| Topic | 方向 | 载荷要点 |
|---|---|---|
ota/model/offer |
云→端 | url、model.ver、sha256、sig、channel、compat 摘要 |
ota/model/ack |
端→云 | verified / activated / rolled_back / rejected + 原因码 |
evt/edge/# |
端→云 | 业务事件;校验失败另发 MODEL_VERIFY_FAIL |
推荐状态序:offered → downloaded → verified → staged → health_ok → activated;任一步失败保持原 current,写 previous 不变。激活用 ln -sfn 原子切换,推理进程只读软链,收到 SIGHUP 或本地 Unix socket「reload」后重新打开权重。
index.json 仓侧最小字段:channel、hw、model.ver、url、sha256、sig、min_agent_ver、schema_ver。设备可缓存上一份 index;断网时允许 U 盘放入同等目录结构后执行 model-agent activate --from-dir /media/usb/store/<sha>。
7.2 RK3588 / Orin 试点差异(compat 怎么填)
| 平台 | backend |
compat 必填 | 常见坑 |
|---|---|---|---|
| RK3588 | rknn |
rknn_toolkit2 / librknnrt 大版本 |
Toolkit 升级后旧 .rknn 可能拒载 → 先系统轨再模型轨 |
| Jetson Orin | tensorrt / engine |
jetpack、tensorrt、CUDA 能力 |
JetPack 小版本也可能导致 .engine 不兼容;必须同环境重建 |
| Hailo / BM1684X 等 | hef / bmodel |
对应 runtime 版本 | 同理:runtime 进系统轨,权重进模型轨 |
试点机柜建议:固定一块「金样板」只收 channel=canary,观察 24–48h 无 MODEL_VERIFY_FAIL、无自动回滚后再推 prod。电力站端若无法 MQTT 出站,用 Mender standalone + U 盘签名包,仍走同一 Agent 验签路径。
7.3 失败模式速查
| 现象 | 根因倾向 | 处置 |
|---|---|---|
| OTA「成功」但全是错检 | compat 未拦、预处理不一致 | Agent 强制核对输入尺寸/mean/std;拒切 |
| 模型激活后管道卡死 | 引擎崩溃拖死主进程 | systemd Restart= + 健康检查回滚;勿整机 A/B |
| 签名公钥轮换后全拒 | 只换了仓侧钥、设备未双钥窗口 | ArtifactVerifyKeys / 模型双公钥并存一个版本周期 |
| 磁盘占满 | keep_n 未执行 |
GC:保留 current/previous + 最近 N 个 store |
| 把训练代码打进包 | 许可与攻击面双爆 | 包内白名单仅权重+json+labels;AGPL 训练栈见台账 |
7.4 为何「模型 Module」仍要二次验签
即便 Mender 已对 .mender 做 RSA/ECDSA 校验,模型内容仍应保留独立 Ed25519(或 RSA)分离签名:
- 直连仓路径不经过 Mender,必须自保;
- 同一
model.ver可能被 USB、内网 mirror、Module 三条通道投递,内容信任锚应是 manifest 签名,而非投递通道; - 便于审计:许可台账 ID、
schema_ver、板型列表与哈希绑在同一 signed blob。
系统轨负责「谁有权改机器」;模型轨负责「谁有权改检测行为」。二者私钥分离,泄露面互不传染。
8. 与已有文档的接法
- GStreamer OTA cheatsheet:本手册展开其 B.3「自建签名仓」与「Mender 勿塞大模型」。
- 事件 Schema:激活成功后遥测必带
model.ver;校验失败用MODEL_VERIFY_FAIL。 - PPE 短名单:试点权重只允许
legal_status达 eng_ok 且非红灯。 - 许可台账:密钥算法、MinIO、Mender、训练栈 SPDX 分行;Balena/Ultralytics AGPL 保持黄/红直至合同。
- 开发路线图:本清单位于 D0–30 的 W4 门禁(
model.ver与哈希一致、OTA 分轨记录)。
9. 参考链接(官方)
- https://docs.mender.io/artifact-creation/create-a-custom-update-module
- https://docs.mender.io/client-installation/use-an-updatemodule
- https://docs.mender.io/artifact-creation/create-an-artifact
- https://docs.mender.io/overview/artifact
- https://docs.mender.io/artifact-creation/sign-and-verify
- https://github.com/mendersoftware/mender/blob/master/Documentation/update-modules-v3-file-api.md
本文为工程手册草稿;密钥管理与法务条款须安全/法务复核。Mender 小版本命令差异以现场 mender-artifact --version / 客户端版本文档为准(unknown 勿写死)。