In-repository Landlock release
仓库内 Landlock 发布
The `@deepseek-ai/node-addon-landlock-run` source already lives beside its DeepSeek Harness consumers under `native/landlock-run`, but it previously kept a separate pnpm workspace and lockfile and depended on a standalone repository for npm publication. Harness packages consumed a fixed registry version, so one pull request could change the launcher contract and its consumer without testing those changes together. Th
English
Problem
The @deepseek-ai/node-addon-landlock-run source already lives beside its DeepSeek Harness consumers under native/landlock-run, but it previously kept a separate pnpm workspace and lockfile and depended on a standalone repository for npm publication. Harness packages consumed a fixed registry version, so one pull request could change the launcher contract and its consumer without testing those changes together. The source repository's native workflow could rehearse the package, but it did not publish the artifact it tested.
The mirror also duplicated release coordination: export the source, update another lockfile, run another release workflow, publish the native family, then return to this repository to bump registry dependencies. That split made it harder to match each binary to its source commit, roll back releases, and coordinate security fixes without changing what npm users actually needed.
The existing unscoped npm names are owned by the standalone publisher account rather than the @deepseek-ai organization. Moving only the workflow would therefore leave publication dependent on a personal credential outside the repository's release ownership.
The consolidation must preserve platform selection. The public distribution is deliberately one JavaScript entry package plus separate Linux x64 and arm64 binary packages; merging repository ownership does not imply putting every binary into one tarball or publishing every DeepSeek Harness package at the launcher version.
Decision
native/landlock-run and native/landlock-run/packages/* belong to the repository's root pnpm workspace and use the root pnpm-lock.yaml. Harness consumers declare @deepseek-ai/node-addon-landlock-run with workspace:*, so development, type checking, builds, and pull-request tests resolve the entry package from the same checkout. The root TypeScript project graph builds that entry package before consumers, and the repository cleaner owns its direct lib/ output.
The public npm boundary is three organization-owned packages with one launcher-family version: @deepseek-ai/node-addon-landlock-run, @deepseek-ai/node-addon-landlock-run-linux-x64, and @deepseek-ai/node-addon-landlock-run-linux-arm64. The entry package retains both platform packages as optionalDependencies; their os and cpu manifest fields let npm install only the compatible package. Repository constraints require publishConfig.access: public for those three names and require their versions to match the private launcher workspace root. The former unscoped names are not release targets of this repository. These three are no longer the only public packages: the per-sequence access decision publishes the nine vendored framework packages publicly as well, while the dsh family stays restricted.
The main repository owns both native CI and publication. Landlock Run runs for relevant pull requests and master pushes and builds each platform on its matching native runner. The manually dispatched Landlock Run Release workflow builds both platform binaries, transfers them as workflow artifacts, assembles and verifies the complete package family, packs immutable npm tarballs, installs and exercises those tarballs, and only then permits the protected publish job. Platform tarballs publish before the entry tarball that optionally depends on them. Publication uses landlock-run-vX.Y.Z tags so launcher releases cannot collide with other release families in the monorepo; prereleases use the npm next dist-tag.
The sandbox packed-install rehearsal no longer permits the npm registry to supply the launcher. It packs the current checkout's entry and matching native package alongside the harness dependency closure, installs those local tarballs into an external plain-Node consumer, and proves that the installed launcher is executable, byte-identical to the native build, and the correct ELF architecture before testing confinement or fail-closed behavior.
Alternatives considered
- Keep the standalone repository as a release mirror — rejected because it preserves the split lockfiles, source export, stale-registry test window, and cross-repository release sequence after the source of record has already moved here.
- Publish one npm package containing every platform binary — rejected because users would download binaries they cannot run and npm could no longer use package-level
os/cpufiltering. Repository ownership and npm package layout are separate choices. - Give the launcher the root DeepSeek Harness version and publish the complete monorepo recursively — rejected because this change owns one three-package public family, not the independent
@deepseek-ai/dsh-*baseline. The artifact-first npm baseline proposal explicitly keeps native workspaces outside its target set. - Cross-compile both binaries in one release job — rejected because the checked-in package matrix already assigns each architecture a native GitHub runner and avoids adding a cross-toolchain trust surface.
Consequences
Launcher protocol, TypeScript entry code, native source, harness consumption, and publish-path tests can change in one pull request and resolve from one lockfile. A release tag now identifies the source, consumer integration, build instructions, and tarballs tested by the main repository. The standalone mirror is no longer part of the release path and can be archived after the first successful in-repository publication.
npm consumers install @deepseek-ai/node-addon-landlock-run; the old unscoped package names are not silently redirected. A supported Linux host downloads the scoped entry package and its matching architecture package; the other architecture package is skipped. An unsupported host receives no platform binary and follows the existing deterministic fail-closed probe path.
The implementation touches more files than a dependency-line edit because the repository must also own workspace constraints, TypeScript build order, cleanup, CI triggers, release tags, lockfile generation, comparison of installed binaries with workspace builds, release documentation, and generated notices. The behavioral boundary stays narrow: it changes only the Landlock package family and its three direct workspace consumers, not the version or publication state of other DeepSeek Harness packages.
The first scoped release must use an @deepseek-ai organization token through the npm-publish environment's NPM_TOKEN, because npm cannot configure trusted publishing until a package exists. After bootstrap, all three packages must authorize this repository's release workflow before the fallback token can be removed. npm still publishes packages sequentially and offers no cross-package transaction, so a failed publish can leave a partial version. Because npm rejects an already-published name and version, an operator must inspect the registry and publish only the missing tarballs rather than rerunning the workflow unchanged. Linux x64 and arm64 runners remain the authoritative binary and real-kernel checks; a macOS checkout can verify the entry package and unsupported-platform behavior but cannot replace those jobs.
This note supersedes only the release-mirror and registry-pinned source-development statements in the sandbox Agent Note; that note continues to own sandbox behavior, runner selection, and enforcement semantics.
中文
问题
@deepseek-ai/node-addon-landlock-run 源码已经与其 DeepSeek Harness 消费方一同位于 native/landlock-run 下,但此前仍保留独立的 pnpm workspace 和锁文件,并依赖一个独立仓库发布到 npm。Harness 包使用 npm 注册表中的固定版本,因此同一个 PR(Pull Request)可以同时修改启动器约定及其消费方,却无法一起测试这些改动。源码仓库的原生工作流可以演练打包流程,但不会发布它实际测试过的产物。
发布镜像还造成重复的发布协调工作:导出源码、更新另一份锁文件、运行另一套发布工作流、发布原生包家族,然后回到本仓库更新注册表依赖。npm 用户的实际需求并未改变,这种拆分却让每个二进制更难对应到其源提交,也让发布回滚和安全修复协调更困难。
现有的非 scoped npm 包名归独立发布账号所有,而不属于 @deepseek-ai 组织。因此,仅迁移工作流仍会让发布依赖仓库发布归属之外的个人凭证。
此次整合必须保留平台选择机制。公开分发有意采用一个 JavaScript 入口包,并为 Linux x64 和 arm64 分别提供二进制包;合并仓库归属并不意味着要把所有二进制文件放进同一个 tarball,也不意味着要按照启动器版本发布所有 DeepSeek Harness 包。
决策
native/landlock-run 和 native/landlock-run/packages/* 属于仓库根 pnpm workspace,并使用根 pnpm-lock.yaml。Harness 消费方将 @deepseek-ai/node-addon-landlock-run 声明为 workspace:*,因此开发、类型检查、构建和 PR 测试都会从同一个 checkout 解析入口包。根 TypeScript 项目图会先构建该入口包,再构建消费方;仓库清理器负责清理其直接生成的 lib/ 输出目录。
公开 npm 分发边界由 3 个归组织所有的包组成,它们共用一个启动器包家族版本:@deepseek-ai/node-addon-landlock-run、@deepseek-ai/node-addon-landlock-run-linux-x64 和 @deepseek-ai/node-addon-landlock-run-linux-arm64。入口包继续通过 optionalDependencies 声明两个平台包;它们在 manifest(元数据清单)中的 os 和 cpu 字段让 npm 只安装兼容的包。仓库约束要求这 3 个包名设置 publishConfig.access: public,并要求其版本与私有启动器 workspace 根包一致。原先的非 scoped 包名不属于本仓库的发布目标。这 3 个已不再是唯一的公开包:按序列区分 access 的决策让 vendored 框架九包也公开发布,而 dsh 族保持受限。
主仓库同时负责原生 CI 和发布。Landlock Run 会为相关 PR 和 master 推送运行,并在各自匹配的原生 runner 上构建每个平台包。手动触发的 Landlock Run Release 工作流会构建两个平台的二进制文件,将其作为工作流产物传递,组装并验证完整的包家族,打包出内容不可变的 npm tarball,安装并实际运行这些 tarball,之后才允许受保护的发布作业执行。发布顺序是平台 tarball 在前,最后发布将它们列为可选依赖的入口 tarball。发布使用 landlock-run-vX.Y.Z tag,避免启动器版本与 monorepo 中其他发布家族发生冲突;预发布版本使用 npm 的 next dist-tag。
沙箱打包安装演练不再允许 npm 注册表提供启动器。它会将当前 checkout 的入口包、匹配的原生包和 harness 依赖闭包一起打包,把这些本地 tarball 安装到仓库外部的纯 Node 消费方中,并在测试约束效果或失败闭合行为之前,证明所安装的启动器可执行、与原生构建产物字节完全一致,且具有正确的 ELF 架构。
曾考虑的替代方案
- 保留独立仓库作为发布镜像:不予采纳,因为在权威源码已经迁入本仓库后,这仍会保留拆分的锁文件、源码导出、测试使用陈旧注册表版本的时间窗,以及跨仓库发布序列。
- 发布一个包含所有平台二进制文件的 npm 包:不予采纳,因为用户会下载无法在其主机上运行的二进制文件,而且 npm 无法再利用包级
os/cpu筛选。仓库归属与 npm 包布局是两个彼此独立的选择。 - 让启动器使用 DeepSeek Harness 根版本,并递归发布整个 monorepo:不予采纳,因为本次改动负责的是一个由 3 个包组成的公开包家族,而不是独立的
@deepseek-ai/dsh-*基线。产物优先的 npm 基线提案明确将原生 workspace 排除在其目标集合之外。 - 在一个发布作业中交叉编译两个二进制文件:不予采纳,因为仓库内已提交的包矩阵已经为每种架构分配了原生 GitHub runner,无需再把交叉工具链纳入信任边界。
后果
同一个 PR 可以同时修改启动器协议、TypeScript 入口代码、原生源码、harness 消费方式和发布路径测试,并从同一份锁文件解析这些内容。发布 tag 现在标识源码、消费方集成、构建指令,以及主仓库测试过的 tarball。独立镜像已不再属于发布路径,可以在第一次成功从本仓库发布后归档。
npm 消费方改为安装 @deepseek-ai/node-addon-landlock-run;原先的非 scoped 包名不会被静默重定向。受支持的 Linux 主机会下载 scoped 入口包及与其架构匹配的包,并跳过另一架构的包。不受支持的主机不会收到平台二进制文件,并继续沿用现有的确定性失败闭合探测路径。
实现涉及的文件比只修改一行依赖更多,因为仓库还必须负责 workspace 约束、TypeScript 构建顺序、清理、CI 触发条件、发布 tag、锁文件生成、将已安装二进制与 workspace 构建进行比较、发布文档和生成的第三方声明。行为边界仍然很窄:此次改动只影响 Landlock 包家族及其 3 个直接 workspace 消费方,不改变其他 DeepSeek Harness 包的版本或发布状态。
第一次发布 scoped 包时,必须通过 npm-publish 环境的 NPM_TOKEN 使用 @deepseek-ai 组织 token,因为 npm 只有在包已经存在后才能配置 trusted publishing。完成 bootstrap 后,必须让 3 个包都授权本仓库的发布工作流,才能移除后备 token。npm 仍会按顺序发布各个包,且不提供跨包事务,因此发布失败可能留下只完成了一部分的版本。由于 npm 会拒绝已经发布的同名同版本包,操作人员必须检查注册表并只发布缺失的 tarball,而不能原样重新运行工作流。Linux x64 和 arm64 runner 仍提供权威的二进制构建与真实内核检查;macOS checkout 可以验证入口包和不受支持平台上的行为,但不能取代这些作业。
本说明仅取代沙箱 Agent Note中有关发布镜像和开发源码时依赖注册表固定版本的表述;该 Agent Note 仍负责沙箱行为、runner 选择和强制执行语义。