Skip to content

v5.8.0 — 记忆网络:几台机器接成一个记忆体

Choose a tag to compare

@MirHuang666 MirHuang666 released this 10 Nov 07:19
· 108 commits to main since this release

本页会随勘误就地更新。规范全文以 main 为准。

这一版只回答一个问题

两台机器,怎样才算「同一个我的记忆」?

不是把"人格"搬过去,是把文件搬过去:设定、会话原文、索引、交接包都是文件。所以这一版做的不是"迁移身份",是给记忆加一根管子——指定一台机器当服务端,其余设备通过虚拟组网连上来,按清单做增量同步。

设计思想(为什么这么设计)

  1. 记忆是文件,搬家就是搬文件。 不需要任何"人格编解码",也不需要模型参与;同步失败最多是没同步上,不会坏掉记忆本身。
  2. 真相只有一个。 服务端那份清单就是真相;客户端只按清单拉,不合并、不双写、不反向覆盖。两台机器同时写同一个文件这种问题,不在这版的职责里——那是同目录并发写的问题。
  3. 落下之前先落盘,落下时原子换。 写盘一律「临时文件 → fsync → os.replace」,任何时刻被杀掉,磁盘上都不会出现写了一半的文件。
  4. 默认不对外。 不显式指定就只听 127.0.0.1;取不到令牌服务端拒绝启动,没有"默认无鉴权"这一档。
  5. 只给白名单。 index/、archive/、续接/;..、绝对路径、盘符一律 400。

和网盘是两种模式,别混为一谈

网盘模式(persona_cloud_bus) 节点直连(本版)
解决什么 多个程序共用一个目录时怎么写不坏 多台机器之间怎么把文件搬过去
传输谁干 外包给网盘 自己走 HTTP/1.1(纯标准库)
真相在哪 本地那个目录 服务端那份清单
用在哪 一台机器 / 一个目录 两到几台机器,经 ZeroTier、Tailscale 之类连成同网段
是否互相替代 不替代,可以同时开 不替代,可以同时开

一句话:网盘管"同一个目录里的并发写",本版管"机器与机器之间的增量"。把两者混成一个"同步",后面就会互相踩。

一条同步从头到尾

客户端                                          服务端(只有一台)
  │                                                 │
  ├─ GET /health ────────────────────────────────▶ │  探活,无需令牌,不含路径
  │                                                 │
  ├─ GET /manifest(Bearer 令牌)───────────────▶ │  白名单目录的 {path,size,sha256,mtime}
  │                                                 │
  ├─ 本地比对 sha256:已一致的跳过                            │
  │                                                 │
  ├─ GET /blob?path=…(Bearer)────────────────▶ │  原始字节,响应头 X-SHA256
  │                                                 │
  ├─ 落 ops/lanbus/ 暂存 → 校验 sha256 → os.replace ─▶ │  只在白名单目录落地
  │                                                 │
  └─ (需要回传时)POST /blob?path=…(Bearer)──▶ │  落盘前再校验一次 X-SHA256,默认 64MB 上限

滚动、重试、跳过都在客户端;服务端只做三件事:报清单、给字节、收字节。服务端不主动推、不删任何文件。

要实现什么 → 对应机制

目标 机制
没网盘也能把记忆搬到另一台机器 清单 + 按 sha256 增量拉取
不让人一不小心把库暴露到公网 --bind 默认 127.0.0.1;启动打印本机 IPv4 并提示「只在虚拟组网网段暴露」
没有令牌就不存在"裸服务" 令牌取不到 → 拒绝启动;hmac.compare_digest 比对,不做长度泄露的字符串比较
不许被路径穿越 白名单三目录;..、绝对路径、盘符/冒号 → 400
杀进程也不会留半个文件 临时文件 → fsync → os.replace
日志里能分清是谁 所有响应带 X-Node: <主机名>

细节(按职责读)

  • 鉴权:环境变量 PERSONA_LAN_TOKEN 优先,其次读本机的令牌文件;服务端启动横幅只打印"令牌来源",不打印令牌内容。
  • 权限边界:可写的只有 index/(同步产物)与拉取暂存 ops/lanbus/;archive/、续接/ 只读。
  • 拉取的落地顺序:先写暂存 → 校验哈希 → 再原子换进目标目录;校验不过就留在暂存并记一条拒收残片,不动正式文件。
  • 可观测:/health 返回节点名、协议版本与可同步文件数(不含完整路径,避免泄露本机目录结构)。

证据(2026-11-10 本机真跑,不是"已实现")

GET /health
  → {"ok":true,"node":"4590-PC","version":1,"root":"lanbus-test","files":2}

GET /manifest(带令牌)
  → 2 个文件:index/a.md(17 字节,sha256 32afc26f…)、续接/b.md(11 字节,sha256 16d0dca6…)

GET /manifest(不带令牌)
  → 401

persona_lanbus.py sync --server http://127.0.0.1:8747 --dry-run
  → 服务端 http://127.0.0.1:8747(节点 4590-PC)
  → 清单 2 个文件(白名单 index/ archive/ 续接/)
  → 比对:需要拉取 0 个,已一致 2 个
  → [dry-run] 只打印计划:不下载、不写 ops\lanbus.json

启动横幅实际打印:令牌来源(内容不打印)、绑定 127.0.0.1:8747(默认安全档,仅本机)、本机 IPv4(127.0.0.1 loopback / 192.168.1.14 网卡)、以及「请只在虚拟组网网段暴露本服务」。

边界(没做的部分)

  • 没有托管:没有"中心记忆服务",服务端就是其中一台机器,关机就同步不了。
  • 没有第三台中继:两两直连,不做中转。
  • 不自动合并冲突:同一个文件两边都改过,本版只按清单判断"谁缺、谁旧",不做语义合并。
  • 不做传输加密:加密交给虚拟组网(WireGuard 那些);本服务自身只做鉴权与完整性校验。
  • 没有界面:只有 serve / sync / token 三个动作;目前也没有接进守护与托盘,不设置就不会自动跑。

修订记录

  • 2026-11-10:首次发布——实现入库(此前只存在于作者本机的库根,仓库里只有规范描述)+ 本页说明。
  • 编号说明:按人的要求取 5.8(v5.7.0 之后的下一个 MINOR),发布在 v6.0.0 之后,属 RELEASING §三 的例外,已在 CHANGELOG 记明。