☰
MemHub 第三方依赖许可证治理:THIRD_PARTY_NOTICES.md 的生成机制、内容解读与发布流水线
2026/10/20 1:24:50 网站建设 项目流程

【免费下载链接】Hy-Memory

项目地址:https://gitcode.com/gh_mirrors/hy/Hy-Memory
点击查看免费下载

MemHub(本仓库memhub/目录下的多端内存应用套件)在发布时不仅交付功能代码,还会随包附带一份合规清单:THIRD_PARTY_NOTICES.md逐一记录自身许可证、打包进运行时的第三方依赖及其许可证文本。本文以 THIRD_PARTY_NOTICES.md 为线索,完整解读这份文档的章节结构,并深入其背后由 generate_sbom.py 实现的“从已安装依赖树自动生成、人工禁止手改”的 SBOM/声明体系,说明它如何接入pnpm release:check、运行时打包与公开发布门禁。读完本文,你将理解 MemHub 如何保证“声明与真正随包分发的内容永远一致”,并掌握重新生成、校验与发布该文档的完整操作路径。

一、文档定位:一份“禁止手改”的运行时依赖声明

文档开篇即给出三条关键事实:

  1. MemHub 自身采用 Apache-2.0 许可,参见仓库根目录的 LICENSE(生成脚本会直接读取该文件内容来判断许可证,见下文第五节);
  2. 它随包捆绑(bundles)了下述运行时依赖,因此需要向再分发者完整交代这些第三方组件的归属;
  3. 文档是由python3 scripts/generate_sbom.py在 2026-10-17T13:52:48Z 从“已安装依赖树”生成的,并明确标注“Do not edit by hand”(禁止手工编辑)。

这最后一条是整份文档的设计核心:它刻意不采用人工维护的依赖清单,而是每次从真实的虚拟环境与包管理器锁文件中重新推导,从而避免声明与实际随包内容“悄悄漂移”(generate_sbom.py 的模块 docstring 原文即指出:“Both artifacts are derived from what is actually installed rather than from a hand-maintained list, so they cannot quietly drift away from the shipped app.”)。

文档还预告了一个重要策略:当前阶段不逐包复制许可证原文,license 列在发布审查(release review)时才填充;在此之前,配套的 SBOM 对每个第三方包记录NOASSERTION(不声明),而不是靠猜。这与《第四部分:NOASSERTION 策略》中源码的“拒绝猜测”逻辑完全对应。

二、内容解读:文档的四个核心章节

1. hy-memory 2.0.4(Apache-2.0,腾讯 Tencent)

文档第一段声明主体是hy-memory 2.0.4,许可证为 Apache-2.0,并附完整许可证文本。其版权声明明确指出:

  • 腾讯(Tencent)向社区提供 Hy-Memory;
  • Hy-Memory 在 Apache-2.0 之外不施加任何额外限制;
  • 该声明仅指腾讯依据 Apache-2.0 公开发布的代码、参数与权重。

从源码看,这段文本不是写死的模板:generate_sbom.py的hy_memory_notice()(L56-L91)会定位apps/core/.venv中已安装的hy_memory-2.0.4.dist-info,从其中的METADATA读取 Name/Version/License-Expression,并读取licenses/LICENSE文件得到版权行与正文;任何一项缺失或不匹配(例如版本不是 2.0.4、许可证不是 Apache-2.0)都会抛出SbomError使生成失败(fail closed)。测试 test_generate_sbom.py 也专门验证了“从 dist-info 取声明”与“缺失 dist-info 必须拒绝”两个行为。

2. MemTensor memmy-agent(adapted,MIT)

文档第二段声明:MemTensor memmy-agent的适配版本,上游固定提交(pinned commit)为25acd5f29fc7df73b943713abe61f1b2f9839887,许可证为 MIT,版权归 MemTensor(2026-present),并附完整 MIT 许可证文本。

随后文档明确划出适配边界(Adaptation boundary):

packages/host-bridge/src/memmy-adapter/claude-code/是 Memmy Claude 集成的源码级适配,为符合 One Memory 的授权与存储契约而被大幅重写;完整的上游/适配边界记录在该包的PROVENANCE.md中。且不随包分发任何 Memmy 品牌、图标、字体或吉祥物资产。

对应仓库实现事实:

  • 适配代码位于 memmy-adapter/claude-code/(含target.ts、install-plan.ts、hook-template.ts、event-adapter.ts、runtime.ts、session-state.ts、recall-trigger.ts等模块);
  • 上游许可原文保留在 LICENSE.memmy,并由license.ts在候选安装中嵌入同一份声明;
  • 详细的上游/适配逐模块对照表(如把上游的“子串删除+目录替换”改为“精确属主条目、预览指纹、冲突拒绝与可逆日志”)记录在 PROVENANCE.md;
  • 生成脚本通过MEMMY_PIN = "25acd5f29fc7df73b943713abe61f1b2f9839887"(L37)固定该提交,并在 SPDX 中为它单独建立SPDXRef-Package-memmy-agent-derived条目,声明 MIT、filesAnalyzed: True,通过CONTAINS关系挂到主包下。

3. JavaScript (npm) — 10 个包

文档以“ecosystem — 数量”作为小节标题,完整列出 10 个运行时 npm 依赖(版本号精确锁定):

包名版本包名版本
@tanstack/react-query5.103.2axios1.20.0
fflate0.8.3https-proxy-agent7.0.0
lucide-react1.43.0proxy-from-env2.1.0
react19.2.8react-dom19.2.8
tar7.5.22three0.185.1

从这组清单可以读出 MemHub 桌面端/Web 端的技术栈轮廓:React 19 渲染层、@tanstack/react-query 数据请求、three 用于三维可视化、axios + https-proxy-agent 处理网络代理、tar 用于归档处理。生成侧对应javascript_packages()(L112-L131):它执行pnpm list --prod --depth 0 -r --json获取生产依赖,并主动跳过link:前缀的 workspace 内部链接包,因此不会把 Monorepo 自己的包误报成第三方。

4. Python (PyPI) — 126 个包

文档列出的 PyPI 运行时依赖共 126 个,对应python_packages()(L134-L161)——它用uv pip list --python apps/core/.venv/bin/python --format json读取受管虚拟环境中的全部包,并剔除one-memory与hy-memory两个自有包后按名称排序。按功能可归类如下(版本均以文档原文为准):

  • 向量检索与图数据库:chromadb 1.5.9、kuzu 0.11.3(对应 Hy-Memory 的向量存储与图存储后端);
  • 机器学习/Embedding 运行时:numpy、scipy、scikit-learn、onnxruntime 1.23.2、tokenizers 0.23.1、tiktoken 0.14.0、huggingface-hub 1.29.0、hf-xet 1.6.0、mmh3 5.3.0、joblib、threadpoolctl;
  • 中文分词与检索:jieba 0.42.1、rank-bm25 0.2.2、langdetect 1.0.9、regex 2026.8.31;
  • Web/API 服务层:fastapi 0.141.1、starlette、uvicorn 0.52.4、uvloop、httpx、httpcore、h11、httptools、openai 2.54.0、websockets 17.1、websocket-client、aiohttp 3.14.3及其配套(aiohappyeyeballs、aiosignal、multidict、yarl、frozenlist、propcache);
  • 数据库驱动:aiomysql 0.3.2、pymysql 1.2.3、pypika 0.51.1;
  • 序列化/校验:orjson、json-repair、protobuf 7.36.0、flatbuffers、pyyaml、pydantic 2.13.5、pydantic-core、pydantic-settings、jsonschema、referencing、rpds-py、attrs、six、annotated-types;
  • 可观测性(OpenTelemetry):opentelemetry-api、opentelemetry-sdk、opentelemetry-proto、opentelemetry-exporter-otlp-proto-common、opentelemetry-exporter-otlp-proto-grpc、opentelemetry-semantic-conventions(均为 1.44.0 / 0.65b0);
  • CLI/日志/工具:click、typer、rich、coloredlogs、humanfriendly、pygments、markdown-it-py、python-dotenv、keyring、oauthlib、requests-oauthlib、kubernetes 36.0.3、distro、shellingham;
  • 打包与分发:pyinstaller 6.22.2、pyinstaller-hooks-contrib、altgraph、macholib、setuptools 84.0.0、build、pyproject-hooks;
  • 测试与质量:pytest 9.1.1、pytest-xdist 3.8.0、execnet、mypy 2.3.1、ruff 0.16.5、pluggy、iniconfig、pathspec;
  • 其他工具链:tenacity 9.1.4、tqdm、zstandard、pybase64、bcrypt 5.0.0、grpcio 1.83.1、googleapis-common-protos、requests 2.34.2、urllib3 2.7.0、certifi、charset-normalizer、idna、filelock、fsspec、importlib-resources、more-itertools、jaraco-*系列、narwhals、overrides、typing-extensions、typing-inspection、jiter、watchfiles、mpmath、sympy、durationpy、cloudpickle、anyio、sniffio等。

完整的 126 项逐条清单请直接查看 THIRD_PARTY_NOTICES.md 原文。

三、生成机制:generate_sbom.py如何从“已安装依赖树”推导两份工件

memhub/scripts/generate_sbom.py是整份声明的唯一事实来源,一次运行同时产出两个文件(main()):

  • SBOM.spdx.json(SPDX-2.3 格式的软件物料清单);
  • THIRD_PARTY_NOTICES.md(本文主题文档)。

生成流水线如下:

  1. 枚举 JS 生产依赖:pnpm list --prod --depth 0 -r --json(工作区根目录执行),跳过link:workspace 内部包;
  2. 枚举 Python 依赖:uv pip list --python apps/core/.venv/bin/python --format json,剔除one-memory与hy-memory;
  3. 读取 hy-memory 的已安装 wheel:从.venv的hy_memory-2.0.4.dist-info读METADATA与licenses/LICENSE,不猜测许可证;
  4. 判定项目自身许可证:project_license()(L176-L190)直接读取 LICENSE 文本,若包含 “Apache License / Version 2.0, January 2004” 则判定 Apache-2.0,若为 MIT 文本则判定 MIT,否则报错——文档中“命名一个仓库并未携带的许可证,是 SBOM 绝不能犯的错误”正是这段代码的注释;
  5. 拼装 SPDX:每个第三方包获得SPDXRef-Package-{ecosystem}-{name}唯一 ID 与pkg:npm/...或pkg:pypi/...的 purl 外部引用,主包通过DEPENDS_ON关系指向所有运行时依赖,通过CONTAINS指向 memmy 适配模块;
  6. 两份工件都成功后才落盘:若任一步失败,先前的文件不会被覆盖,避免给发布门禁留下“半更新”的 SBOM。

重生成命令(在memhub/目录下)即 package.json 中定义的脚本(package.json):

pnpm sbom # 等价于 apps/core/.venv/bin/python scripts/generate_sbom.py

前置条件:apps/core/.venv已通过uv sync建立、pnpm依赖已安装、Python 版本 ≥ 3.10(脚本入口有显式版本守卫,见 L12-L16)。

四、NOASSERTION 策略与项目级许可证的防猜机制

THIRD_PARTY_NOTICES.md明言:“per-package license texts are not reproduced here yet; the license column is filled in as part of release review, and the SBOM recordsNOASSERTIONuntil then rather than guessing.”

对应的 SPDX 构建逻辑在 build_spdx() 中清晰分层:

  • MemHub 自身(one-memory):licenseConcluded/licenseDeclared来自project_license()的真实读取结果,copyrightText为NOASSERTION;
  • 126 个 PyPI + 10 个 npm 第三方包:licenseConcluded、licenseDeclared、copyrightText一律NOASSERTION,代码注释解释这是“解析每个上游许可证文本是独立、需审查的步骤,在此处声明任何许可证都等于猜测”;
  • hy-memory:许可证与版权来自已安装 wheel 的真实元数据(Apache-2.0);
  • memmy 适配模块:MIT 与版权来自固定提交与LICENSE.memmy。

这套“宁可写 NOASSERTION 也不猜”的策略,将许可证审查刻意保留在发布审查(release review)这个人工环节,避免自动化工具在缺少证据时擅自给出错误的合规结论。

五、发布流水线中的硬门禁:没有声明文件就拒绝发布

THIRD_PARTY_NOTICES.md不是装饰性文档,它被发布检查、运行时打包、公开发布三个环节强制引用:

  1. 发布就绪检查(pnpm release:check):verify_release_ready.py将LICENSE、NOTICE、THIRD_PARTY_NOTICES.md、SBOM.spdx.json四者列为REQUIRED_RELEASE_FILES(L8-L13),任一缺失即输出missing release file: ...并以非零码退出;同时还会拒绝“许可证仍为 pending”的LICENSE-PENDING.md占位文件,并检查 Core 的uv.lock不再依赖腾讯内部 Python 源,防止内部依赖泄漏到公开包。

  2. 运行时打包:build_runtime_distribution.cjs(L397-L412)把SBOM.spdx.json、THIRD_PARTY_NOTICES.md直接复制进产物根目录,并把LICENSE→licenses/LICENSE.one-memory、LICENSE.memmy→licenses/LICENSE.memmy、PROVENANCE.md→licenses/PROVENANCE.memmy.md一起随 Runtime 分发;随后在必产物清单中逐一断言这些文件存在(L429-L449)。也就是说,用户拿到的每个 Runtime 包里都物理携带这三份声明。

  3. 公开发布清单:build-release-assets.mjs(L159-L179)将SBOM.spdx.json作为role: sbom(spdx-json)、THIRD_PARTY_NOTICES.md作为role: notices(markdown)的发布元数据工件登记,计算 sha256 并对外公布;文件缺失、为空或为符号链接都会抛出release_metadata_invalid。

  4. 公开发布门禁二次校验:verify_public_release_ready.py(L254-L267)还会重新解析SBOM.spdx.json:JSON 无法解析则记录sbom_invalid阻断项;若包名集合中出现hy-memory-internal这类内部依赖则记录sbom_contains_internal_dependency阻断项——确保公开清单里绝不出现内部包名。

  5. README 官方指引:仓库 README.md 的许可证一节同样指向 THIRD_PARTY_NOTICES.md 与 SBOM.spdx.json,作为第三方归属与许可证的唯一权威出处。

六、可复现性与测试保障

THIRD_PARTY_NOTICES.md头部的生成时间戳2026-10-17T13:52:48Z默认取自datetime.now(timezone.utc),但生成脚本支持环境变量SOURCE_DATE_EPOCH(L356-L369):设置后将从该 Unix 秒数换算 UTC 时间,从而支持可复现的发布构建(reproducible build);值为负数或非法时生成直接失败。对应测试test_source_date_epoch_drives_reproducible_release_timestamp验证了该行为(test_generate_sbom.py)。

围绕生成器,test_generate_sbom.py 提供四组回归测试:

  • test_hy_memory_notice_comes_from_the_installed_dist_info:构造临时hy_memory-2.0.4.dist-info,验证声明名称/版本/许可证/版权均来自其中真实元数据;
  • test_hy_memory_notice_refuses_missing_dist_info:dist-info 缺失时必须抛SbomError(fail closed);
  • test_script_has_a_modern_python_guard_before_type_annotations:保证 Python 3.10 版本守卫先于Path | None等新语法出现,避免旧解释器在解析阶段就崩溃;
  • test_source_date_epoch_drives_reproducible_release_timestamp:验证时间戳可复现路径。

此外,构建侧测试(如 build_runtime_distribution.test.cjs)断言打包产物必然包含SBOM.spdx.json与THIRD_PARTY_NOTICES.md,build-release-assets.test.mjs 验证发布清单中两份文件的 sha256 与内容一致性。

七、对开发者与使用者的实践建议

  • 作为再分发者:若你基于 MemHub 构建衍生发行版,Apache-2.0 要求保留版权与归属声明。请沿用THIRD_PARTY_NOTICES.md+SBOM.spdx.json+licenses/目录的完整随包结构,尤其不要遗漏LICENSE.memmy与PROVENANCE.memmy.md,因为 memmy 适配模块的 MIT 归属与适配边界声明都在其中。
  • 作为 MemHub 维护者:任何依赖变更后应运行pnpm sbom重新生成两份工件,再执行pnpm release:check验证发布就绪;生成前的uv sync与pnpm install是必要前置步骤,因为脚本坚持从真实安装树而非手工清单推导。
  • 作为审查者:第三方包的NOASSERTION条目应在发布审查时逐包填充许可证结论,但禁止在填充前以猜测方式把NOASSERTION替换成具体许可证——这是生成器刻意保留的人工把关点。

综上,THIRD_PARTY_NOTICES.md 表面上是一份法务声明,实质上是一套由 generate_sbom.py 驱动、被发布检查与打包脚本强制执行的“依赖归属可追溯”工程机制:声明永远来自真实安装树,未知信息宁可留白(NOASSERTION)也不猜测,最终让随包分发的每一份许可证文本都有源码级出处可查。

【免费下载链接】Hy-Memory

项目地址:https://gitcode.com/gh_mirrors/hy/Hy-Memory
点击查看免费下载

相关推荐

上一篇:create-mf-app完全指南:从安装到部署的完整流程
下一篇:从 PyTorch 到 MLX:moge-3-vitg-mlx-fp32 是如何用 mlx-vlm 转换的?模型迁移实战详解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询