refactor(native): expose Landlock through its capability subpath

This commit is contained in:
imccyu committed 2026-09-08 20:49:10 +08:00
1 parent a2784a223e
commit 97e7223d5f
34 files changed
+102 -46

No files matched your search

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-07-prebuilt-system-primitives.md
2026-09-07-prebuilt-system-primitives.md: 23037c2d0ebc0871125cf8057459cc3930bcc57a
2026-09-07-prebuilt-system-primitives.zh.md: 4e1e6cc061d6169078c25cf6092965edd553f7e8
2026-09-07-prebuilt-system-primitives.md: 183cd3ea984cd779417493f0de17a770f38fc001
2026-09-07-prebuilt-system-primitives.zh.md: f051bba92910086601aa48a877f360e276df524d
@@ -12,9 +12,9 @@ The JSONL writer's `fs-ext` dependency compiled a NAN addon during consumer inst
The independently versioned `@deepseek-ai/node-addon-system` family in [native/system](../../../../native/system/README.md) 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 root JavaScript entry retains Landlock's API and [CLI protocol](../../../../native/system/docs/cli-contract.md). 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 package has no root export. The `./landlock-run` JavaScript entry retains Landlock's API and [CLI protocol](../../../../native/system/docs/cli-contract.md). 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](../feature/2026-08-31-cross-process-session-write-lease.md) 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 continues to use the root Landlock JavaScript API.
The [Session write-lease decision](../feature/2026-08-31-cross-process-session-write-lease.md) 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](../../../../native/system/test/flock.test.js) cover descriptor/process contention, close and crash release, independent errno values, and worker teardown.
@@ -12,9 +12,9 @@ JSONL 写入方依赖的 `fs-ext` 在用户安装时编译 NAN addon。因此,
[native/system](../../../../native/system/README.zh.md) 中独立版本的 `@deepseek-ai/node-addon-system` 包族分发既有 `landlock-run` 可执行文件和使用稳定 Node-API v8 的 `system.node` addon。平台包按操作系统和 CPU 选择;Linux 分别携带 glibc 与 musl addon 文件。macOS 携带 addon,但不包含 Landlock 可执行文件。入口包和平台包都不在安装期间编译。
根 JavaScript 入口保留 Landlock API 和 [CLI 协议](../../../../native/system/docs/cli-contract.md)。`./flock` 入口仅在调用 `tryLockExclusive(fd)` 时加载 addon。它在异步原生工作中执行 `flock(fd, LOCK_EX | LOCK_NB)`,并在该工作线程保存 errno。调用方在完成前持有描述符,并通过关闭它释放锁。绑定缺失时拒绝获取锁,不授予没有保护的锁。
包不提供根导出。`./landlock-run` JavaScript 入口保留 Landlock API 和 [CLI 协议](../../../../native/system/docs/cli-contract.md)。`./flock` 入口仅在调用 `tryLockExclusive(fd)` 时加载 addon。它在异步原生工作中执行 `flock(fd, LOCK_EX | LOCK_NB)`,并在该工作线程保存 errno。调用方在完成前持有描述符,并通过关闭它释放锁。绑定缺失时拒绝获取锁,不授予没有保护的锁。
[Session 写租约决策](../feature/2026-08-31-cross-process-session-write-lease.zh.md) 继续负责获取时机、inode 校验、关闭所有权和崩溃语义。Windows 保留既有 koffi 信号量。浏览器 worker 仅替换 flock 子路径,继续使用根 Landlock JavaScript API。
[Session 写租约决策](../feature/2026-08-31-cross-process-session-write-lease.zh.md) 继续负责获取时机、inode 校验、关闭所有权和崩溃语义。Windows 保留既有 koffi 信号量。浏览器 worker 仅替换 flock 子路径,使用未经修改的 `./landlock-run` JavaScript API。
源码构建在需要 addon 的仓库测试与构建之前显式编译当前宿主 addon。Native CI 构建完整平台产物,并让相同 addon 字节跨 Node 版本测试;Linux 还在 Alpine 中执行 musl 产物。平台 prepack 拒绝格式错误或不完整的二进制,离线 npm 安装演练检查安装字节与真实锁行为。Native [测试](../../../../native/system/test/flock.test.js) 覆盖描述符与进程竞争、关闭和崩溃释放、独立 errno 值及 worker 清理。
+1 -1
View File
@@ -8,7 +8,7 @@ This workspace owns `@deepseek-ai/node-addon-system`: the Linux `landlock-run` c
- The launcher uses only libc, statically linked against musl. Its kernel UAPI definitions remain in the reviewed C source.
- Node bindings use stable Node-API v8, never NAN, V8 C++ APIs, or experimental Node interfaces. Linux glibc and musl addons are distinct binaries; macOS has its own Mach-O bundle.
- The flock binding attempts only `LOCK_EX | LOCK_NB` in asynchronous work and captures errno on that worker. The caller owns the fd through completion and releases its lock by closing it.
- Root entry imports must not load the addon. `./flock` loads it only when called; Windows retains the Harness's existing semaphore implementation.
- `./landlock-run` and `./flock` are independent capability exports; the package has no root export. Neither import loads the addon. `./flock` loads it only when called; Windows retains the Harness's existing semaphore implementation.
- Runtime binary selection has no environment-variable overrides. `NALR_REQUIRE_LANDLOCK` is a test-only enforcement requirement.
- There is no install-time compile fallback. Missing Landlock binaries probe unusable; missing flock bindings reject acquisition, never silently grant a lock.
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write native/system/README.md
README.md: 674e47c79726d0212ab17468d90e0a73e6c63db6
README.zh.md: b9770453e25620add25a67d93a89abd0f41c6554
README.md: 5a01754cb96fabbdfd2dbf4f068f19addd7a2347
README.zh.md: dabd00cecd05ded13a50d2efd000985afed713d4
+1 -1
View File
@@ -18,7 +18,7 @@ Use the Linux `landlock-run` executable to confine subprocesses, or the `./flock
## Use
The root entry exports `launcherPath`, `probe`, and `grantArgs` for Landlock. Its executable name, flags, and failure semantics are defined by the [CLI contract](docs/cli-contract.md).
`@deepseek-ai/node-addon-system/landlock-run` exports `launcherPath`, `probe`, and `grantArgs` for Landlock. Its executable name, flags, and failure semantics are defined by the [CLI contract](docs/cli-contract.md).
The [flock behavior contract](docs/flock-contract.md) maps descriptor, process, and advisory-lock semantics to independent native tests.
+1 -1
View File
@@ -18,7 +18,7 @@ kind: "package-library"
## Use
根入口为 Landlock 导出 `launcherPath`、`probe` 和 `grantArgs`。其可执行文件名、参数和失败语义由 [CLI 约定](docs/cli-contract.md) 定义。
`@deepseek-ai/node-addon-system/landlock-run` 为 Landlock 导出 `launcherPath`、`probe` 和 `grantArgs`。其可执行文件名、参数和失败语义由 [CLI 约定](docs/cli-contract.md) 定义。
[flock 行为约定](docs/flock-contract.md) 将描述符、进程和咨询式锁语义对应到独立原生测试。
+2 -2
View File
@@ -4,9 +4,9 @@ The system package family supplies native mechanisms to Node callers: a Linux co
## Package family
The ESM entry `@deepseek-ai/node-addon-system` and its optional platform packages share one version. Platform metadata chooses the operating system and CPU; each package's `prebuilds.json` declares the files it must contain.
The ESM package `@deepseek-ai/node-addon-system` and its optional platform packages share one version. Platform metadata chooses the operating system and CPU; each package's `prebuilds.json` declares the files it must contain.
The root entry owns the existing Landlock path resolution, grant argv, and functional probe. It does not load native addons. The `./flock` entry lazily loads `system.node` only when `tryLockExclusive(fd)` is called. Importing either JavaScript entry therefore works without a matching native payload.
The `./landlock-run` entry owns Landlock path resolution, grant argv, and the functional probe. It does not load native addons. The `./flock` entry lazily loads `system.node` only when `tryLockExclusive(fd)` is called. Importing either JavaScript entry therefore works without a matching native payload. The package exposes these capability subpaths and its manifest, without a root export.
## Separate mechanisms
+2 -2
View File
@@ -4,7 +4,7 @@ The family publishes one ESM entry package plus OS/CPU-selected optional platfor
## Payloads
The entry exports the Landlock API at its root and the asynchronous lock API at `./flock`, with C sources included for auditability. Platform packages contain no JavaScript.
The entry package exports the Landlock API at `./landlock-run` and the asynchronous lock API at `./flock`, with C sources included for auditability. There is no root export. Platform packages contain no JavaScript.
- Linux: `bin/landlock-run`, `bin/glibc/system.node`, and `bin/musl/system.node`.
- macOS: `bin/system.node`.
@@ -15,7 +15,7 @@ The entry exports the Landlock API at its root and the asynchronous lock API at
Neither entry nor platform packages have installation lifecycle scripts. The entry resolves its matching optional package when a native operation needs it. Optional means that the package manager selects a platform, not that a requested lock can succeed without its binding.
The root Landlock API stays importable without native payloads and reports unavailable enforcement through its probe. The flock entry is also lazy at import; acquisition reports a missing or unloadable addon instead of compiling or granting an unprotected lock.
The `./landlock-run` API stays importable without native payloads and reports unavailable enforcement through its probe. The flock entry is also lazy at import; acquisition reports a missing or unloadable addon instead of compiling or granting an unprotected lock.
## Pack verification
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write native/system/packages/entry/README.md
README.md: af3f5ac4df5883d7d9b4f65d273f9d45fae874e8
README.zh.md: 55f428568cb7b5e7a9956c842931f52e0feeaf0a
README.md: 8f6a4d341c8f9e4c104e5706e08d9a7d45c065e1
README.zh.md: 152257916df0cf40511b9743d389ae753ab6c85c
+1 -1
View File
@@ -6,7 +6,7 @@ kind: "package-library"
English | [中文](README.zh.md)
The root entry exports the Landlock launcher path, enforcement probe, grant arguments, and protocol constants. Its independent `./flock` entry exports `tryLockExclusive(fd): Promise<void>`; importing either entry does not load `system.node`.
The `./landlock-run` entry exports the Landlock launcher path, enforcement probe, grant arguments, and protocol constants. The independent `./flock` entry exports `tryLockExclusive(fd): Promise<void>`; importing either entry does not load `system.node`. The package has no root export.
The lock operation attempts `LOCK_EX | LOCK_NB` asynchronously. Keep the caller-owned descriptor open until completion; contention rejects with `EAGAIN`/`EWOULDBLOCK`, other syscall failures also reject, and errors carry their code, positive errno, and `syscall: 'flock'`. Native setup errors reject the same promise. Closing the last descriptor for the open file description releases the lock. The binding does not open, duplicate, close, or explicitly unlock descriptors.
+1 -1
View File
@@ -6,7 +6,7 @@ kind: "package-library"
[English](README.md) | 中文
根入口导出 Landlock 启动器路径、强制执行探测、授权参数和协议常量。独立的 `./flock` 入口导出 `tryLockExclusive(fd): Promise<void>`;导入任一入口都不会加载 `system.node`。
`./landlock-run` 入口导出 Landlock 启动器路径、强制执行探测、授权参数和协议常量。独立的 `./flock` 入口导出 `tryLockExclusive(fd): Promise<void>`;导入任一入口都不会加载 `system.node`。包不提供根导出。
锁操作异步尝试 `LOCK_EX | LOCK_NB`。在完成前保持调用方拥有的描述符打开;竞争以 `EAGAIN`/`EWOULDBLOCK` 拒绝,其他系统调用失败也会拒绝,错误携带 code、正 errno 和 `syscall: 'flock'`。原生调用准备阶段的错误也会拒绝同一个 promise。关闭该打开文件描述的最后一个描述符即释放锁。绑定不打开、复制、关闭或显式解锁描述符。
+1 -3
View File
@@ -8,10 +8,8 @@
"url": "git+https://github.com/deepseek-harness/deepseek-harness.git",
"directory": "native/system/packages/entry"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"./landlock-run": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
@@ -166,9 +166,13 @@ import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { grantArgs, launcherPath, probe } from '@deepseek-ai/node-addon-system';
import { grantArgs, launcherPath, probe } from '@deepseek-ai/node-addon-system/landlock-run';
import { tryLockExclusive } from '@deepseek-ai/node-addon-system/flock';
await assert.rejects(import('@deepseek-ai/node-addon-system'), {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
});
const requireLandlock = process.env.NALR_REQUIRE_LANDLOCK === '1';
const platformPackage = '@deepseek-ai/node-addon-system-' + process.platform + '-' + process.arch;
const resolved = launcherPath();
+5 -1
View File
@@ -15,7 +15,11 @@ import {
grantArgs,
launcherPath,
probe,
} from '@deepseek-ai/node-addon-system';
} from '@deepseek-ai/node-addon-system/landlock-run';
await assert.rejects(import('@deepseek-ai/node-addon-system'), {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
});
// --- constants are part of the CLI contract ---
assert.equal(LAUNCHER_BIN, 'landlock-run');
+1 -1
View File
@@ -22,7 +22,7 @@ import {
grantArgs,
launcherPath,
probe,
} from '@deepseek-ai/node-addon-system';
} from '@deepseek-ai/node-addon-system/landlock-run';
const FATAL_PREFIX = 'landlock-run: ';
const PARTIAL_NOTICE = 'landlock-run: partial enforcement (older Landlock ABI)';
+50 -2
View File
@@ -7,6 +7,54 @@ import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { verifyPlatformBinaries } from '../scripts/repo.mjs';
test('the real Landlock subpath imports without platform packages or dlopen, while the root is unexported', { timeout: 120_000 }, (t) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'system-landlock-entry-'));
t.after(() => fs.rmSync(dir, { recursive: true, force: true }), { timeout: 120_000 });
const entry = fileURLToPath(new URL('../packages/entry/', import.meta.url));
const installed = path.join(dir, 'node_modules', '@deepseek-ai', 'node-addon-system');
fs.mkdirSync(installed, { recursive: true });
fs.copyFileSync(path.join(entry, 'package.json'), path.join(installed, 'package.json'));
// Only the real entry payload is present; no platform package or addon is copied.
fs.cpSync(path.join(entry, 'lib'), path.join(installed, 'lib'), { recursive: true });
const manifest = JSON.parse(fs.readFileSync(path.join(installed, 'package.json'), 'utf8'));
assert.equal(manifest.main, undefined);
assert.equal(manifest.types, undefined);
const result = spawnSync(process.execPath, ['--no-addons', '--input-type=module', '--eval', `
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
const originalDlopen = process.dlopen;
let dlopenCalls = 0;
try {
process.dlopen = () => {
dlopenCalls++;
throw new Error('Landlock import attempted dlopen');
};
const api = await import('@deepseek-ai/node-addon-system/landlock-run');
assert.equal(api.LAUNCHER_BIN, 'landlock-run');
assert.deepEqual(api.grantArgs({}), []);
assert.equal(dlopenCalls, 0);
await assert.rejects(import('@deepseek-ai/node-addon-system'), {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
});
assert.throws(() => createRequire(import.meta.url).resolve('@deepseek-ai/node-addon-system'), {
code: 'ERR_PACKAGE_PATH_NOT_EXPORTED',
});
} finally {
process.dlopen = originalDlopen;
}
`], {
cwd: dir,
encoding: 'utf8',
timeout: 120_000,
env: Object.fromEntries(Object.entries(process.env)
.filter(([key]) => !/KEY|TOKEN|SECRET|PASSWORD|^NODE_PATH$/i.test(key))),
});
assert.equal(result.error, undefined);
assert.equal(result.signal, null, result.stderr);
assert.equal(result.status, 0, result.stderr);
});
// These minimal headers exercise format rejection, not executable behavior.
// flock.test.js and packed-install verification execute the real addon.
function fixture(t, { platform = 'linux', arch = 'x64', kind = 'node-api' } = {}) {
@@ -106,14 +154,14 @@ test('rejects a symbolic-link payload', { skip: process.platform === 'win32' },
assert.throws(() => verifyPlatformBinaries(f.dir), /not a regular file/);
});
test('entry prepack rejects a missing exported flock file even when the root entry exists', (t) => {
test('entry prepack rejects a missing exported flock file even when the Landlock entry exists', (t) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'system-entry-'));
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
fs.mkdirSync(path.join(dir, 'lib'));
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({
name: 'entry-fixture',
exports: {
'.': { types: './lib/index.d.ts', default: './lib/index.js' },
'./landlock-run': { types: './lib/index.d.ts', default: './lib/index.js' },
'./flock': { types: './lib/flock.d.ts', default: './lib/flock.js' },
},
}));
@@ -36,6 +36,7 @@ const repoRoot = fileURLToPath(new URL('../../../../', import.meta.url))
/** A leaf workspace package: real build output, no dependencies to drag in. */
const SUBJECT = '@deepseek-ai/dsh-timeout'
const LANDLOCK = '@deepseek-ai/node-addon-system'
const LANDLOCK_ENTRY = `${LANDLOCK}/landlock-run`
const PLUGIN_INVENTORY = '@deepseek-ai/dsh-plugin-package-inventory-deepseek'
const WEB_SERVER = '@deepseek-ai/dsh-host-webserver'
@@ -240,7 +241,7 @@ const archive = async (): Promise<Uint8Array> =>
expect(result.packages.has(LANDLOCK)).toBe(true)
expect(result.missing).toEqual([])
expect(Object.hasOwn(result.files, `node_modules/${LANDLOCK}/lib/index.js`)).toBe(true)
expect(createNodeBuiltins()[LANDLOCK]).toBeUndefined()
expect(createNodeBuiltins()[LANDLOCK_ENTRY]).toBeUndefined()
const vfs = loadVfsImage(await inflateImage(result.image, 'the packed Landlock package'), DEFAULT_ROOT)
const loader = new WorkerModuleLoader({
@@ -251,7 +252,7 @@ const archive = async (): Promise<Uint8Array> =>
})
setActiveVfs(vfs)
setActiveModuleLoader(loader)
const landlock = loader.requireFrom(`${DEFAULT_ROOT}/workspace`)(LANDLOCK) as {
const landlock = loader.requireFrom(`${DEFAULT_ROOT}/workspace`)(LANDLOCK_ENTRY) as {
LAUNCHER_BIN: string
LAUNCHER_FAILURE_EXIT: number
launcherPath(): string
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/experimental/webworker-runtime/README.md
README.md: 0bfac2891e14d7c6f9947c62acf968bef8220565
README.zh.md: b31fd325c79c82d3992a62cbe4c9fb8c5506211b
README.md: df61667329deacf99c320cd4121715115403d6f5
README.zh.md: 7faff0584764978ba30cb8f832adf25a98237500
@@ -50,7 +50,7 @@ None; this package neither assembles nor sends a provider request.
- **The worker composition writes plaintext session logs** (`compression: 'none'` boot patch): it carries no Zstandard codec, so exported logs are `.jsonl`, never `.jsonl.zstd`.
- **`node:dns/promises`, `node:vm`, `node:net`, `node:sqlite`, `node:worker_threads` are structural stubs**: every call reports its refusal on the console and throws. Rows needing native DNS, a real process, or realm isolation cannot run here.
- **Filesystem watchers observe only the mounted VFS**: image seeding is silent and the VFS has no symlinks or external writers. `persistent`, `ref()`, and `unref()` preserve the Node API but cannot control a dedicated Worker's lifetime because browsers expose no ref-counted event loop.
- **Worker confinement is a VFS boundary, not kernel Landlock**: `read-only` and `workspace-write` run the unchanged `@deepseek-ai/node-addon-system` JavaScript and launcher argv, but the process layer implements the logical `landlock-run` executable and enforces its grants on every shell filesystem request. `full` therefore covers the Worker command table and mounted VFS only; it does not claim arbitrary native-process execution or Linux kernel isolation.
- **Worker confinement is a VFS boundary, not kernel Landlock**: `read-only` and `workspace-write` run the unchanged `@deepseek-ai/node-addon-system/landlock-run` JavaScript and launcher argv, but the process layer implements the logical `landlock-run` executable and enforces its grants on every shell filesystem request. `full` therefore covers the Worker command table and mounted VFS only; it does not claim arbitrary native-process execution or Linux kernel isolation.
- **The worker bundle pins a path inside `@yarnpkg/parsers`** — the build resolves the package's own `lib/shell.js` instead of its root, whose barrel also re-exports the Syml parser and so drags js-yaml into a bundle that never parses that format (around 175 kB, plus its module body at worker start). The path is derived from the package manifest, so a layout change fails the build rather than reinstating the barrel; upgrading the dependency means re-checking that the shell parser still lives there.
- **The shell is not bash**: no loops, functions, `case`, job control, or process substitution — the grammar stops at pipelines, `&&`/`||`, subshells, groups, redirections, and expansion. `&` runs its command to completion in place, `sed` accepts only substitution scripts, patterns are JavaScript regular expressions, and the command table holds coreutils only (no `git`, no network tools).
- **A shell process has no synchronous filesystem**: it reads and writes the host's VFS by message, because blocking on a reply would need `SharedArrayBuffer`, which requires a cross-origin isolation GitHub Pages cannot grant. Directory-walking commands therefore cost one round trip per entry, and two concurrent commands can interleave their writes.
@@ -50,7 +50,7 @@ kind: "package-library"
- **worker 组合写明文会话日志**(`compression: 'none'` boot patch):不带 Zstandard 编解码器,导出日志是 `.jsonl`,不会是 `.jsonl.zstd`。
- **`node:dns/promises`、`node:vm`、`node:net`、`node:sqlite`、`node:worker_threads` 是结构化 stub**:每次调用在 console 报告拒绝并抛出。需要原生 DNS、真进程或真 realm 隔离的行在此无法运行。
- **文件 watcher 只能观察已挂载的 VFS**:镜像 seed 不产生事件,VFS 也没有符号链接或外部写入方。`persistent`、`ref()` 和 `unref()` 保留 Node API,但浏览器没有引用计数事件循环,因此这些接口不能控制 dedicated Worker 的生存期。
- **Worker confinement 是 VFS 边界,不是内核 Landlock**:`read-only` 和 `workspace-write` 运行未经修改的 `@deepseek-ai/node-addon-system` JavaScript 与 launcher argv,进程层则实现逻辑 `landlock-run` 可执行文件,并在 shell 的每次文件系统请求上执行其授权。`full` 仅覆盖 Worker 命令表和已挂载 VFS,不表示能够执行任意 native 进程,也不表示 Linux 内核隔离。
- **Worker confinement 是 VFS 边界,不是内核 Landlock**:`read-only` 和 `workspace-write` 运行未经修改的 `@deepseek-ai/node-addon-system/landlock-run` JavaScript 与 launcher argv,进程层则实现逻辑 `landlock-run` 可执行文件,并在 shell 的每次文件系统请求上执行其授权。`full` 仅覆盖 Worker 命令表和已挂载 VFS,不表示能够执行任意 native 进程,也不表示 Linux 内核隔离。
- **worker 束钉住了 `@yarnpkg/parsers` 的包内路径**——构建解析到该包自己的 `lib/shell.js` 而非包根,因为包根 barrel 还 re-export 了 Syml 解析器,会把 js-yaml 拖进一个从不解析该格式的束(约 175 kB,外加 worker 启动时的模块体求值)。该路径由包 manifest 派生,包内布局一变即构建期失败、不会静默退回 barrel;升级这个依赖时须复核 shell 解析器是否仍在那里。
- **这个 shell 不是 bash**:没有循环、函数、`case`、作业控制或进程替换——语法止步于管道、`&&`/`||`、子 shell、group、重定向与展开。`&` 会就地把命令跑完,`sed` 只接受替换脚本,模式是 JavaScript 正则,命令表只有 coreutils(没有 `git`,没有网络工具)。
- **shell 进程没有同步文件面**:它靠消息读写宿主的 VFS,因为阻塞等待回帧需要 `SharedArrayBuffer`,而那要求 GitHub Pages 给不了的跨源隔离。因此目录遍历类命令每个条目一次往返,并发的两条命令写入可以交错。
@@ -18,7 +18,7 @@ import { setActiveVfs } from '@deepseek-ai/dsh-experimental-webworker-runtime/sr
import { spawn, spawnSync } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/builtin_modules/implemented/child_process.ts'
import {
LAUNCHER_FAILURE_EXIT, grantArgs, launcherPath, probe,
} from '@deepseek-ai/node-addon-system'
} from '@deepseek-ai/node-addon-system/landlock-run'
import { processAlive, signalProcess } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/node/process-table.ts'
import { hostFileSystem } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/shell/fs-access.ts'
import {
@@ -119,6 +119,7 @@ describe('replaced external packages', () => {
it('lists the packages the loader serves from the bundle', () => {
expect(REPLACED_EXTERNAL_PACKAGES).not.toContain('chokidar')
expect(REPLACED_EXTERNAL_PACKAGES).not.toContain('@deepseek-ai/node-addon-system')
expect(REPLACED_EXTERNAL_PACKAGES).not.toContain('@deepseek-ai/node-addon-system/landlock-run')
expect(REPLACED_EXTERNAL_PACKAGES).toContain('ws')
})
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/sandbox/sandbox-local/README.md
README.md: 16deeef790f28b232dc517784ca45ecb38749c6f
README.zh.md: 4d163766ff6c8097c60cfb8376a21d2a9a441100
README.md: 33853b68407c2d3294ad1323e47b5473419b0e12
README.zh.md: 8d62c20c65e2d7555844a1ba81e13694c5c6a8df
+1 -1
View File
@@ -74,7 +74,7 @@ Selection is by platform first, probes second: each platform has a runner chain
The bwrap profile combines a read-only host root, a fresh `/dev`, and `/proc` from a private PID namespace — commands manage their descendants but cannot see host processes, so procfs magic links cannot bypass the mounts; `workspace-write` adds an ephemeral `/tmp` and a writable workspace bind. The [private-PID note](../../../.agents/notes/implemented/bug-fix/2026-08-06-bwrap-private-pid-namespace.md) records the boundary.
The Landlock launcher ships as an npm-distributed native addon (`@deepseek-ai/node-addon-system`) that supplies the platform launcher, functional probe, and grant vocabulary; this provider maps mode to grants only, keeping path resolution and probe parsing with the versioned binary.
The `@deepseek-ai/node-addon-system/landlock-run` API supplies the platform launcher, functional probe, and grant vocabulary; this provider maps mode to grants only, keeping path resolution and probe parsing with the versioned binary.
The Seatbelt profile is allow-default with `(deny file-write*)` plus write allow-lists derived from the shared `writableRoots` helper, so exactly the mode's promised file effects are governed; every root is canonicalized because Seatbelt matches resolved paths (`/tmp` IS `/private/tmp`).
+1 -1
View File
@@ -74,7 +74,7 @@ kind: "package-reference"
bwrap profile 组合只读宿主根目录、全新 `/dev` 与私有 PID 命名空间中的 `/proc`——命令可管理其后代,但看不到宿主进程,因此 procfs 魔法链接无法绕过挂载;`workspace-write` 另加临时的 `/tmp` 与可写工作区绑定挂载。[私有 PID 笔记](../../../.agents/notes/implemented/bug-fix/2026-08-06-bwrap-private-pid-namespace.zh.md)记录该边界。
Landlock launcher 以 npm 分发的原生插件(`@deepseek-ai/node-addon-system`)提供平台 launcher、功能探测与授权词汇;此提供方只做模式到授权的映射,把路径解析与探测解析保留在带版本的 binary 中。
`@deepseek-ai/node-addon-system/landlock-run` API 提供平台 launcher、功能探测与授权词汇;此提供方只做模式到授权的映射,把路径解析与探测解析保留在带版本的 binary 中。
Seatbelt profile 默认允许,带 `(deny file-write*)` 与来自共享 `writableRoots` 辅助函数的写入 allow-list,因此恰好管辖模式承诺的文件操作;每个根目录都经过规范化,因为 Seatbelt 匹配解析后的路径(`/tmp` 就是 `/private/tmp`)。
+1 -1
View File
@@ -30,7 +30,7 @@ import {
LAUNCHER_FAILURE_EXIT,
launcherPath as landlockLauncherPath,
probe as defaultProbeLandlock,
} from '@deepseek-ai/node-addon-system'
} from '@deepseek-ai/node-addon-system/landlock-run'
import { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { SandboxProvider, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
@@ -4,7 +4,7 @@
* @module @deepseek-ai/dsh-sandbox-local/profiles
*/
import { grantArgs as landlockGrantArgs } from '@deepseek-ai/node-addon-system'
import { grantArgs as landlockGrantArgs } from '@deepseek-ai/node-addon-system/landlock-run'
import { writableRoots } from '@deepseek-ai/dsh-sandbox'
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
@@ -6,7 +6,7 @@ import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
import { launcherPath } from '@deepseek-ai/node-addon-system'
import { launcherPath } from '@deepseek-ai/node-addon-system/landlock-run'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
/**
@@ -12,7 +12,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system'
import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system/landlock-run'
import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
import {
@@ -109,7 +109,7 @@ describe.skipIf(!packable)('sandbox-local: packed-tarball distribution (publish-
import { spawnSync } from 'node:child_process'
import { existsSync } from 'node:fs'
import { Context } from '@deepseek-ai/cordis'
import { launcherPath } from '@deepseek-ai/node-addon-system'
import { launcherPath } from '@deepseek-ai/node-addon-system/landlock-run'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
const ctx = new Context()
await ctx.plugin(LocalSandboxProvider, {})
@@ -5,7 +5,7 @@ import { homedir, tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import { launcherPath } from '@deepseek-ai/node-addon-system'
import { launcherPath } from '@deepseek-ai/node-addon-system/landlock-run'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
@@ -10,7 +10,7 @@ import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system'
import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-system/landlock-run'
import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
+1 -1
View File
@@ -37,7 +37,7 @@
"@deepseek-ai/cordis-plugin-timer": ["./vendor/timer/src"],
"@deepseek-ai/cordis-plugin-hmr": ["./vendor/hmr/src"],
"@deepseek-ai/cordis-plugin-logger-console": ["./vendor/logger-console/src"],
"@deepseek-ai/node-addon-system": ["./native/system/packages/entry/src/index.ts"],
"@deepseek-ai/node-addon-system/landlock-run": ["./native/system/packages/entry/src/index.ts"],
"@deepseek-ai/node-addon-system/flock": ["./native/system/packages/entry/src/flock.ts"],
"@deepseek-ai/dsh-invariants": ["./packages/runtime-diagnostics/invariants/src/index.ts"],
"@deepseek-ai/dsh-tool-call-timeout-policy": ["./packages/guard/timeout-policy/src"],