mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-02 15:27:33 +00:00
三条主线,由 e2e 实测驱动。 ## 一、差分 OTA(internal/update/delta.go) 实测:改一行逻辑,88MB 的二进制逐字节只差 80 字节,而 OTA 一直下整包。 用 rsync 族的滚动哈希块匹配(4KB 粒度),不是 bsdiff —— 后者需 17×N 峰值内存(88MB → 1.5GB),而升级恰可能发生在内存紧张时。 真实二进制实测:88,036,728 字节 → 补丁 77,316 字节(0.088%)。 三道闸:应用前验旧文件 sha256(错基线会静默产出垃圾)、应用后验结果 sha256、COPY/LITERAL 越界防护。任何失败**退回整包**,不向上抛 —— 差分是优化,不是可用性前提。 接入:Source.Resolve 优先试 delta;CheckRelease 按 -from-<commit8>.delta 匹配(不猜基线);CLI 新增 -make-delta;打包脚本 build_delta(需 PREVIOUS_HOMED),delta 进 SHA256SUMS。 ## 二、安装形态分派(internal/update/install.go + channel.go) 内核对「能不能自己升级」此前只有一个答案:直接替换二进制。而 deb/rpm 形态下那样做会让 dpkg/rpm 数据库与实际内容不一致(下次 apt upgrade 覆盖回旧版,或 dpkg 拒绝后续升级)。 新增探测:deb/rpm/tar/container/source 五类形态 + 变体(full/server/ client/toolchain)+ 目录写权限。判据全部来自可观测系统事实(dpkg 的 info 目录、容器标记文件),不做路径推断。 三端统一走 PlanUpgrade 分派;形态闸门放在**下载 880MB 之前**。 ## 三、事件总线统一受安全监控(internal/events + security) 用户指正:「eventbus 上所有事件都要接受安全模块的监控」。 此前 EventPolicy 只被 internal/sdk/events.go 调用,而那是四条路径中的 一条。s.Publish / s.Subscribe / s.EventBus() 拿的是裸 *events.Bus, 任何插件都能绕过策略发任意事件(含伪造内核事件)。 现在闸门在 Bus.Publish/Subscribe 本体上(Guard 接口注入,避免 events ↔ security 成环),四条路径全部覆盖。sdk.EventBus() 改返回带身份的 BusHandle。内核身份免授权,但载荷照样过凭据闸门。 白名单按主体收窄:agent_output/stage/terminal_output 只允许承载者 (webui/cli/agentcli)发布 —— 它们是内核向外的输出面,谁都能发就等于 能伪造 agent 说的话。 ## e2e 暴露并修掉的三个真实回归 [bus] subscribe denied: pluginmgr type=update_request [bus] subscribe denied: skillmgr type=skill_detected 前两个让升级请求静默丢失、职责链交接断掉。内部协作事件必须可订阅 (安全边界在发布侧,订阅只是「听得到」)。 ## 顺带的止血修复 - 回滚判据从 `--version` 改为 digest:旧内核(1.3.x 及以前)没有该 flag,而它恰是最常见的回滚目标 —— 原逻辑让「从旧版升级失败」永久 无法回滚。 - CopyFile 补 fsync(替换 homed 本体时掉电会留半截文件)。 - restore 完整还原才清 marker(原先先清再还原,还原失败就失去第二次 自愈机会)。 - 回滚时移除升级中新增的 depgraph(否则「旧 homed + 新 depgraph」 会因 digest 不匹配崩溃循环)。 - homed-upgrade 不再硬编码 systemctl restart(丢 guard 状态、unit 名 漂移);改用内核已有的 restartCh → exit 42 语义。 - homed-upgrade 不再用 CGO_ENABLED=0 编却依赖 go-sqlite3(stub 下 静默失效,该路径从未工作过);改用 internal/config。 - 服务单元补 User=hologramagent + 三条安装路径补建用户(此前发行物里 完全没有落实降权)。 - rpm 的 fpm 调用补 --after-install(此前 rpm 装完根本不建用户)。 ## 判据 新增 30 条:差分 8(往返/压缩比/错基线/截断/篡改/边界)、通道 8、 执行器 6、事件闸门 8、按主体授权 8。全仓 go test ./... 0 失败。
133 lines
5.9 KiB
Desktop File
133 lines
5.9 KiB
Desktop File
[Unit]
|
||
Description=HologramAgent - 24/7 AI Butler
|
||
Documentation=https://github.com/trueagent/HologramAgent
|
||
After=network-online.target docker.service
|
||
Wants=network-online.target
|
||
|
||
# ★★ 为什么默认就走 guard(父守护)模式(2026-10-17 对齐本机生产实例)
|
||
#
|
||
# guard 是**双进程**架构:
|
||
# homed --role=guard 只负责拉起/看护 worker,自己不初始化内核
|
||
# homed --role=agent worker,跑真正的新版模块化启动(依赖图定序 + BFS 分层)
|
||
#
|
||
# 它带来的能力是单进程模式**没有**的:
|
||
# · 崩溃自愈:worker 崩了 guard 用**当前二进制**重新拉起
|
||
# · 安全模式:连续崩溃超 max_restarts → 自动进 failback 受限启动
|
||
# (只加载 webui/pluginmgr/recoverydiag,跑恢复梯子)
|
||
# · 卡死检测:IPC PING + 心跳 mtime,带启动宽限
|
||
# · 热更新:worker 以 exit 42 优雅退出 → guard 用新二进制重拉,
|
||
# **不经过 systemd**、不计崩遣次数、guard 进程保持存活
|
||
#
|
||
# 为什么不是 drop-in:单文件发行时 drop-in 容易被漏装。写在主单元里,
|
||
# 装完即是生产同款行为。要回退成单进程,只需把 ExecStart 的 --role=guard 去掉。
|
||
|
||
[Service]
|
||
# Type=exec(不是 simple):simple 会在 fork 后**立刻**认为启动完成,
|
||
# 于是 ExecStartPost 与 LoadCredential 的建立可能竞争。exec 会等到
|
||
# exec() 真正成功才继续,凭据目录与 ExecStartPost 的顺序才是确定的。
|
||
Type=exec
|
||
|
||
# ★ 降权运行(2026-10-28 落定)。
|
||
#
|
||
# 此前本单元**没有** User= ⇒ 以 root 运行。生产实例之所以是
|
||
# hologramagent,完全靠现场手工 drop-in —— 也就是说发行物里并没有
|
||
# 落实降权,装到新机器上就是 root。
|
||
#
|
||
# 配套要求:用户与组由包脚本(deb postinst / rpm %pre / install.sh)创建。
|
||
# 缺失时 systemd 会因 User= 不存在而拒绕启动 —— 这是**响亮失败**,
|
||
# 比默默用 root 好。
|
||
#
|
||
# ⚠️ 降权后 `Apply` 无法替换 /usr/bin/homed(属主 root、目录不可写)。
|
||
# 这不是缺陷,是设计:是否允许自替换由 update.Capabilities 判定,
|
||
# deb/rpm 形态一律走包管理器(见 internal/update/install.go)。
|
||
User=hologramagent
|
||
Group=hologramagent
|
||
|
||
# ★ guard 模式(与本机生产实例一致)。
|
||
# 数据目录默认 /var/lib/hologram(Debian 惯例);迁移到别的目录时
|
||
# 必须**同时**改这里和 core.daemon.data_dir —— 两者不等价,见 README。
|
||
ExecStart=/usr/bin/homed --role=guard --data /var/lib/hologram
|
||
|
||
Restart=always
|
||
RestartSec=10
|
||
|
||
# ★ 指数退避(对齐本机 zz-restart-backoff.conf)
|
||
#
|
||
# 为什么需要:RestartSec=10 是**固定**间隔,而 StartLimitBurst/Interval
|
||
# 在这些长驻 agent 上是故意放松的(收紧会让故障单元进入终态 failed、
|
||
# 必须人工 reset-failed —— 与"自愈"相反)。结果就是一个永久坏掉的服务
|
||
# 以固定频率无限重试,systemd 一句都不说。
|
||
# 实测(2026-09-05,llmsproxy):每 5 秒拉一次,连拉 79 分钟 = 836 次重启。
|
||
#
|
||
# RestartSteps 让间隔从 RestartSec 起指数递增到 RestartMaxDelaySec:
|
||
# 崩溃循环会衰减到每 5 分钟一次,服务不会被放弃(病根修好后自己回来),
|
||
# 日志也不再被刷爆。
|
||
RestartSteps=6
|
||
RestartMaxDelaySec=5min
|
||
|
||
# 关机上限 25s(对齐本机 zz-shutdown-timeout.conf)。
|
||
# 实测本服务正常退出 <1s;给 guard 转发 SIGTERM → 等 worker → Kill 留足余量,
|
||
# 而默认 90s 在本机从未用到,属于纯粹的风险敞口。
|
||
TimeoutStopSec=25s
|
||
|
||
# KillMode=control-group(默认):guard 与 worker 同 cgroup,
|
||
# 一起收,双保险不留孤儿进程。
|
||
KillMode=control-group
|
||
|
||
WorkingDirectory=/var/lib/hologram
|
||
StateDirectory=hologram
|
||
Environment=HOME=/root
|
||
# onnx 运行库随包安装(不在 /opt,见 packaging/package-linux.sh)
|
||
Environment=ONNXRUNTIME_DIR=/usr/lib/hologram/onnxruntime
|
||
|
||
# ★ 主密钥生产注入(security/key.go)
|
||
#
|
||
# 优先级:LoadCredential > HOMED_SECURITY_KEY(env) > <datadir>/security.key
|
||
#
|
||
# 为何用 LoadCredential:环境变量对同用户进程**可见**
|
||
# (/proc/<pid>/environ),任何以该用户身份运行的进程都能读到主密钥。
|
||
# LoadCredential 把内容放到 tmpfs($CREDENTIALS_DIRECTORY,仅本进程 0400),
|
||
# 不在磁盘留副本、也不进 environ。
|
||
#
|
||
# 生成密钥(一次性,然后妥善保管):
|
||
# head -c 32 /dev/urandom | xxd -p -c 64 | sudo tee /etc/hologram/security.key
|
||
# sudo chmod 600 /etc/hologram/security.key
|
||
#
|
||
# ⚠ 该文件就是数据的主密钥:**丢了它,已加密的变量全部解不开**(响亮失败,
|
||
# 不会静默重建)。请与备份一并保管。
|
||
#
|
||
# ⚠ 未配置时**不要**留一个空文件 —— 凭据目录一旦被设置而凭据缺失,内核会
|
||
# **拒绝启动**(不静默回退到磁盘密钥)。若要用磁盘密钥路径,删掉下面这行。
|
||
#
|
||
# ⚠ 从旧版(/var/lib/homeagent/security.key,改名前的路径)升级时:
|
||
# 内核支持"磁盘钥 + 内嵌钥"并存解密(AdditionalDecryptKeys),
|
||
# 所以先放 /etc/hologram/security.key 与旧磁盘钥**同一份内容**再启用本行,
|
||
# 否则旧密文解不开。
|
||
LoadCredential=homed_security_key:/etc/hologram/security.key
|
||
|
||
# Security hardening
|
||
NoNewPrivileges=true
|
||
ProtectSystem=strict
|
||
ProtectHome=true
|
||
PrivateTmp=true
|
||
PrivateDevices=false
|
||
DevicePolicy=closed
|
||
# 只允许读主密钥所在目录,其余系统路径保持只读
|
||
ReadOnlyPaths=/etc/hologram
|
||
# 插件需要写自己目录 + 数据目录 + 插件沙箱
|
||
ReadWritePaths=/var/lib/hologram
|
||
# Audio devices
|
||
DeviceAllow=/dev/snd rw
|
||
DeviceAllow=/dev/dsp rw
|
||
|
||
# Resource limits
|
||
LimitNOFILE=65536
|
||
LimitNPROC=256
|
||
# Chinese-CLIP 本身实测约 1.15GB;加文档稠密索引、词向量、插件与
|
||
# ORT arena 后生产实例约 4.5GB。2GB 会在首次全量建索引时被 cgroup OOM。
|
||
MemoryMax=8G
|
||
CPUQuota=100%
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|