feat(systemd,webui,security): 数据目录一致性接线 + 内嵌主密钥注入

## 1. 数据目录一致性(原为死代码)

`internal/systemd/sync.go` 能编译但**从未接线** —— 没有任何地方调用它。

内核有**两处**数据目录来源且不等价:

	-data <dir>            ← systemd 单元 → memory/graph.db、插件数据根
	core.daemon.data_dir   ← 数据库     → memory/text、documents、knowledge

不一致时数据**分裂**:图记忆在 A、文本/知识在 B,检索跨目录、备份会漏。
生产当前两者一致所以没暴露,但 WebUI 允许改 DB 里的值而**无任何提示**。

接线三处:

 · cmd/homed/main.go 启动时检查,不一致则打告警 + 内联修复命令
 · GET /api/v1/system/data-dir 返回诊断(只在真不一致时给 sync_command)
 · 判据 6 条(InSync/Clean/SyncCommand 内联 sed/备份/daemon-reload/drop-in)

★ 只告警**不改**:静默改 systemd 单元比不提示更危险 —— 改错了服务
起不来,而用户不知道是谁改的。命令里已内联 sed 替换,人复制执行即可。

## 2. 内嵌主密钥(方案 C)

build.sh 支持 `HA_EMBEDDED_KEY_HEX`(64 位 hex)经 -ldflags 注入。
从**环境变量**读而不是写进文件:本仓有两个公开远端,64 位 hex 一旦
写进被跟踪的文件就永久留在 git 历史里。

未设时不注入、不报错:开发机与 CI 本来就不该带生产密钥。
设了但格式不对则**中断构建**(`return 1`)—— 静默产出一个没有内嵌钥的
二进制,会让"防丢失"这个目的悄悄失效。

实测(真机):磁盘上**没有** security.key 时,仅靠 embedded 密钥即可
Submit + 二次加载解密成功;日志确认
`密钥来源 embedded (构建期注入,指纹 0db8e9c5)`。

## 判据第一版的两个错误(记下来)

 · 没给 UnitPath 就断言"必须含 sed -i" —— 实际 sed 行只在 UnitPath
   非空时生成,判据误报。
 · 断言 `/` 必须转义 —— 但 sed 分隔符是 `|` 不是 `/`。`/` 无需转义;
   真正必须转义的是 `&`(sed 里表示整个匹配)与 `|`(分隔符)。
This commit is contained in:
JianFeeeee
2026-10-18 13:51:00 +08:00
parent 6a15b80ee9
commit 40de84facc
5 changed files with 276 additions and 1 deletions

View File

@ -15,6 +15,7 @@ import (
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/kbtree"
_ "github.com/JianFeeeee/HomeAgent/internal/plugins/pluginmgr"
"github.com/JianFeeeee/HomeAgent/internal/security"
"github.com/JianFeeeee/HomeAgent/internal/systemd"
// 空白导入内置 provider:它们各自在 init 里注册到 pkg/embedding。
// 想把核心换成自己的模型,只需替换这一行(或另建一个发行版 main)。
@ -204,6 +205,29 @@ func main() {
cfgReg.SetLLMSnapshotFile(filepath.Join(opt.dataDir, "llm_snapshot.json"))
ks.Cfg = cfgReg.ToConfig()
// ★ 数据目录一致性检查(2026-10-18 接线)。
//
// 内核有**两处**数据目录来源,且不等价:
// -data <dir> ← systemd 单元,决定 ks.DataDir
// core.daemon.data_dir ← 数据库,决定 ks.Cfg.Daemon.DataDir
//
// 不一致时数据会**分裂**:图记忆(memory/graph.db)在 -data 那个目录,
// 而文本/知识/文档在 DB 那个目录 ⇒ 检索跨目录、备份会漏。
// 生产当前两者一致,所以从没暴露;但 WebUI 允许改 core.daemon.data_dir
// 而**没有任何提示**,用户改完重启就会发现"数据丢了"。
//
// 这里只告警 + 给出可直接粘贴的修复命令,**不改任何东西**:
// 静默改 systemd 单元比不提示更危险(改错了服务起不来,而用户不知道
// 是谁改的)。修复由人执行 —— 命令里已内联 sed 替换。
if cdir := ks.Cfg.Daemon.DataDir; cdir != "" && cdir != opt.dataDir {
st := systemd.State{RuntimeDataDir: opt.dataDir, ConfigDataDir: cdir}
log.Printf("[homed] ⚠️ 数据目录不一致(数据会分裂):")
log.Printf("[homed] -data = %s(图记忆、插件 Settings.DataDir)", opt.dataDir)
log.Printf("[homed] DB data_dir = %s(文本/知识/文档)", cdir)
log.Printf("[homed] 修复命令(勿手改单元,生产用 drop-in 覆盖 ExecStart):")
log.Printf("[homed] %s", st.SyncCommand(cdir))
}
// 装配全部内核模块(缺依赖/环/重名 ⇒ 这里就报错,不必等运行期)。
//
// ★ 插件调度已**归位到内核模块**(`internal/kernelmod/pluginhost`,