Prebuilt system primitives
预编译系统原语
The JSONL writer's `fs-ext` dependency compiled a NAN addon during consumer installation. Native compiler availability and Node module ABI changes therefore affected ordinary installs, including Node 26. The repository already maintained the Landlock launcher and its per-platform publication workflow.
English
Problem
The JSONL writer's fs-ext dependency compiled a NAN addon during consumer installation. Native compiler availability and Node module ABI changes therefore affected ordinary installs, including Node 26. The repository already maintained the Landlock launcher and its per-platform publication workflow.
Decision
The independently versioned @deepseek-ai/node-addon-system family in native/system distributes the existing landlock-run executable and a stable Node-API v8 system.node addon. Platform packages select OS and CPU; Linux carries distinct glibc and musl addon files. macOS carries the addon without a Landlock executable. Neither the entry nor platform packages compile during installation.
The package has no root export. The ./landlock-run JavaScript entry retains Landlock's API and CLI protocol. The ./flock entry loads its addon only when tryLockExclusive(fd) is called. It runs flock(fd, LOCK_EX | LOCK_NB) in asynchronous native work and captures errno on that worker. The caller owns the descriptor through completion and releases its lock by closing it. Missing bindings reject acquisition rather than granting an unprotected lock.
The Session write-lease decision continues to own acquisition timing, inode checks, close ownership, and crash semantics. Windows retains its existing koffi semaphore. The browser worker substitutes only the flock subpath; it uses the unchanged ./landlock-run JavaScript API.
Source builds explicitly compile the host addon before repository tests and builds that need it. Native CI builds the complete platform payload and tests the same addon bytes across Node releases; Linux also exercises the musl payload in Alpine. Platform prepack rejects malformed or incomplete binaries, and an offline npm install rehearsal checks installed bytes and real lock behavior. Native tests cover descriptor/process contention, close and crash release, independent errno values, and worker teardown.
Alternatives considered
Keep NAN and publish one build per Node ABI. This retains a Node-major build matrix for a binding that needs only stable Node-API operations. The evaluated [email protected] selected a Node 25 ABI 141 binary under Node 26 ABI 147; its default-install fallback also exited without building when NAN was hoisted.
Bundle fs-ext into the parent tarball. npm normally still runs bundled dependency installation hooks. Bundling alone neither suppresses compilation nor makes one binary portable across operating systems, CPUs, libc implementations, or Node ABIs.
Replace flock with OFD/fcntl locks. On ordinary Linux filesystems these locks do not necessarily exclude existing flock holders. A tmpfs probe admitted an OFD lock while fs-ext held flock, so this is not a behavior-preserving replacement.
Use koffi for the POSIX call. A synchronous call changes event-loop blocking behavior; reading errno after its asynchronous callback reads the wrong thread's value. A native async-work adapter keeps the syscall result and errno together without another FFI coordination layer.
Consequences
The family owns a small C binding, platform builds, and installed-artifact verification rather than an entire filesystem-extension API. Node-API removes the per-Node-major binary requirement, not OS/CPU/libc requirements. The shared native release includes both capabilities, but importing or using one does not load the other. Landlock binary semantics, Windows locking, and released Session data formats remain unchanged.
中文
Problem
JSONL 写入方依赖的 fs-ext 在用户安装时编译 NAN addon。因此,原生编译器是否可用以及 Node 模块 ABI 的变化会影响普通安装,包括 Node 26。仓库已经维护了 Landlock 启动器及其按平台发布的工作流。
Decision
native/system 中独立版本的 @deepseek-ai/node-addon-system 包族分发既有 landlock-run 可执行文件和使用稳定 Node-API v8 的 system.node addon。平台包按操作系统和 CPU 选择;Linux 分别携带 glibc 与 musl addon 文件。macOS 携带 addon,但不包含 Landlock 可执行文件。入口包和平台包都不在安装期间编译。
包不提供根导出。./landlock-run JavaScript 入口保留 Landlock API 和 CLI 协议。./flock 入口仅在调用 tryLockExclusive(fd) 时加载 addon。它在异步原生工作中执行 flock(fd, LOCK_EX | LOCK_NB),并在该工作线程保存 errno。调用方在完成前持有描述符,并通过关闭它释放锁。绑定缺失时拒绝获取锁,不授予没有保护的锁。
Session 写租约决策 继续负责获取时机、inode 校验、关闭所有权和崩溃语义。Windows 保留既有 koffi 信号量。浏览器 worker 仅替换 flock 子路径,使用未经修改的 ./landlock-run JavaScript API。
源码构建在需要 addon 的仓库测试与构建之前显式编译当前宿主 addon。Native CI 构建完整平台产物,并让相同 addon 字节跨 Node 版本测试;Linux 还在 Alpine 中执行 musl 产物。平台 prepack 拒绝格式错误或不完整的二进制,离线 npm 安装演练检查安装字节与真实锁行为。Native 测试 覆盖描述符与进程竞争、关闭和崩溃释放、独立 errno 值及 worker 清理。
Alternatives considered
保留 NAN,为每个 Node ABI 发布构建。 这会为仅需稳定 Node-API 操作的绑定保留 Node 主版本构建矩阵。已评估的 [email protected] 在 Node 26 ABI147 下选中 Node 25 ABI141 二进制;默认安装回退还会在 NAN 被提升安装时提前退出而不编译。
将 fs-ext 打入父包 tarball。 npm 默认仍执行 bundled 依赖的安装钩子。仅打包既不能禁止编译,也不能让一个二进制跨操作系统、CPU、libc 实现或 Node ABI 通用。
将 flock 换成 OFD/fcntl 锁。 在普通 Linux 文件系统上,这些锁不一定排斥既有 flock 持有者。tmpfs 探针在 fs-ext 持有 flock 时仍取得 OFD 锁,因此这不是保持行为的替换。
通过 koffi 执行 POSIX 调用。 同步调用改变事件循环的阻塞行为;在异步回调后读取 errno 会读到错误线程的值。原生 async-work 适配器把系统调用结果和 errno 保存在一起,无须另加 FFI 协调层。
Consequences
包族维护小型 C 绑定、平台构建和安装产物验证,而不是整套文件系统扩展 API。Node-API 消除按 Node 主版本分发二进制的要求,但不消除操作系统、CPU 和 libc 要求。统一原生发布包含两项能力,但导入或使用其中一项不会加载另一项。Landlock 二进制语义、Windows 锁和已发布 Session 数据格式保持不变。