SINGLE SOURCE OF TRUTH · 唯一下发源

把整片机群
收敛到一台服务端

Zephyr Music Server 用一台 x86 Linux 主机统一管理数十至数百台 Hi-Fi 终端: 签名制品仓库、BOM 期望态、灰度下发、播放门控、席位授权与全量审计。 纯 Go,无 CGO,SQLite 单文件,控制台内嵌在二进制里。

  • 0CGO 依赖
  • 3终端架构
  • 126冒烟断言
  • MIT开源许可
01 · CAPABILITIES

七个域,全部真实实现

不是路线图,不是占位。以下每一项都在仓库里有对应的 Go 代码、API 路由与端到端冒烟断言。

下发核心

应用目录与多架构制品仓库,入库强制 GPG 验签。BOM 期望态支持草稿、校验、发布、导入、导出与历史回滚;任务状态机贯穿预检到验证,失败自动回滚。

  • 制品入库 enforce 验签,未签名直接拒绝
  • BOM 版本历史完整保留,可回滚到任意已发布版本
  • 播放门控:播放中绝不中断,等空闲再动服务
  • 10 段任务状态机 + 失败回滚链

终端生命周期

一次性注册令牌(TTL 可选)、三步添加向导、终端配置面板,以及注销后的撤销恢复。

  • 别名 · 房间 · 期望 BOM · DAC 仲裁
  • 播放保护 · 维护窗口 · 离线阈值
  • 注销 + 撤销恢复,误操作可回退

授权与许可

离线签名许可证激活,席位实时占用统计,逐终端授权与撤销。

  • 许可证离线签发,服务端只验签
  • 下发前许可证 + 授权双门禁
  • 席位占用可视化,超额即时拦截

运维与诊断

远程 apt 检查与升级走独立命令通道;mDNS / SSDP / 多播网络诊断;内核待重启告警。

  • 灰度发布按环推进,环内未完成禁止推进
  • 系统升级受播放保护双侧拦截
  • 网络诊断定位发现协议故障

安全信任根

离线主密钥、受控签名子密钥、服务端公钥信任根三层分工,密钥导入与信任根导出。

  • 服务端永不持有私钥
  • 终端本地控制台变更需节点密钥
  • 全量审计 + CSV / JSON 导出

可观测

总览 KPI 与服务端消耗、终端资源与 DAC 占用、告警铃铛抽屉、终端控制台反向代理。

  • 告警确认 · 解决 · 主动扫描
  • 核心服务监控融合进服务端消耗
  • 反向代理直达终端本地控制台

终端 Agent

注册、心跳、快照;任务执行覆盖 apt、dpkg、配置渲染与 systemd 单元白名单;DAC 仲裁执行与播放保护本地门禁;自带本地控制台(服务、系统更新、网络诊断、电源)。以 root 运行,但每个动作都被白名单与开关约束。

amd64arm64armhfsystemd 白名单ALLOW_POWERALLOW_LOCAL节点密钥门禁
02 · ARCHITECTURE

签名链决定谁能下发

制品从离线签名主机进入仓库,服务端只持公钥信任根,终端只接受通过验签的期望态。

离线主密钥 OFFLINE MASTER · 永不联网 签名子密钥 受控签名主机 · 离线/CI 构建 服务端 · 公钥信任根 制品仓库 + BOM + 任务 + 授权 enforce 验签门禁 未签名制品拒绝入库 / 拒绝下发 树莓派arm64 / armhf x86 小主机amd64 播放终端DAC 仲裁
  1. 01

    主密钥离线

    主密钥保存在不联网的介质上,只用于签发子密钥与许可证。它从不进入服务端,也不进入 CI。

  2. 02

    子密钥签名

    构建在离线主机或 CI 完成,签名子密钥对制品出具分离签名。服务端不做在线构建,也不做二进制补丁——那会破坏签名链。

  3. 03

    服务端只持公钥

    服务端导入公钥作为信任根,负责仓库、BOM 期望态、任务编排、席位授权与审计。私钥泄露面被压缩到签名主机一处。

  4. 04

    enforce 双门禁

    入库与下发都验签。生产必须 enforce;dev 仅用于本地联调。下发前还要过许可证与终端授权两道门。

  5. 05

    终端受约束执行

    Agent 以 root 运行,但动作被 systemd 单元白名单、ALLOW_POWER / ALLOW_LOCAL 与节点密钥三重约束。播放保护在服务端与终端双侧执行。

03 · SECURITY

把门禁亲手拨一遍

下面是真实门禁逻辑的可交互复刻:切换签名模式与播放保护,看制品与任务如何被放行或拦截。

制品入库门禁

enforce

下发播放门控

空闲

不变量

  • 服务端永不持有私钥只导入公钥信任根;密钥导入接口拒绝 PRIVATE KEY BLOCK。
  • 许可证离线签发服务端只验签与统计席位,不生成许可证。
  • Agent 动作白名单systemd 单元白名单 + ALLOW_POWER / ALLOW_LOCAL + 节点密钥。
  • 播放保护双侧执行服务端拦截下发与升级,终端本地控制台同样拦截停服务与关机。
  • 全量审计每次变更落审计,支持 CSV / JSON 导出。
04 · LIVE SIMULATION

亲手操纵一整片机群

下面这台机群完全由你驱动:改数量、改播放比例、改心跳与遥测、改灰度环、改签名模式与门控,画面立刻响应。点任意终端看详情,按「开始灰度下发」跑完整任务链。

CONTROL DECK13 项实时参数
3
0在线
0播放中
0离线
—当前环
0/0席位
enforce门控开
点击终端查看详情 · 拖动滑块实时改变机群
任务链 · 实时0 个任务

本演示为浏览器内本地模拟,用于展示真实产品的状态机与门禁行为,不连接任何真实终端。真实控制台内嵌在服务端二进制中。

05 · DEPLOY

一条命令装机,一条命令入网

服务端目标是 x86_64 Linux + systemd。终端安装器自动识别 amd64 / arm64 / armhf,从服务端 /dl/ 拉取对应 deb。

A

服务端 · x86_64 Linux

systemd
sudo apt-get update && sudo apt-get install -y gnupg
tar -xzf zephyr-music-server-0.2.0.tar.gz
cd zephyr-music-server-0.2.0/server
sudo ./install.sh
curl -s http://127.0.0.1:8080/api/v1/health

安装器落地二进制、systemd 单元、配置、公钥信任根与 /dl 分发目录。浏览器打开 http://<服务端IP>:8080/ 即进内嵌控制台。生产请在前面加 TLS 反向代理并配置鉴权。

B

终端 · 每台一条命令

架构自动识别
curl -s -X POST http://<服务端IP>:8080/api/v1/nodes/token \
  -H 'Content-Type: application/json' \
  -d '{"ttl_seconds":1800}'
curl -fsSL http://<服务端IP>:8080/dl/agent-install.sh | \
  sudo bash -s -- --server http://<服务端IP>:8080 \
                  --token <一次性令牌>

控制台「终端管理 → 添加终端」向导签发令牌后会给出同款命令。离线环境可直接 dpkg -i 包内 deb。注册成功后终端立即出现在控制台。

06 · SPECIFICATIONS

技术规格

没有隐藏运行时,没有外部数据库服务,没有容器编排依赖。

服务端

语言Go 1.27+ · CGO_ENABLED=0
存储SQLite 单文件(modernc.org/sqlite,纯 Go)
控制台go:embed 内嵌 SPA,随二进制分发
目标x86_64 Linux + systemd(另提供 arm64 二进制)
监听0.0.0.0:8080,TLS 由前置反向代理负责
依赖gnupg(验签)· 无其他系统依赖
许可MIT

终端 Agent

模块独立 Go module,仅标准库
架构amd64 · arm64 · armhf(GOARM=7)
打包内置 debpack 生成 deb,无需 dpkg-deb / 交叉 gcc
权限root,受 systemd 单元白名单约束
开关ALLOW_POWER · ALLOW_LOCAL
本地控制台端口 8080,变更需节点密钥
心跳默认 15s,离线阈值 = 3× 心跳

验证状态

126 / 0enforce 端到端冒烟断言 通过 / 失败
全绿服务端与 Agent go build / vet / test
CI 通过GitHub Actions:测试 → 冒烟 → 构建 → 发布
16 / 16发布包 SHA256SUMS 校验全部 OK
双视口控制台桌面 1440 与移动 515 零溢出零裁剪
07 · FAQ

常见问题

服务端必须联网吗?

不需要。主密钥与许可证签发都可以完全离线。服务端只在入库时验签、在下发时把制品推给终端,整个链路可以在隔离 LAN 内闭环运行。终端安装器从服务端 /dl/ 取包,不访问公网。

为什么不做在线构建和二进制补丁?

在线构建要求服务端内置各应用的交叉工具链,体积与攻击面都不可接受;二进制补丁会破坏已签名制品的签名链。两者都被删除,改为「离线/CI 构建 → 签名 → 上传入库」这一条可验证的路径。

播放中的终端会被升级打断吗?

不会。播放门控(FR-3.10)在服务端与终端双侧执行:任务进入 waiting_idle 等待空闲,系统升级、停服务与关机在播放中同样被拦截。服务端返回 409,终端本地控制台也拒绝执行。

许可证是怎么工作的?

许可证由离线主密钥签发,服务端只验签并统计席位占用。每次下发前都要过「许可证有效 + 该终端已授权 + 席位充足」三道检查,任一不满足即拦截。

灰度发布怎么推进?

环的百分比必须合计 100,绑定的 BOM 版本必须已发布。当前环仍有未完成任务时推进一步会返回 409,避免把故障扩散到下一环。删除灰度不会撤销已下发的任务,且支持撤销恢复。

控制台的鉴权在哪里做?

在反向代理层。控制台假定可信 LAN,生产部署请在 Nginx / Caddy 前配置 TLS 与 OIDC 或 basic-auth。产品内不做 mock 角色切换,因为那不是真鉴权。

误删了 BOM 版本或终端怎么办?

BOM 保留完整版本历史,可回滚到任意已发布版本;回滚会生成一个新的已发布版本而不是覆盖历史。终端注销后支持撤销恢复。破坏性操作在控制台都带撤销入口。

支持哪些终端硬件?

安装器自动识别 amd64、arm64 与 armhf,覆盖 x86 小主机、树莓派 4/5(64 位)与树莓派 2/3(32 位)。Agent 仅依赖标准库,deb 由内置 debpack 生成,不需要目标机上有构建工具。

READY TO DEPLOY

把机群交给一个下发源

下载发布包,装上服务端,每台终端一条命令入网。