chore(task): archive 10-04-slim-distribution

This commit is contained in:
Antigravity committed 2026-10-05 00:20:57 +00:00
1 parent 4fda7c8c1a
commit a2e0436ed4
8 files changed
+6 -5

No files matched your search

@@ -0,0 +1,159 @@
# Research: Lib 按需准备与精确锁恢复
- Query: 追踪 Lib、Worker、Package 的准备/离线消费路径,设计 `dever lib install <root>` 与扩展管理器接点。
- Scope: internal;仅源码阅读,无测试、构建、下载、Git 操作。
- Date: 2026-10-04
## Findings
### 归属与已读规范
实际研究仓库为 `/data/project/dever`,独立 Rust 语言编译器,不是 `/data/project/shemic` 的 Go 应用。已读仓库 `AGENTS.md`、`.trellis/workflow.md`、当前 `prd.md`、backend index 与 toolchain-and-library 中目标、生态、锁、缓存和 Worker 合同,以及仓库 `skills/SKILL.md`。全局 shemic-dever/framework reference 描述 Go 项目,不能覆盖本仓库事实。
`task.py current --source` 返回 none;输出目录由主代理明确指定,未建立/修改会话指针。
### Files found
| 文件 | 作用 |
| --- | --- |
| `crates/dever-cli/src/libs.rs` | 锁结构、显式命令、已安装 runtime 读取、校验与离线嵌入 |
| `crates/dever-cli/src/libs/mutation.rs` | 稳定 inode 的项目依赖互斥锁 |
| `crates/dever-cli/src/libs/registry.rs` | 官方源安全传输、PyPI/npm/Go 解析及产物缓存发布 |
| `crates/dever-cli/src/libs/registry/npm.rs` | 独立 npm 环境、精确实例图、归档展开与需构建判断 |
| `crates/dever-cli/src/libs/registry/sumdb.rs` | Go 已锁透明日志证据与 ZIP 校验 |
| `crates/dever-cli/src/libs/build.rs` | 构建 Inputs、Session、完整 Receipt、离线收据校验 |
| `crates/dever-cli/src/libs/build/python.rs` | sdist 解包、PEP517 build 依赖解析与隔离执行 |
| `crates/dever-cli/src/libs/build/npm.rs` | npm 生命周期执行、optional 剪枝、最终安装归档 |
| `crates/dever-cli/src/libs/build/npm/environment.rs` | Node/Python/native 工具与 sandbox 环境 |
| `crates/dever-cli/src/workers/runtime.rs` | maker/consumer 共享 runtime descriptor 校验 |
| `crates/dever-cli/src/workers/go_build.rs` | 已准备 host Go 工具编译 target Worker |
| `crates/dever-cli/src/packages.rs` | Package 解析、精确 ZIP、源码/Worker/Lib 汇入公共锁 |
| `crates/dever-cli/src/toolchain/service.rs` | daemon ArtifactGet/Put IPC |
| `crates/dever-cli/src/toolchain/launcher.rs`、`src/main.rs` | 选版本与公共参数路由 |
### 现有调用链与准备入口
1. `libs::execute_for_target` (`libs.rs:1870`) 在 add/update 中先读声明、`source_workers`、Package libs,再 `resolve_project`,最后写 setting/lock。不能将 install 实现为 add/update 别名。
2. `source_workers_with_packages` (`libs.rs:2173`) 加载源码、check,再 `bound_program_workers` (`1236`)。后者立即调用 `InstalledRegistry::runtime` 绑定每个非 exec Worker 的 runtime。因此只在 `resolve_project` (`612`) 加 ensure 太晚:全新精简安装会先失败。无 Lib 的 managed Worker 也需要 runtime。
3. `resolve_project` 为实际生态调用 `InstalledRegistry::runtime` (`648`),创建 `HttpRegistry::official`、sumdb、`build::Session`,交给 resolver。fixture 全 exec 分支完全离线,不应强制要求 managed 安装。
4. `packages::execute_locked` (`1109`) 的 add/update 解析 Package closure,然后 `source_workers_with_packages` (`1173 附近`) 和 `resolve_project` (`1193 附近`)。Package 的传递 Lib 与 Worker 必须享有同一显式准备能力;remove 则 `retain_locked_libs`,不得顺便安装扩展。
5. `prepare` (`libs.rs:1139`) 名称易误导:它只校验声明是否在锁中并统计大小,不下载、不校验缓存完整性。
6. `embedded_resources_for_target` (`1268`) 在离线路径核对 runtime identity、目标、每个归档摘要/长度和 Go sumdb,再把字节作为资源。`prepare_resources` (`1418`) 追加目标 sandbox,Go 另外需要 host sandbox,随后 `workers::prepare_for_target`。
7. `workers/go_build.rs:61` 的 `compile_worker` 是离线构建;Go 编译器/链接器/selector/target stdlib 目前在 Go **runtime pack** 中,非 `build/go` 原生扩展包。保持 host 工具 + target 输出区分。
### 扩展管理器最小 API 接点
主代理提出使用 daemon 可写的不可变 `cache/extensions`,catalog 绑定已签名核心。Lib 层可保留当前逻辑路径,无需另一份生态业务实现:
- `ensure_extension(version, kind, target)`:仅显式准备阶段调用;kind 至少 runtime(pip/npm/go)、build(pip/npm)、target。目标 extension 包含 native runtime/target sandbox;生态 target runtime 单独选取。
- `read_verified_extension_artifact(version, kind, target, relative)` 与有界 artifact listing:只读验证已安装 extension,返回缺资源的准确准备命令。`InstalledRegistry::runtime` (`791`)、`build::Inputs::tools` (`726`)、`sandbox_resources` (`839`) 共用这个验证 owner,避免保留各自手写签名读取。当前 `signed_artifact` (`817`) 仅从 core manifest.artifacts 查叶子,需切换到合并后的已验证资源读取。
- 显式 preparation wrapper 实现 `build::Inputs`,runtime/tools 可先 ensure 再读。普通 InstalledRegistry 永远只读;或者使用显式、不可隐式默认的 preparation policy,并沿 worker 绑定路径传递。
- `source_workers_with_packages` 保持只读供 doctor/remove/run/build;新增同一底层源码扫描的显式准备入口,先收集未绑定 Worker,再 ensure、绑定。Package add/update 必须走该入口。不要复制 SourceMap/check 逻辑。
- npm build environment 自身请求辅助 Python (`build/npm/environment.rs:57`);按需准备需覆盖此调用,不能只看顶层 npm 声明。
- cross target 的本机 runtime、sandbox 与目标资源由类型化 target 选择,不能全局修改 host identity。
### 何时真的需要 build extension
- Python:优先兼容 wheel;只有进入 `RegistryResolver::python_source_candidate` (`registry.rs:933`) 的 sdist 分支才调用 Session.python。`Environment::new` (`build/python.rs:239`) 读取 build/pip;提前为每个 pip 根都安装 build/pip 不合理。
- npm:`Session::npm` (`build/npm.rs:15`) 先展开户籍归档,`npm::requires_build` 为 false 在第 43 行前后返回。只有确需生命周期/native 检查才创建 Environment,读取 build/npm。
- 当前 npm 的任何需执行 hook 路径都加载 Python + native build pack;当前 Python 的任何 sdist 也加载完整 build/pip。若 PRD 的“确有必要”要求进一步区分纯 JS hook / 纯 Python sdist 与 native 编译,需要收紧现有 environment 合同,而不仅加 lazy ensure;不能声称已做到逐个原生编译器按需。
### 当前锁记录什么、未记录什么
| 记录 | 源码依据 | 能力/缺口 |
| --- | --- | --- |
| LockedArtifact | `libs.rs:238` | target/path/bytes/sha256;path 是生成的逻辑路径,不是下载 URL |
| RuntimePack | `libs.rs:247` | name/version/sha256;运行时元数据从已签名版本读取 |
| LockedLib | `libs.rs:255` | 精确 spec、依赖、runtime、artifacts、schema、build receipt identity |
| LockFile | `libs.rs:267` | libs/workers/go_sumdb/npm/builds/packages;decode 要求规范字节与当前 TOOLCHAIN (`353`) |
| npm Environment | `registry/npm.rs:73` | 每个 Worker 独立 roots、实例路径、owner archive/prefix、edges、omission、build id |
| build Receipt | `build.rs:52` | roots/sources/graph/runtime/aux runtime/tools/frontend/Python backend/dynamic deps/递归依赖锁/output |
| LockedPackage | `packages.rs:75` | name/version/zip sha256/bytes/精确依赖/manifest sha256;来源在 setting 中已有 registry |
普通 PyPI wheel filename/URL 在解析后丢失(`registry.rs:905`);npm dist URL/SRI 未持久化(`988`);Go ZIP 地址可由已锁 spec 按现有 go_path 规则导出(`1121` 附近)。不改锁格式的恢复可查询**已锁精确版本**的 metadata 并仅选匹配锁 SHA/长度的文件,绝不重新选择版本或依赖;若文件撤下或变字节,明确失败。下载仍通过现有官方源/redirect/长度策略。
构建产物不是远端 registry 文件,必须优先从共享 cache 恢复;缺失时用固定 receipt 重放并比对锁定输出 SHA/长度,不能把新输出写回锁。
### 锁恢复的完整最小流程
1. install 参数禁止 specs,保留明确 target 选择。读且保存原始 setting/lock 字节;无锁立即报错;`LockFile::decode` + `doctor` 校验。将 install 纳入 `ProjectMutation::for_command` 的稳定互斥锁 (`mutation.rs:13`),不调用 setting 或 lock writer。
2. Package 必须先恢复:从已有 registry 的 `/v1/packages/<name>/<version>.zip`(`packages.rs:467` 的现有格式)取**锁定** ZIP,以 lock 摘要/长度验证并发布共享 artifact cache;不读版本索引、不选版本。然后复用 `owned_files_with` (`726`)、`validate_locked_roots` (`811`) 核对 manifests、依赖、源码归属和本地冲突。
3. 收集 config Lib、Package manifest Lib、源码 Worker 合同,验证锁 roots/closure;现有 `prepare` 只查声明包含性,不拒绝无主锁 libs。可借 `retain_locked_libs` (`2072`) 的闭包投影与原锁比较(不发布),同时核对 workers;注意 npm receipt 的原始图与 remove 后视图允许不同,不误判合法离线 remove 结果。
4. 仅为锁中实际 runtime 及 Worker ensure 对应版本/target 扩展,然后比对 RuntimePack;目标错配先报错,不替用户切换锁。对 fixture 条目复用内置字节与完整 metadata 校验。
5. 实现 `restore_locked` 的精确归档恢复,接受 `RegistryTransport`、`ArtifactStore`、build Inputs,便于 fixture 注入。按 SHA/bytes 查缓存,真实 miss 才取精确版本 metadata/归档。Go 使用锁中 `go_sumdb` 验 ZIP,不调用新的 MVS/checkpoint 解析。
6. 对缺失的构建产物递归恢复 receipt.sources 和 receipt.dependencies;验证 frontend、tools、runtime identity;通过同一个 sandbox/environment 实现固定重放。Python 重放使用锁定 backend/static+dynamic 依赖,不调用 `build_requirements`;npm 使用锁定实例图与已记录 optional 结果,不再进行 semver 解析。
7. 恢复后复用 `embedded_resources_for_target` 验最终产物,确认 setting/lock 原字节未变。任何失败保留已存在可用数据;新增正确 cache 项可留作重试复用。
### 必须正视的重放缺口
- `Session::python` (`build/python.rs:37`) 在 73/94 行调用 resolver.build_requirements,直接重用会重新解析,违反 install 合同。应提取固定依赖的执行阶段,供首次解析和锁重放共用。
- Python receipt source 被写成 `build/source/<hash>/source` (`python.rs:144`),没有 filename/格式;`source_files` (`196`) 需要 `.tar.gz` 或 `.zip`。可用 receipt 根的精确 PyPI metadata 按 source hash 找回文件名,但若 metadata 不再包含该源,必须报缺失。
- npm `sources` 在 optional 构建失败剪枝**前**采集 (`build/npm.rs:46`),receipt.graph 在 `prune_failed` **后**保存 (`72/87`)。失败节点 spec/owner 可能已丢,sources 只有散列路径,故当前 receipt 不保证可以完整重放原环境;不能凭摘要路径猜原包名。
- 若需要对新生成锁完整恢复,应在首次解析时保存 source locator/filename 与 npm 原始执行图(不同于最终消费图),并在 doctor/identity 中校验。不要添加旧格式兼容分支;按仓库“新语言直接迁移合同”规则迁移 fixtures/callers。已有锁缺最终产物且缺恢复信息时,明确说明不可恢复,不能静默 resolve。
- 即使输入齐全,任意第三方 hook/wheel 打包不保证字节确定性。重放输出必须匹配原 SHA,否则明确失败;没有远端保存原产物的机制就不能保证所有第三方构建永远能在全新机器复现。PRD 的精确恢复应以这一合同验证,不能以“版本相同”代替“字节相同”。
- `ManagedArtifactStore::get_exact` (`libs.rs:893`) 调 `artifact_get(...).map(Some)`;当前 miss、损坏、IPC 失败都 Err。`service.rs:327/515` 未提供 typed miss。必须增加明确 absence 结果或专用有界 existence/read 协议,不能 `Err => 下载`、不能匹配错误文本。缓存损坏和 service 不可达应继续失败。
### target add 与 CLI
`main.rs:435` 的 Lib operation 白名单和两个 USAGE 都需 install;launcher `project_root` (`launcher.rs:174`) 已识别任意 lib operation,可原样按项目版本转发。
`dever target add linux-aarch64` 没有 root 参数,应在 launcher dispatch 使用当前选定的 active 版本并调用 extension owner;若期望 cwd pin,则需要与主设计明确一致,不能悄悄扩大既有 project_root 行为。它仅准备 target runtime/sandbox;第三方 ARM Lib 仍 `lib ... --target linux-aarch64`。
Python sdist/npm hook 在非 host target 上由 `build::require_execution_target` (`build.rs:13`) 明确拒绝,需要匹配隔离执行环境或完整目标产物。target add 不能解除这个约束。Go runtime 则支持 host tools + target stdlib。
### 最近的测试夹具(本次未执行)
- `test/dever-cli-tests/external_libs.rs:331/400/418`:LocalRegistry、FixtureArtifactStore、三生态精确归档;`:451` 目标选择;`:1333` 离线确定性;`:1397` setting 不变;`:1469` 错配配置;`:1508` Worker 合同;`:1649` 无 Lib Worker。
- `test/dever-cli-tests/external_libs/build.rs:241`:不兼容 sdist 在装工具前拒绝;`:260/359` backend fixture / PEP517 hooks;`:422` 官方 sdist 为显式重测试,不默认运行。
- `test/dever-cli-tests/external_libs/npm_build.rs:68`:完整固定 receipt;`:264` 不重跑 hooks 的锁图投影;`:421` optional 失败/bin/offline remove;可增加记录请求次数的精确 install 用例。
- `test/dever-cli-tests/packages.rs:121/174`:注入 transport/store、传递 Package→Lib;`:621` 移除不请求 registry;`:725` 原 setting 字节回滚;`:909` signed managed 全链路(重验收,不默认执行)。
- 新定向验收应覆盖冷 cache install 后锁字节不变、metadata 新增更高版本仍不选、Package→managed Worker、空 specs/无锁/声明漂移、typed miss 与损坏差异、固定 Python build deps、npm optional 原执行图、输出摘要不一致失败、ARM mismatch、已缓存构建输出不加载 build extension,以及普通 run/build 绝不发 ensure/download。
### 建议定稿的数据合同与实施所有权
当前源码实际是 `LOCK_FORMAT = "dever-lock-v5"` (`libs.rs:25`),部分 spec 文案仍写 v2;本次直接升级 **dever-lock-v6**,无旧格式 fallback。建议单一、最小模式:
```text
LockedArtifact += source: Option<RegistrySource>
RegistrySource = { ecosystem: Ecosystem, locator: String, filename: String }
Receipt.dependencies -> Receipt.inputs: Box<LockFile>
```
`source` 对 registry 原始归档必填,对最终本地构建 output/fixture 可为 None;locator 是受现有 RegistryTransport 官方 origin/path 策略约束的精确地址,filename 只允许单文件名并承担解包格式选择,不从 URL query 猜格式。SHA/长度/target 沿用同一 LockedArtifact,不引入第二份内容身份。既然只接受 v6,source 必须成为已知字段,不需要兼容性 serde default;可序列化为 null。receipt 没有独立外部文件,**锁 v6 就是其 schema 版本**,不要再加无独立生命周期的 receipt 格式版本。
`inputs` 复用现有 LockFile:Python 为固定 build requirements 的精确递归锁;npm 为剪枝前 `libraries + npm graph`,图中 build=None,inputs.builds 为空,彻底避免自身 receipt 环。当前 receipt.graph 继续保存完整构建结束后的结果图(包括 prune 结果),供离线消费者与 `remove` 后投影使用。`sources` 保留为执行输入摘要列表并由 doctor 确认等于 inputs 的原始归档集合(Python 为单 sdist,build requirements 另在 inputs)。npm receipt.input 图不可被 remove 剪枝;最终 lock.npm 可以投影,原 receipt 保持不变。
实施所有权建议:
| Owner | 文件/API | 验证责任 |
| --- | --- | --- |
| extension/cache worker | toolchain extension owner、service/cache;ensure/read/list 与 `artifact_get_optional` typed miss | 签名、只读缺失/损坏区分、跨 UID、并发、失败原子性 |
| libs integration worker | libs.rs、main.rs、packages.rs、mutation.rs;明确 preparation policy、`install_locked`、Package exact restoration | 公共命令、Package 先恢复、Worker 提前准备、锁字节不变、离线边界 |
| registry/receipt worker | libs/registry.rs、build.rs、build/python.rs、build/npm.rs;v6 source、`restore_locked_artifacts`、`Session::replay` | 固定来源/输入、拒绝重新解析、嵌套依赖、输出 hash 不同失败 |
这些是逻辑所有权,可由同一 implementer 串行完成;`libs.rs` 类型/API 应由主实现者统一修改,避免多个 worker 同写。
具体公共/内部接点建议:
```text
toolchain::artifact_get_optional(layout, sha256, bytes) -> Result<Option<Vec<u8>>, String>
packages::restore_locked(project_root, lock, transport, store) -> Result<(), String>
libs::restore::install(project_root, target, preparation_inputs, transport, store)
-> Result<PreparationReport, String>
registry::restore_artifact(source, artifact, transport, store) -> Result<(), String>
build::Session::replay(receipt, store, signed_runtimes) -> Result<(), String>
```
`Session::replay` 绝不接收可解析版本的 RegistryResolver;将 Environment.run 中只用到的 store/runtime 提取成参数,首次 resolve 与 replay 共用原隔离执行器。Python inspect/requires 若执行,只用来比对已锁 backend/requirements,不能更新 inputs;metadata/wheel 阶段沿用现有检查。npm 执行 input 图,验证产出的 optional outcomes/native entries/rejections/结果图及 output SHA 与 receipt 相等后 publish。先完成全部验证再发布最终 output,不能先 publish 后发现不一致。
最小可测直接路径:现有 `build.rs:260` 的内联 backend fixture 无网络包管理器,验证固定动态 build requirements;`npm_build.rs:68` 固定安装收据与 `:421` optional 生命周期 fixture 验证原始图;在内存/目录 ArtifactStore 清空最终 output 后重放,两次字节一致。另加 hook 有意生成变化输出的 fixture,要求明确失败且锁字节保持。真实 C extension/node-gyp 仍复用现有显式 native fixture,不能把纯脚本 replay 通过报告成原生编译验收。
### External references
未查外部网络;本研究以仓库实现为准。协议事实来自现有代码:PyPI PEP440/508 + wheel、npm semver/SRI/实例图、Go proxy/MVS/sumdb、PEP517。版本绑定以 lock.TOOLCHAIN 和 signed RuntimePack/pack::Descriptor 为准,不假定系统 Python/Node/Go 版本。
## Caveats / Not Found
- 未实现任何功能、未运行测试/build/download、未修改产品或 specs。
- Extension catalog/IPC 的最终结构由发行路径研究和主代理决定;本文提供实际 caller 需求,不重复设计签名/安装 owner。
- 现有锁可精确获取大多数普通 registry 归档,但并非所有构建收据都包含完整重放信息;这是当前实现完整性的实际缺口,不是通过更多 retry 可解决的问题。
@@ -0,0 +1,101 @@
# Research: 精简发行、签名扩展与机器更新
- Query: 核对现有签名发行、bootstrap、共享机器资源与更新契约,给出最小完整拆分方案。
- Scope: mixed;源码只读,外部仅查解码 API 文档。
- Date: 2026-10-04
## Findings
### 现状与文件归属
| 文件 | 归属与证据 |
| --- | --- |
| `crates/dever-cli/src/toolchain/release.rs` | `ReleaseManifest`(74)只有 format/version/platform/artifacts;`MachineManager` 安装(293)、更新(310)、只读 core(437)、编译租约(470)、签名验证(579)、文件验证(616)、安装权限(968);稳定 `install.lock` inode(713)。 |
| `crates/dever-cli/src/toolchain/release_source.rs` | 官方 URL/重定向白名单(16、119)、元数据先验签(184)、同版本清单字节不变(213)、有界 gzip 下载(223)、catalog(250)、严格 USTAR 文件白名单(280)、NOREPLACE 发布(343)。 |
| `crates/dever-cli/src/toolchain/packaging.rs` | 固定 author 输入与签名;assemble(141)将 native、生态、sandbox、build 全部加入同一清单(202–234);必须在此按语义分类,不在压缩脚本根据随机路径猜测归属。 |
| `toolchain/packaging/{ecosystems,builds,sandbox,bootstrap}.rs` | 各类资源本身的语义校验与打包复用点;bootstrap(18)已有独立 ELF loader closure。 |
| `sdk/release-assets.py` | 所有清单 artifact 输出一个 deterministic gzip USTAR(create);目录发布使用 renameat2 NOREPLACE(publish)。 |
| `skills/scripts/install.py` | Python/OpenSSL 独立锚定公钥;authenticate(79)先验签,copy_artifact(129)精确字节/SHA 校验,payload(148)严格 tar 流,最后执行 bootstrap/dever。 |
| `toolchain/bootstrap.rs` 与 `bootstrap/transaction.rs` | 固定 bootstrap 系统落点、可信 key pin、崩溃恢复;SERVICE(19)仅允许写 state/cache/run,不允许服务写 versions 或新顶层目录。 |
| `toolchain/service.rs` | IPC Operation(100)只有 Status/Clean/ArtifactPut/ArtifactGet/Compile;内核 UID 认证(440),外 UID 可编译/资源缓存,不能 Clean;socket 0666、8 workers(34–36、154)。 |
| `toolchain/service/compile.rs` | compilation 租约、cache identity(74–86)、verify_pack(120)逐项验证所选 runtime 文件属于签名 manifest,cache hit 前也检查。 |
| `toolchain/runtime_pack.rs` | load_manifest(99)接受资源根目录,内部追加 runtime/<platform>;公开 validate(142)、RuntimePack::load(173)。 |
| `crates/dever-cli/src/compile.rs` | daemon 子进程依然在 worker(149)用 compiler_directory 加载 runtime;仅改 daemon 的 verify_pack 不够。 |
| `crates/dever-cli/src/libs.rs` | InstalledRegistry(709);tools(726)、runtime(797)、signed_artifact(823)、sandbox_resources(841)直接读版本根与根清单。 |
| `toolchain/cache.rs` | CacheStore 根实际为 cache/native(118),其中 ArtifactPut 单文件上限 64 MiB(81);不能用此 opaque artifact 上传接口安装 256 MiB 运行包。 |
| `toolchain/launcher.rs` | install/update 先下载后调用 MachineManager(27);当前 update 仅 core+skill,target 命令尚不存在。 |
现有清单并非全目录严格白名单:verify_artifacts 验证声明文件,core_libraries 对 lib 目录另有精确 closure 检查。拆分后不能把“目录存在”或只读 root metadata 当作签名证明。所有消费者仍需验证所属声明和文件字节。
### 建议的数据契约:一个签名根清单
将现有根清单明确升级新格式,保留 `version/platform/artifacts`,增加有界 `extensions`。`artifacts` 仅基础必需项;`extensions` 每项为 `{kind, target, artifacts}`。enum `ExtensionKind = Runtime(Ecosystem) | Build(Ecosystem) | Target`;身份为 `(version, host_platform, kind, deployment_target, signed_catalog_sha256)`,内容身份可对该项规范化完整描述计算 SHA-256。根签名覆盖所有扩展文件完整 path/bytes/sha256,不需要第二套扩展密钥或可变 unsigned receipt 作为信任来源。
基础内容:core、LLVM 完整 private closure、本机四 profile runtime、host sandbox、bootstrap、skill/templates。Target(ARM) 包含 ARM native 四 profile/CRT 与 ARM sandbox。Runtime(pip/npm/go,target) 与 Build(pip/npm,target) 独立;Go runtime 已包含 host 工具和 target stdlib,不强造 build/go。无基础文件与扩展间路径重叠,也无扩展互相路径重叠。
根清单 validator 统一校验版本/host、枚举目标组合、重复 ID、规范路径、lowercase SHA、数量/字节溢出、每包上限与全部描述上限。按固定类型限制 logical path:runtime/<ecosystem>/<target>、build/<ecosystem>/<target>、Target 对应 runtime/<target> 与 sandbox/<target>。防止扩展声明覆盖 core、lib、bootstrap、skill 或另一个 target。元数据顶层需 deny_unknown_fields;Python 同步语义,不维持兼容分叉。
外部 asset 名从固定 enum 计算;客户端与 manifest 不传任意 URL。每包一个 tar.zst;基础清单和签名单独发布;bootstrap 解码器作为原始文件单独发布并由根清单绑定。先签名固定原始内容,再压缩;确定性 USTAR 排序、mtime/uid/gid 和完整文件集继续成立。
### 扩展存储、接口与权限
推荐 `/opt/dever/cache/extensions/<version>/<catalog-sha>/<kind-target>/`;位于当前 service 可写范围内,和 `cache/native` 的可清理缓存分开。扩展先在专用 owner-private staging 中完全解压/验签/校验语义,fsync 文件和子目录,再 NOREPLACE rename 到最终路径。终态目录 0755、数据 0644,root-owned、不可被组/其他用户写;无需把 /opt/dever/cache 或 state 改成 0700。终态存在时重验内容而不是静默覆盖;失败仅清理自己 staging。manifest 摘要不同但版本相同必须失败,不创建并行“修正版”身份。
```rust
enum Operation {
// 现有操作……
EnsureExtension { version: Version, extension: ExtensionId },
}
struct ExtensionId { kind: ExtensionKind, target: BuildTarget }
// 网络/落盘仅 daemon 或拥有机器写权限的更新 owner 调用。
fn ensure_extension(layout: &Layout, version: &Version, id: &ExtensionId) -> Result<(), String>;
// 只读;不得打开 root-only install.lock,也不得下载。
fn resolve_extension(layout: &Layout, version: &Version, id: &ExtensionId) -> Result<VerifiedExtension, String>;
```
IPC 只接受已安装版本与其已验签目录声明的有限扩展,返回成功/内容身份而非用户指定路径。复用内核 UID/admission 和严格官方 HTTPS 下载,按请求限时、压缩/解压字节和数量限额;拒绝 caller 提供 URL、任意 filesystem path、payload 或 trust key。没有资源时 readonly resolve 返回准确 `dever lib install ...` 或 `dever target add linux-aarch64` 准备命令;run/build/check 不隐式进入 ensure。
采用一个 `SignedResources` 只读 owner,负责 root catalog、路径归属和已安装 extension 定位,供 InstalledRegistry 与 native pack 共用。`read_artifact(logical_path)` 与 `artifacts(prefix)` 只看到选定基础/扩展文件;避免构造 unsigned merged ReleaseManifest。RuntimePack::load 接收其实际资源根(base root 或 Target extension root),保留 logical path 到签名 artifacts 的逐项绑定。
SignedResources 需同时接入 libs.rs runtime/tools/sandbox_resources、service/compile.rs verify_pack、compile.rs worker RuntimePack::load。LLVM 动态库仍从 core 根加载。daemon BuildIdentity 必须包含所选 target pack 的签名/内容身份;不要让未使用扩展改变本机编译 cache identity。cache hit 仍重验所选 target pack。
### 更新与原子性
在同一个安装事务 owner 下读取 active 版本和其已安装扩展 ID 集合,验证新根清单有对应完整定义,下载并准备新 core+skill+这些扩展,最终重验后才 append active-version。失败保持旧 journal、旧 core 和扩展;已完整准备的新 immutable bytes 可留待重试复用,不声称更新成功。不自动安装新清单里所有扩展。
扩展选择直接由严格校验后的已发布目录集合导出,无需增加一个独立可漂移 selection pointer。若更新期间允许并行 ensure,必须在提交前锁内重新读取旧 active 扩展集合并补齐;更简单是将 ensure 与 update 的集合采样/发布串行化,避免丢失更新期间新增的扩展。沿用现有持久 inode lock,但不能在持有 exclusive lock 时递归调用再次 acquire 的公用 API;拆 locked 内部路径。当前锁为 try_lock,冲突会明确失败;如需“并发两项目都成功”应有限等待并在获锁后重验,不引入无限等待。
私有 staging 与 release.cleanup_staging 的全目录清扫不能冲突:若沿用 cache/staging,所有操作必须持同一安装锁;独立下载阶段则必须独占子树并禁止被旧 cleanup 删除。cache clean 目前只清 cache/native,保持其不删除 installed extensions。卸载版本时同安装锁删除对应扩展命名空间,active 保护不变。
当前 `dever update` 不更新机器 `/opt/dever/bin/dever[d]`,只更新版本目录/active。新资源 IPC 必須在首装 bootstrap launcher/daemon 中就存在;后续若改变 IPC,旧 daemon 兼容性是单独的 bootstrap 生命周期问题,不能假设 active core 更新自动重启了新服务。
### Zstandard:统一 Rust 解码,Python 验证后执行已有 launcher
推荐 zstd crate 的安全 streaming wrapper,静态编入发行时所需 decoder library;不要手写 C FFI 或 ctypes 依赖宿主 libzstd。Rust release downloader 与 launcher 隐藏 extraction 入口复用一个 codec/archive 模块:固定 `window_log_max(27)` 对应 128 MiB、压缩输入上限、解压输出上限、精确 frame 结束与 EOF 检查。不得仅 tar 遍历到结束标记就成功,必须排空/校验 zstd frame 尾部,拒绝截断与尾随额外数据;默认 Decoder 会连续解多个 frame,单 frame 策略需显式实施。
首装 Python 保留 OpenSSL 签名锚:下载/验证根 manifest → 从基础 artifacts 的固定 bootstrap/dever 与 bootstrap/lib/* 集合,按 digest 命名的 raw assets 下载全部 launcher closure → 精确大小与 SHA 验证、权限落盘 → 以空环境、绝对路径执行隐藏 `--dever-extract <owned-config-root>` → launcher 再验证根清单签名并调用共享 Rust USTAR 解包 → 完整已校验后执行 bootstrap 安装。配置与 trust key 仅放 root-owned scratch,不从应用读取。raw helper 文件与 payload 目标必须是两个目录,避免 create_new 冲突。需要 subprocess timeout 与失败后 kill/wait,不能把解包失败当成可继续安装。
现有 staged manifest 声明 `bootstrap/lib/libgcc_s.so.1`(`target/release-0.1.0/assets/dever-linux-x86_64.manifest.json:27`)。因此直接下载裸 launcher 不成立;但按主 agent 收敛方案,额外发布已签名 raw launcher + 完整小型私有 lib closure 后,复用 launcher 优于新 decoder bin:没有额外执行文件/第二套 tar validator。raw asset 文件名由既有 SHA 推导,不能由远端任意 URL 或目录控制。作者阶段仍需验证 ELF DT_NEEDED、RPATH、完整私有 closure,确保 zstd 为静态且 LLVM 不被引入 launcher/daemon;Linux libc/loader 为已声明 OS ABI。Python 在执行前只能信赖独立签名和已固定路径规则,签名发行 maker 对 closure 的检查仍是必要发行门槛。
Python SDK maker 可以使用作者提供的 zstd 工具或编码库完成 deterministic streaming tar.zst;这属于 author 工具链,不把系统 zstd 加成最终用户前提。公共资产总大小应单独计入 raw bootstrap closure,而不是只报告主 tar.zst。
### External references
- [zstd Decoder API](https://docs.rs/zstd/latest/zstd/stream/read/struct.Decoder.html),本次页面为 0.14.0:Read streaming、single_frame、window_log_max;finish_frame 错误需要显式处理。版本选型应核对仓库 MSRV 并固定 Cargo.lock,本次没有安装或锁定依赖。
- 本地 SDK `target/backend-sdk/usr/include/zstd.h:107` 为 1.5.5;`:606` 说明 streaming window 上限控制、`:648` 提供 DCtx 参数 API。这是本地已有头文件证据,不代表最终打包采用此版本。
- GitHub zstd-sys Cargo.toml 页面读取失败;静态链接需在实施中核对选定 crate build 配置与产物 ELF,不能将本研究当作已验证链接证据。
### Related specs
- `.trellis/spec/backend/toolchain-and-library.md:23` compiler-relative runtime 与 daemon 签名闭包;`:27` bootstrap;`:84–96` release/skill/author/ELF closure;`:337–343` onboarding、native release、shared toolchain 定向验收。
- `.trellis/spec/backend/index.md`:本仓库是 Rust 语言编译器,不采用 Go Dever Model/Page/Service 规则。
- `.trellis/tasks/10-04-slim-distribution/prd.md`:离线 run/build、多用户共享、按需自动生态、target 命令、core+已装扩展更新、不改宿主服务。
## Caveats / Not Found
- 子 agent `task.py current --source` 返回 none;主 agent 已明确指定此任务与写入位置,未修改任务状态。
- 本次未下载发行/依赖、未运行构建/测试、未改产品文件/服务,也未读取私钥或授权凭据。仅查看公开解码 API 文档。
- 120 MiB 目标是已有压缩估算;codec 与新正式优化 core 会改变最终数字,尚无本次测量。
- 精简需同时调整 package/Lib 显式准备策略:另一研究分支已发现 `source_workers_with_packages` 在 resolve_project 前读 runtime;不能只在 resolver 内 ensure。remove/doctor/check/run/build 必须 readonly。
- 更新携带范围建议 active 版本已安装选择;若产品要求机器所有历史 pinned 版本的 union,应在设计明确,不应默默给新版本安装历史上任何扩展。
- 大包首次安装、损坏重试、多 UID 同时 ensure、更新失败仍运行旧版、ARM cache hit 校验、decoder 超大 window/截断/尾随数据均需定向验收;本研究没有执行验证。