
字节笔记本
2026年10月6日 · 约 5 分钟读完
命名即边界:landlock-run 的命名约定解析
给安全组件起名字,听起来像件小事。但在 landlock-run 项目里,命名被当成接口设计的一部分来对待:包名、二进制名、环境变量前缀、C 符号,每一层都有明确规则,而且规则背后几乎都对应一条安全或分发上的理由。项目在 docs 里为这件事单独维护了一篇命名约定,本文把它拆开来看。
项目背景:三百行 C 写成的沙箱启动器
landlock-run 是一个运行在 Linux 上的 Landlock 自限制后 exec 启动器:先在自己的进程上装好 Landlock 规则集,再 exec 真正要运行的命令。规则集会跨 execve 继承,所以被包裹的命令以及它派生出的全部子进程都生活在允许清单之内,而发起调用的宿主进程不受限制。Landlock 本身是 Linux 内核的安全模块,5.13 及以上的内核开启后,允许非特权进程给自己套上文件系统访问约束。
整个启动器只有约 300 行 C11,直接对内核 UAPI 编程,静态链接 musl,以预编译的按平台 npm 包分发,外面再套一个薄的 JavaScript 入口包,负责解析二进制路径、探测内核能力、构造授权参数。对 agent harness 这类需要在宿主机上运行不可信命令的场景,这是一种只约束子进程、不约束宿主自己的沙箱方案,并且处处 fail closed:内核无法强制执行时,启动器直接以非零退出码失败,命令根本不会被执行。

npm 包名:平台后缀只回答平台问题
包家族采用入口包加平台包的结构。公共包族归属 @deepseek-ai scope,统一使用 node-addon-landlock-run 前缀,平台包只在后面追加平台信息:
@deepseek-ai/node-addon-landlock-run
@deepseek-ai/node-addon-landlock-run-linux-x64
@deepseek-ai/node-addon-landlock-run-linux-arm64平台后缀刻意保持干净:不带 libc 分量,也不带变体分量。原因有两条。其一,二进制是静态链接 musl 的,同一份二进制在 glibc 和 musl 发行版上都能运行,gnu 与 musl 两套后缀在这里没有存在意义,包元数据里的 libc 字段也一并省去。其二,构建变体不进包名,它们被收进平台包内的 prebuilds.json 清单和二进制文件名里;安装期选对平台包,则交给 npm 的 os 与 cpu 字段完成。名字保持单一职责:包名回答在哪个平台,清单回答包里有什么。
二进制名:一个固定路径
可执行文件的命名同样从简。启动器叫 landlock-run,随每个平台包发布,固定位于包内 bin/landlock-run。入口包的 launcherPath() 解析出平台包之后,拼出来的就是这条路径。名字和包内位置都是常量,消费方不需要任何配置或搜索逻辑去发现它。
环境变量:前缀留给构建测试,运行时一律不读
这一节最能体现整套约定的态度。NALR_ 前缀(Node Addon Landlock Run 的缩写)保留给构建与测试编排使用,目前唯一的成员是 NALR_REQUIRE_LANDLOCK,测试专用:当内核无法强制执行 Landlock 时,让测试直接失败,而不是悄悄跳过。
与此相对的是一条运行时安全规则:运行期二进制和入口包不读取任何环境变量。哪个二进制去约束一个进程,绝不能由外围环境决定,否则等于给环境注入留了一扇门;测试注入一律改走函数参数。约定还特别提醒,环境变量命名不要混入 npm scope,所以前缀是紧凑的 NALR_,而不是把组织名拉进来。

C 符号:没有命名空间,就是最好的命名空间
C 侧几乎是空的。启动器是单个 C 文件、静态链接,不导出任何符号,自然也就不需要导出符号的前缀约定。唯一出现的 LL_ 前缀属于本地定义的内核 UAPI 常量:项目把内核头文件里的相关定义原样抄进 C 源码,保留内核原名,再加 LL_ 前缀标明来源。这样做让构建不依赖工具链头文件的版本,也让这些定义本身成为审计记录的一部分。
名字即边界
把四层规则放在一起看,思路是一致的:让名字承载确定性,让接口面尽量小。平台后缀保证安装期拉对文件;省掉 libc 与变体分量,保证一份二进制处处可跑;运行时零环境变量,关掉了通过环境劫持沙箱选择的路径;不导出 C 符号,把审计面压缩到 C 源码加内核稳定的系统调用约定。对同样要发布原生二进制的前端工程来说,这是一个可以直接借鉴的模板:平台信息进包名,工具变体进清单文件,运行时输入面能省则省。边界画清楚了,组件才容易被信任。



