diff --git a/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md b/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md new file mode 100644 index 0000000000..71d3bde2ec --- /dev/null +++ b/docs/architecture/managed-dependency-storage-authority-v1.zh-CN.md @@ -0,0 +1,147 @@ +--- +document_status: implementation-contract +status: draft-stacked-foundation +date: 2026-08-08 +milestone: M1.3-storage-authority +base: upstream/main@08bcf324b +--- + +# Managed Dependency Storage Authority v1 + +## 1. 本 PR 只证明一个不变量 + +> 同一个 canonical dependency environment identity 最多对应一棵由 Maka 发布、由 artifact 权限域之外的 durable receipt 证明、可在崩溃后收敛重开的依赖树;任何身份、路径、内容或平台证据不可证明时 fail closed。 + +本 PR 的 owner 是 `ManagedDependencyEnvironmentAuthority`。它拥有 environment identity、artifact publication、SQLite receipt、lease、pending reservation 与 GC。注入的 producer 只获得一次性 staging 中的 `outputRoot` 与 `scratchRoot`,不获得 storage root、canonical artifact path 或 receipt database。 + +同一个 canonical storage root 在同一时刻只能存在一个该 authority:同进程由 module-private owner claim 拒绝重复实例,跨进程由稳定 lock artifact 上的 OS exclusive lock 拒绝第二个 owner。lock 在 receipt database 或 artifact 被读取、修复、发布、租用、GC 之前取得,只在 authority 完整关闭或初始化失败时释放;active lease 导致的 close 拒绝不会释放 owner。 + +本 PR 不包含: + +- bundled npm 的实现与网络策略; +- `node_modules/.bin` symlink 的 producer 配额扫描; +- ManagedWorkspaceOwner/worker logical binding; +- Desktop/CLI/Runtime Host 接线; +- release packaging、audit 或 SBOM。 + +因此该 PR 是 stacked foundation,在出现下一个生产 owner consumer 前保持 Draft;不得单独宣称 M1.3 用户能力闭环。 + +## 2. 权威与原子性边界 + +权威事实存放在 artifact 域之外的专用 SQLite database: + +```text +managed-workspaces/dependency-environment-authority-v1.sqlite +``` + +artifact 目录只允许包含 `node_modules/`,不能携带同目录 receipt。发布顺序固定为: + +```text +canonical identity validation + -> process owner claim + cross-process OS lock + -> pending reservation + -> producer-owned random staging + -> producer provision resolves (PR 2 must prove its process tree has exited) + -> deep-copy node_modules into new authority-owned inodes + -> delete the producer-owned tree + -> one authoritative tree seal: + hash every entry + fsync every regular file + + fsync directories bottom-up + + ADS/reparse validation + -> fsync artifact staging root + -> atomic rename artifact + -> fsync publication parent + -> SQLite receipt transaction (synchronous=FULL) + -> reopen and revalidate artifact + receipt + -> lease +``` + +filesystem rename 与 SQLite 不是共同事务,恢复依靠可收敛状态而非假装原子: + +- artifact 存在、receipt 不存在:删除 orphan artifact,重新物化; +- receipt 存在、artifact 不存在:删除 orphan receipt; +- 两者都存在且一致:重新验证后复用; +- 两者都存在但不一致:fail closed,不自动接受或覆盖。 + +## 3. 身份、路径和平台规则 + +- authority 根据 manifest/lockfile bytes、package manager、Node ABI、platform/arch、producer runtime/policy 重新计算 identity;调用者提供的 `environmentId` 不能自证。 +- publication digest 必须是固定 64 位小写十六进制 SHA-256,所有 join/realpath/rename 后重新检查 containment。 +- Linux/macOS 的相对 symlink 只有在目标仍位于 dependency root 内时允许,link path/target 进入 tree digest;绝对或逃逸 link 拒绝。 +- Linux/macOS 上 rename 不会撤销 producer 保留的 writable file descriptor,因此 producer tree 绝不直接 rename 成 artifact。authority 必须复制到新 inode、删除 producer tree,再做完整内容证明;旧 descriptor 后续只能修改已 unlink 的 producer inode。 +- Windows 拒绝 symlink/reparse point,并通过系统绝对路径 PowerShell 枚举、拒绝 NTFS named stream。 +- 每次 acquisition 都重新验证完整内容树。不能用目录 mtime 作为终裁,因为内容变化不保证可靠改变父目录 mtime;性能优化不能削弱内容证明。 + +所有平台的 publication 顺序都要求:每个 authority-owned 普通文件先完成平台可用的同步,内部目录按子目录到父目录的顺序尝试同步,随后同步 staging root;rename 后再同步 publication parent,最后才允许提交 SQLite receipt。tree hash 与 durable seal 必须由同一个遍历 primitive 完成,不能维护两套可能漂移的目录语义。 + +Linux 只有在文件系统和硬件兑现 file/directory `fsync` 合同时,才承诺上述顺序覆盖断电恢复。macOS 的普通 `fsync` 不等价于 `F_FULLFSYNC`,而本协议也没有同时为 artifact 与 SQLite receipt 启用并证明 full-sync ordering;Windows 的 Node 文件系统接口同样不能提供与 POSIX 等价的目录项持久性证明。因此 macOS 与 Windows v1 都只承诺**进程崩溃收敛**,不承诺断电后的 artifact publication 自动收敛。断电后若 artifact/receipt 不一致,必须 fail closed,并通过删除缓存后重新物化或人工清理恢复。绿色的 child-process crash test 不能充当 power-loss 证明。 + +Windows 上只读 dependency file 是合法输入。authority 只在自己的 unpublished staging inode 上临时增加 owner-write 权限以完成文件同步,并在继续 publication 前恢复原始 mode;producer inode 与最终 published mode 都不得被永久改写。任何同步或权限恢复失败都必须在 receipt 前 fail closed。 + +## 4. Lease、配额与失败状态 + +- authority 生命周期为 `open -> draining -> closed`。`close()` 先拒绝新的 acquisition,再等待所有已接纳 acquisition 完成 lease 安装或失败;完成后若存在 active lease,则 close 明确拒绝并恢复 `open`,不能先关闭 receipt owner 再返回活 lease。 +- acquire 开始即安装 pending reservation,直到正式 lease 建立或失败清理;GC 不得删除 pending/inflight/leased digest。 +- cache cost 为内容字节数加每个 entry 的固定治理成本,避免大量空文件绕过软配额。 +- GC 串行执行;一次 GC rejection 可报告给当前调用者,但不能永久毒化后续 GC task chain。 +- authority startup 遇到 malformed receipt 或 unowned cache entry 时整体 fail closed。Quarantine 会引入新的 durable lifecycle,不在本 PR 静默增加。 + +## 5. Crash 与对抗矩阵 + +| 场景 | 唯一合法结果 | +| ------------------------------------ | -------------------------------------- | +| producer 中断 | staging 清理,无 artifact/receipt | +| 完整 tree durable seal 前中断 | 无 receipt;重启清理 staging/orphan | +| tree seal 完成、artifact rename 前中断 | 无 receipt;重启清理 staging/orphan | +| artifact rename 后进程退出 | 重启删除无 receipt artifact,再物化 | +| receipt commit 后进程退出 | 重启重验并复用 | +| Linux receipt 前断电 | receipt 不得领先已同步的完整 artifact tree | +| macOS/Windows publication 期间断电 | v1 不承诺自动收敛;不一致时 fail closed | +| Windows dependency file 为只读 | staging 临时提权同步后恢复 mode,再继续发布 | +| artifact 与同目录伪 receipt 一起修改 | 外部 SQLite receipt 不变,acquire 拒绝 | +| 伪造 environmentId/path traversal | T1/任何文件写入前拒绝 | +| Windows ADS/reparse | publish/reopen 拒绝 | +| acquisition pending 时 GC | pending digest 保留 | +| cache 中任意已发布内容漂移 | 新 acquisition fail closed | +| 同进程第二个 authority | 接触 receipt/artifact/GC 前拒绝 | +| 另一进程持有同 root authority | OS lock 处拒绝;不得观察或删除其 lease | +| close 与 lease 安装并发 | drain 后因 active lease 拒绝 close | +| POSIX producer 保留 output writable fd | late write 不得改变 authority-owned artifact | + +## 6. Extraction ledger + +来源仅作为实现证据,不继承集成分支历史: + +| 文件 | PR 1 归属 | 来源/处理 | +| --------------------------------------------------------------------------------------- | ------------------ | ------------------------------------------------------ | +| `packages/storage/src/managed-dependency-environment.ts` | authority owner | 从 #2307 storage commits 手工提取,删除无消费者 helper | +| `packages/storage/src/__tests__/managed-dependency-environment.test.ts` | identity/tamper/GC | 测试先迁移并在 main 上 RED | +| `packages/storage/src/__tests__/managed-dependency-environment-crash.test.ts` | crash convergence | production-shaped child-process crash | +| `packages/storage/src/__tests__/fixtures/managed-dependency-environment-crash-child.ts` | crash fixture | 仅服务上述 crash contract | +| `packages/storage/src/__tests__/fixtures/managed-dependency-environment-owner-child.ts` | owner fixture | 持有真实跨进程 owner lock,验证第二 owner 被拒绝 | +| producer/runtime-host/Desktop/release files | 不属于 PR 1 | 留给后续平铺 PR,不迁移 | + +旧集成分支 `codex/managed-workspace-environment-provisioning-m1-3` 只作为实现来源。提交按不变量拆解如下,任何混合提交都不得整体 cherry-pick: + +| 来源提交 | 原提交主题 | 平铺归属 | 处理方式 | +| ----------- | ------------------------------------------------------- | -------- | ------------------------------------------------------------------------ | +| `afb798092` | `feat(storage): add managed dependency artifact authority` | PR 1 | 迁移 authority、identity、receipt、crash fixture 与最小测试 | +| `57aba6bb7` | `fix(runtime): bind managed dependencies to producer authority` | PR 1/4 | 仅提取 storage authority 的 producer capability/identity 硬化;runtime consumer 留给 PR 4 | +| `cff6fc476` | `fix(storage): harden managed dependency authority` | PR 1 | 提取外置 SQLite receipt、路径/平台校验、pending/GC 与对抗测试 | +| `89c9e0a3c` | `feat(runtime-host): ship verified bundled npm environments` | PR 2/3/4 | producer 隔离归 PR 2,bundled runtime 供应链归 PR 3,host 组装归 PR 4 | +| `8150e90a7` | `fix(runtime-host): constrain bundled npm provisioning` | PR 2/3 | provision policy/quota 归 PR 2,shipped runtime verification 归 PR 3 | +| `27f8f6b8e` | `fix(release): verify shipped bundled npm closure` | PR 3 | release audit、manifest、notice 与 packaged smoke | +| `16e10b3c5` | `feat(storage): bind dependency environments to managed scopes` | PR 4 | owner-bound lease、logical binding 与 read-only worker consumer | +| `9a42a761c` | `fix(runtime-host): transport packaged dependency authority` | PR 4 | Desktop/CLI/Runtime Host 生命周期与关闭顺序 | +| `980143a3b` | `docs(architecture): split M1.3 authority boundaries` | 文档证据 | 不迁移旧总文档;由每个平铺 PR 维护自己的 owner/rollback 合同 | + +后续平铺顺序固定为: + +1. PR 1:本文件定义的 storage authority; +2. PR 2:producer boundary,必须证明 producer 进程树退出后 `provision()` 才 resolve,并包含真实 npm `.bin` symlink 的配额与取消/超时测试; +3. PR 3:bundled npm runtime 供应链、发布审计与许可证材料; +4. PR 4:唯一生产 consumer,把真实 baseline、environment lease 和 Read/Glob/Grep worker 串成闭环。 + +PR 1–3 只是堆叠地基;PR 4 合并前不能把 M1.3 描述为用户可用。PR 2 不得通过拒绝所有 POSIX symlink 规避 `.bin`:producer inventory 应计量合法 link,最终 storage authority 与 worker 仍负责 target containment;Windows 继续拒绝 reparse point。 + +验证时必须执行 storage build、focused authority tests、真实 Windows ADS test,以及三个 child-process crash failpoint。后续 PR 不得通过放宽本 authority 的 fail-closed 规则来提高采用率。 diff --git a/packages/storage/src/__tests__/fixtures/managed-dependency-environment-crash-child.ts b/packages/storage/src/__tests__/fixtures/managed-dependency-environment-crash-child.ts new file mode 100644 index 0000000000..b85b9ff717 --- /dev/null +++ b/packages/storage/src/__tests__/fixtures/managed-dependency-environment-crash-child.ts @@ -0,0 +1,64 @@ +import { mkdir, writeFile } from 'node:fs/promises'; +import { + computeManagedDependencyEnvironmentIdentity, + createManagedDependencyEnvironmentAuthority, + createManagedDependencyEnvironmentProducerCapability, + type ManagedDependencyEnvironmentFailpoint, +} from '../../managed-dependency-environment.js'; + +const storageRoot = process.env.MAKA_DEPENDENCY_CRASH_ROOT; +const failpoint = process.env.MAKA_DEPENDENCY_CRASH_POINT as + | ManagedDependencyEnvironmentFailpoint + | 'during_environment_provision' + | undefined; +if (!storageRoot || !failpoint) throw new Error('Missing dependency crash fixture input'); + +const producerCapability = createManagedDependencyEnvironmentProducerCapability( + `sha256:${'a'.repeat(64)}`, +); + +const source = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: producerCapability.runtimeIdentitySha256, + producerPolicyIdentitySha256: producerCapability.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, +}; +const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer: { + capability: producerCapability, + packageManagerName: 'npm', + packageManagerVersion: '11.12.1', + nodeRuntime: { + version: '24.7.0', + abi: '137', + platform: process.platform, + arch: process.arch, + }, + async provision(input) { + await mkdir(joinPath(input.outputRoot, 'fixture-package'), { + recursive: true, + }); + await writeFile(joinPath(input.outputRoot, 'fixture-package', 'index.js'), 'safe\n'); + if (failpoint === 'during_environment_provision') process.exit(73); + }, + }, + failpoint(point) { + if (point === failpoint) process.exit(73); + }, +}); +await authority.acquire(computeManagedDependencyEnvironmentIdentity(source), source); +throw new Error('Crash failpoint was not reached'); + +function joinPath(...parts: string[]): string { + return parts.join(process.platform === 'win32' ? '\\' : '/'); +} diff --git a/packages/storage/src/__tests__/fixtures/managed-dependency-environment-owner-child.ts b/packages/storage/src/__tests__/fixtures/managed-dependency-environment-owner-child.ts new file mode 100644 index 0000000000..80592aa649 --- /dev/null +++ b/packages/storage/src/__tests__/fixtures/managed-dependency-environment-owner-child.ts @@ -0,0 +1,28 @@ +import { + createManagedDependencyEnvironmentAuthority, + createManagedDependencyEnvironmentProducerCapability, +} from '../../managed-dependency-environment.js'; + +const storageRoot = process.env.MAKA_DEPENDENCY_OWNER_ROOT; +if (!storageRoot) throw new Error('Missing dependency owner fixture root'); + +const producerCapability = createManagedDependencyEnvironmentProducerCapability( + `sha256:${'a'.repeat(64)}`, +); +await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer: { + capability: producerCapability, + packageManagerName: 'npm', + packageManagerVersion: '11.12.1', + nodeRuntime: { + version: '24.7.0', + abi: '137', + platform: process.platform, + arch: process.arch, + }, + async provision() {}, + }, +}); +process.stdout.write('READY\n'); +setInterval(() => undefined, 1_000); diff --git a/packages/storage/src/__tests__/managed-dependency-environment-crash.test.ts b/packages/storage/src/__tests__/managed-dependency-environment-crash.test.ts new file mode 100644 index 0000000000..74be44798f --- /dev/null +++ b/packages/storage/src/__tests__/managed-dependency-environment-crash.test.ts @@ -0,0 +1,186 @@ +import assert from 'node:assert/strict'; +import { execFile, spawn, type ChildProcess } from 'node:child_process'; +import { mkdtemp, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { promisify } from 'node:util'; +import test from 'node:test'; +import { + computeManagedDependencyEnvironmentIdentity, + createManagedDependencyEnvironmentAuthority, + createManagedDependencyEnvironmentProducerCapability, + type ManagedDependencyEnvironmentFailpoint, +} from '../managed-dependency-environment.js'; + +const execFileAsync = promisify(execFile); +const producerCapability = createManagedDependencyEnvironmentProducerCapability( + `sha256:${'a'.repeat(64)}`, +); +const childEntrypoint = fileURLToPath( + new URL('./fixtures/managed-dependency-environment-crash-child.js', import.meta.url), +); +const ownerChildEntrypoint = fileURLToPath( + new URL('./fixtures/managed-dependency-environment-owner-child.js', import.meta.url), +); + +test('rejects a second authority for the same storage root in another process', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-owner-process-')); + const child = spawn(process.execPath, [ownerChildEntrypoint], { + env: { ...process.env, MAKA_DEPENDENCY_OWNER_ROOT: storageRoot }, + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: true, + }); + t.after(async () => { + if (child.exitCode === null) child.kill(); + if (child.exitCode === null) await onceChildExit(child); + await rm(storageRoot, { recursive: true, force: true }); + }); + await waitForChildReady(child); + + const second = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer: { + capability: producerCapability, + packageManagerName: 'npm', + packageManagerVersion: '11.12.1', + nodeRuntime: { + version: '24.7.0', + abi: '137', + platform: process.platform, + arch: process.arch, + }, + async provision() {}, + }, + }).then( + (authority) => ({ authority }), + (error: unknown) => ({ error }), + ); + if ('authority' in second) { + await second.authority.close(); + assert.fail('a second process acquired the same dependency authority'); + } + assert.match(String(second.error), /already has an active owner/u); +}); + +for (const failpoint of [ + 'during_environment_provision', + 'after_environment_tree_durable', + 'after_environment_receipt_durable', + 'after_environment_publish', +] as const satisfies readonly ( + | ManagedDependencyEnvironmentFailpoint + | 'during_environment_provision' +)[]) { + test(`converges after process exit at ${failpoint}`, async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-crash-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + await assert.rejects( + execFileAsync(process.execPath, [childEntrypoint], { + env: { + ...process.env, + MAKA_DEPENDENCY_CRASH_ROOT: storageRoot, + MAKA_DEPENDENCY_CRASH_POINT: failpoint, + }, + windowsHide: true, + }), + (error: unknown) => error instanceof Error && 'code' in error && Number(error.code) === 73, + ); + + let provisionCalls = 0; + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer: { + capability: producerCapability, + packageManagerName: 'npm', + packageManagerVersion: '11.12.1', + nodeRuntime: { + version: '24.7.0', + abi: '137', + platform: process.platform, + arch: process.arch, + }, + async provision(input) { + provisionCalls += 1; + await mkdir(join(input.outputRoot, 'fixture-package'), { + recursive: true, + }); + await writeFile(join(input.outputRoot, 'fixture-package', 'index.js'), 'safe\n'); + }, + }, + }); + const source = dependencySource(); + const lease = await authority.acquire( + computeManagedDependencyEnvironmentIdentity(source), + source, + ); + assert.equal( + await readFile(join(lease.dependencyRoot, 'fixture-package', 'index.js'), 'utf8'), + 'safe\n', + ); + assert.equal(provisionCalls, failpoint === 'after_environment_receipt_durable' ? 0 : 1); + assert.deepEqual( + await readdir(join(storageRoot, 'managed-workspaces', 'dependency-environments', '.staging')), + [], + ); + await lease.release(); + await authority.close(); + }); +} + +function dependencySource() { + return { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: producerCapability.runtimeIdentitySha256, + producerPolicyIdentitySha256: producerCapability.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; +} + +async function waitForChildReady(child: ChildProcess): Promise { + await new Promise((resolve, reject) => { + let output = ''; + const timeout = setTimeout(() => finish(new Error('owner child did not become ready')), 15_000); + const finish = (error?: Error) => { + clearTimeout(timeout); + child.stdout?.off('data', onData); + child.stderr?.off('data', onErrorData); + child.off('error', onError); + child.off('exit', onExit); + if (error) reject(error); + else resolve(); + }; + const onData = (chunk: Buffer) => { + output += chunk.toString('utf8'); + if (output.includes('READY\n')) finish(); + }; + const onErrorData = (chunk: Buffer) => finish(new Error(chunk.toString('utf8'))); + const onError = (error: Error) => finish(error); + const onExit = (code: number | null) => finish(new Error(`owner child exited early: ${code}`)); + child.stdout?.on('data', onData); + child.stderr?.on('data', onErrorData); + child.on('error', onError); + child.on('exit', onExit); + }); +} + +async function onceChildExit(child: ChildProcess): Promise { + if (child.exitCode !== null) return; + await new Promise((resolve) => { + const onExit = () => resolve(); + child.once('exit', onExit); + if (child.exitCode !== null) { + child.off('exit', onExit); + resolve(); + } + }); +} diff --git a/packages/storage/src/__tests__/managed-dependency-environment.test.ts b/packages/storage/src/__tests__/managed-dependency-environment.test.ts new file mode 100644 index 0000000000..d214538030 --- /dev/null +++ b/packages/storage/src/__tests__/managed-dependency-environment.test.ts @@ -0,0 +1,757 @@ +import assert from 'node:assert/strict'; +import { + access, + chmod, + type FileHandle, + mkdtemp, + mkdir, + open, + readFile, + rm, + stat, + symlink, + writeFile, +} from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { test } from 'node:test'; +import { + computeManagedDependencyEnvironmentIdentity, + createManagedDependencyEnvironmentAuthority, + createManagedDependencyEnvironmentProducerCapability, +} from '../managed-dependency-environment.js'; + +const FIXTURE_PRODUCER_RUNTIME_IDENTITY = `sha256:${'a'.repeat(64)}` as const; +const FIXTURE_PRODUCER_CAPABILITY = createManagedDependencyEnvironmentProducerCapability( + FIXTURE_PRODUCER_RUNTIME_IDENTITY, +); + +test('computes one shared environment identity for equivalent dependency inputs', () => { + const input = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: 'linux' as const, + arch: 'x64' as const, + producerRuntimeIdentitySha256: `sha256:${'1'.repeat(64)}` as const, + producerPolicyIdentitySha256: `sha256:${'2'.repeat(64)}` as const, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + + const first = computeManagedDependencyEnvironmentIdentity(input); + const second = computeManagedDependencyEnvironmentIdentity({ ...input }); + + assert.match(first.environmentId, /^sha256:[0-9a-f]{64}$/); + assert.equal(first.environmentId, second.environmentId); + assert.equal(first.manifestSha256, second.manifestSha256); + assert.equal(first.lockfileSha256, second.lockfileSha256); + assert.notEqual( + first.environmentId, + computeManagedDependencyEnvironmentIdentity({ ...input, nodeAbi: '138' }).environmentId, + ); + assert.notEqual( + first.environmentId, + computeManagedDependencyEnvironmentIdentity({ + ...input, + platform: 'darwin', + }).environmentId, + ); + assert.notEqual( + first.environmentId, + computeManagedDependencyEnvironmentIdentity({ + ...input, + producerRuntimeIdentitySha256: `sha256:${'3'.repeat(64)}`, + }).environmentId, + ); + assert.notEqual( + first.environmentId, + computeManagedDependencyEnvironmentIdentity({ + ...input, + producerPolicyIdentitySha256: `sha256:${'4'.repeat(64)}`, + }).environmentId, + ); +}); + +test('rejects a producer that does not declare the exact hermetic capability', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-capability-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + + await assert.rejects( + createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer: { + capability: { + ...FIXTURE_PRODUCER_CAPABILITY, + network: 'unrestricted' as never, + }, + packageManagerName: 'npm', + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision() {}, + }, + }), + /producer capability is invalid/u, + ); +}); + +test('rejects a second authority for the same storage root in one process', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-single-owner-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision() {}, + }; + const first = await createManagedDependencyEnvironmentAuthority({ storageRoot, producer }); + + const second = await createManagedDependencyEnvironmentAuthority({ storageRoot, producer }).then( + (authority) => ({ authority }), + (error: unknown) => ({ error }), + ); + if ('authority' in second) { + await second.authority.close(); + assert.fail('a second authority acquired the same storage root'); + } + assert.match(String(second.error), /already has an active owner/u); + await first.close(); +}); + +test('rejects a published environment whose dependency content was modified', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-tamper-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + await mkdir(join(input.outputRoot, 'fixture-package'), { + recursive: true, + }); + await writeFile(join(input.outputRoot, 'fixture-package', 'index.js'), 'trusted\n', 'utf8'); + }, + }; + const identityInput = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + const identity = computeManagedDependencyEnvironmentIdentity(identityInput); + const firstAuthority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + const lease = await firstAuthority.acquire(identity, identityInput); + await writeFile(join(lease.dependencyRoot, 'fixture-package', 'index.js'), 'tampered\n', 'utf8'); + await lease.release(); + await firstAuthority.close(); + + const reopened = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + await assert.rejects(reopened.acquire(identity, identityInput), /does not match its receipt/u); + await reopened.close(); +}); + +test('keeps the receipt in a constrained authority outside the producer-owned artifact domain', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-receipt-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + await writeFile(join(input.outputRoot, 'index.js'), 'trusted\n', 'utf8'); + }, + }; + const source = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + const lease = await authority.acquire(identity, source); + const artifactRoot = dirname(lease.dependencyRoot); + const authorityDatabasePath = join( + storageRoot, + 'managed-workspaces', + 'dependency-environment-authority-v1.sqlite', + ); + await lease.release(); + await assert.rejects(readFile(join(artifactRoot, 'environment-receipt.json'), 'utf8'), { + code: 'ENOENT', + }); + await access(authorityDatabasePath); + const reopened = await authority.acquire(identity, source); + await reopened.release(); + await authority.close(); +}); + +test('rejects a coordinated artifact and co-located receipt rewrite', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-coordinated-tamper-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + await writeFile(join(input.outputRoot, 'index.js'), 'trusted\n', 'utf8'); + }, + }; + const source = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + const lease = await authority.acquire(identity, source); + const artifactRoot = dirname(lease.dependencyRoot); + await lease.release(); + await authority.close(); + + await writeFile(join(lease.dependencyRoot, 'index.js'), 'malicious\n', 'utf8'); + await writeFile( + join(artifactRoot, 'environment-receipt.json'), + `${JSON.stringify({ environmentId: identity.environmentId, contentTreeSha256: 'forged' })}\n`, + 'utf8', + ); + + const reopened = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + await assert.rejects( + reopened.acquire(identity, source), + /artifact contains an unowned entry|content does not match its receipt/u, + ); + await reopened.close(); +}); + +test('rejects an environment id that is not the digest of the requested identity', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-identity-forgery-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision() {}, + }; + const source = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + await assert.rejects( + authority.acquire({ ...identity, environmentId: `sha256:${'f'.repeat(64)}` }, source), + /identity is not canonical/u, + ); + await assert.rejects( + authority.acquire( + { ...identity, environmentId: 'sha256:../../escaped' } as typeof identity, + source, + ), + /identity is not canonical/u, + ); + await assert.rejects(access(join(storageRoot, 'escaped')), { + code: 'ENOENT', + }); + await authority.close(); +}); + +test('rejects an NTFS alternate stream created inside a dependency artifact', { + skip: process.platform !== 'win32', +}, async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-ads-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + const target = join(input.outputRoot, 'index.js'); + await writeFile(target, 'trusted\n', 'utf8'); + await writeFile(`${target}:unhashed`, 'malicious\n', 'utf8'); + }, + }; + const source = dependencySourceForName('ads'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + await assert.rejects(authority.acquire(identity, source), /alternate data stream/u); + await authority.close(); +}); + +test('accepts a POSIX package bin symlink whose target remains inside the dependency root', { + skip: process.platform === 'win32', +}, async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-bin-link-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + await mkdir(join(input.outputRoot, 'fixture-package'), { + recursive: true, + }); + await mkdir(join(input.outputRoot, '.bin'), { recursive: true }); + await writeFile(join(input.outputRoot, 'fixture-package', 'cli.js'), 'trusted\n', 'utf8'); + await symlink('../fixture-package/cli.js', join(input.outputRoot, '.bin', 'fixture-cli')); + }, + }; + const source = dependencySourceForName('posix-bin-link'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + }); + + const lease = await authority.acquire(identity, source); + assert.equal( + await readFile(join(lease.dependencyRoot, '.bin', 'fixture-cli'), 'utf8'), + 'trusted\n', + ); + await lease.release(); + await authority.close(); +}); + +test('publishes authority-owned file inodes instead of producer-owned inodes', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-inode-handoff-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + let producerFileIdentity: { dev: bigint; ino: bigint } | undefined; + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + const producerFile = join(input.outputRoot, 'payload'); + await writeFile(producerFile, 'trusted\n', 'utf8'); + const producerInfo = await stat(producerFile, { bigint: true }); + producerFileIdentity = { dev: producerInfo.dev, ino: producerInfo.ino }; + }, + }; + const source = dependencySourceForName('inode-handoff'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ storageRoot, producer }); + + const lease = await authority.acquire(identity, source); + const publishedInfo = await stat(join(lease.dependencyRoot, 'payload'), { bigint: true }); + assert.notDeepEqual( + { dev: publishedInfo.dev, ino: publishedInfo.ino }, + producerFileIdentity, + 'published content retained the producer-owned inode', + ); + await lease.release(); + await authority.close(); +}); + +test('seals the complete authority-owned tree before publishing its receipt', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-durable-tree-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + const observedBoundaries: string[] = []; + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + await mkdir(join(input.outputRoot, 'nested', 'package'), { recursive: true }); + await writeFile(join(input.outputRoot, 'root.js'), 'root\n', 'utf8'); + await writeFile(join(input.outputRoot, 'nested', 'package', 'index.js'), 'nested\n', 'utf8'); + }, + }; + const source = dependencySourceForName('durable-tree'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + failpoint(point) { + observedBoundaries.push(point); + }, + }); + + const lease = await authority.acquire(identity, source); + assert.deepEqual(observedBoundaries, [ + 'after_environment_tree_durable', + 'after_environment_publish', + 'after_environment_receipt_durable', + 'before_environment_lease', + ]); + await lease.release(); + await authority.close(); +}); + +test('publishes Windows read-only dependency files without changing their final mode', { + skip: process.platform !== 'win32', +}, async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-readonly-file-')); + let publishedFile: string | undefined; + t.after(async () => { + if (publishedFile) await chmod(publishedFile, 0o644).catch(() => undefined); + await rm(storageRoot, { recursive: true, force: true }); + }); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + const path = join(input.outputRoot, 'readonly.js'); + await writeFile(path, 'readonly dependency\n', 'utf8'); + await chmod(path, 0o444); + }, + }; + const source = dependencySourceForName('readonly-file'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ storageRoot, producer }); + + const lease = await authority.acquire(identity, source); + publishedFile = join(lease.dependencyRoot, 'readonly.js'); + assert.equal(await readFile(publishedFile, 'utf8'), 'readonly dependency\n'); + assert.equal((await stat(publishedFile)).mode & 0o222, 0); + await lease.release(); + await authority.close(); +}); + +test('isolates published POSIX content from a producer-retained writable handle', { + skip: process.platform === 'win32', +}, async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-retained-handle-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + let retainedHandle: FileHandle | undefined; + t.after(async () => retainedHandle?.close().catch(() => undefined)); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + retainedHandle = await open(join(input.outputRoot, 'payload'), 'w+'); + await retainedHandle.writeFile('trusted\n', 'utf8'); + await retainedHandle.sync(); + }, + }; + const source = dependencySourceForName('retained-handle'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const authority = await createManagedDependencyEnvironmentAuthority({ storageRoot, producer }); + + const lease = await authority.acquire(identity, source); + const malicious = Buffer.from('MALICIOUS\n', 'utf8'); + await retainedHandle?.write(malicious, 0, malicious.length, 0); + await retainedHandle?.truncate(malicious.length); + await retainedHandle?.sync(); + assert.equal(await readFile(join(lease.dependencyRoot, 'payload'), 'utf8'), 'trusted\n'); + await retainedHandle?.close(); + retainedHandle = undefined; + await lease.release(); + await authority.close(); +}); + +test('publishes one Maka-owned artifact for concurrent equivalent acquisitions', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-environment-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + let provisionCalls = 0; + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer: { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm', + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input) { + provisionCalls += 1; + await mkdir(join(input.outputRoot, 'fixture-package'), { + recursive: true, + }); + await writeFile( + join(input.outputRoot, 'fixture-package', 'index.js'), + 'export const source = "maka-owned";\n', + 'utf8', + ); + }, + }, + }); + const identityInput = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + const identity = computeManagedDependencyEnvironmentIdentity(identityInput); + + const [first, second] = await Promise.all([ + authority.acquire(identity, { + manifestBytes: identityInput.manifestBytes, + lockfileBytes: identityInput.lockfileBytes, + }), + authority.acquire(identity, { + manifestBytes: identityInput.manifestBytes, + lockfileBytes: identityInput.lockfileBytes, + }), + ]); + + assert.equal(provisionCalls, 1); + assert.equal(first.environmentId, second.environmentId); + assert.equal(first.dependencyRoot, second.dependencyRoot); + await first.release(); + await second.release(); + await authority.close(); +}); + +test('close drains an acquisition through lease installation before deciding its outcome', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-close-drain-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + let acknowledgeLeaseBoundary!: () => void; + const leaseBoundary = new Promise((resolve) => { + acknowledgeLeaseBoundary = resolve; + }); + let continueLeaseInstallation!: () => void; + const leaseInstallationAllowed = new Promise((resolve) => { + continueLeaseInstallation = resolve; + }); + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string }) { + await writeFile(join(input.outputRoot, 'payload'), 'trusted\n', 'utf8'); + }, + }; + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + async failpoint(point) { + if (point !== 'before_environment_lease') return; + acknowledgeLeaseBoundary(); + await leaseInstallationAllowed; + }, + }); + const source = dependencySourceForName('close-drain'); + const identity = computeManagedDependencyEnvironmentIdentity(source); + const acquireTask = authority.acquire(identity, source); + await leaseBoundary; + + const closeTask = authority.close().then( + () => ({ closed: true as const }), + (error: unknown) => ({ error }), + ); + await Promise.resolve(); + continueLeaseInstallation(); + const [lease, closeResult] = await Promise.all([acquireTask, closeTask]); + if ('closed' in closeResult) { + await assert.rejects(lease.release(), /receipt authority is closed/u); + assert.fail('close succeeded while an acquisition was still installing its lease'); + } + assert.match(String(closeResult.error), /still has active leases/u); + await lease.release(); + await authority.close(); +}); + +test('collects the least-recently-used unleased environment under the cache quota', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-gc-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + let provisionCalls = 0; + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string; identity: { lockfileSha256: string } }) { + provisionCalls += 1; + await writeFile(input.outputRoot + '/payload', input.identity.lockfileSha256.slice(-8)); + }, + }; + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + maxCacheBytes: 8, + }); + const source = { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from('{"lockfileVersion":3,"name":"first"}\n'), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; + const firstIdentity = computeManagedDependencyEnvironmentIdentity(source); + const first = await authority.acquire(firstIdentity, source); + await first.release(); + + const secondSource = { + ...source, + lockfileBytes: Buffer.from('{"lockfileVersion":3,"name":"second"}\n'), + }; + const secondIdentity = computeManagedDependencyEnvironmentIdentity(secondSource); + const second = await authority.acquire(secondIdentity, secondSource); + await second.release(); + + const firstAgain = await authority.acquire(firstIdentity, source); + assert.equal(provisionCalls, 3); + await firstAgain.release(); + await authority.close(); +}); + +test('does not collect a published environment while its acquisition is still pending', async (t) => { + const storageRoot = await mkdtemp(join(tmpdir(), 'maka-dependency-pending-gc-')); + t.after(() => rm(storageRoot, { recursive: true, force: true })); + let releasePendingPublish!: () => void; + const pendingPublish = new Promise((resolve) => { + releasePendingPublish = resolve; + }); + let acknowledgeReceiptDurable!: () => void; + const receiptDurable = new Promise((resolve) => { + acknowledgeReceiptDurable = resolve; + }); + let pendingDigest: string | undefined; + const producer = { + capability: FIXTURE_PRODUCER_CAPABILITY, + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeRuntime: fixtureNodeRuntime(), + async provision(input: { outputRoot: string; identity: { lockfileSha256: string } }) { + await writeFile(join(input.outputRoot, 'payload'), input.identity.lockfileSha256, 'utf8'); + }, + }; + const authority = await createManagedDependencyEnvironmentAuthority({ + storageRoot, + producer, + maxCacheBytes: 0, + async failpoint(point) { + if (point === 'after_environment_receipt_durable' && pendingDigest) { + acknowledgeReceiptDurable(); + await pendingPublish; + } + }, + }); + const firstSource = dependencySourceForName('first'); + const firstIdentity = computeManagedDependencyEnvironmentIdentity(firstSource); + const first = await authority.acquire(firstIdentity, firstSource); + const secondSource = dependencySourceForName('second'); + const secondIdentity = computeManagedDependencyEnvironmentIdentity(secondSource); + pendingDigest = secondIdentity.environmentId; + const secondTask = authority.acquire(secondIdentity, secondSource); + await receiptDurable; + await first.release(); + releasePendingPublish(); + const second = await secondTask; + assert.equal( + await readFile(join(second.dependencyRoot, 'payload'), 'utf8'), + secondIdentity.lockfileSha256, + ); + await second.release(); + await authority.close(); +}); + +function dependencySourceForName(name: string) { + return { + manifestPath: 'package.json', + manifestBytes: Buffer.from('{"packageManager":"npm@11.12.1"}\n'), + lockfilePath: 'package-lock.json', + lockfileBytes: Buffer.from(`{"lockfileVersion":3,"name":"${name}"}\n`), + packageManagerName: 'npm' as const, + packageManagerVersion: '11.12.1', + nodeVersion: '24.7.0', + nodeAbi: '137', + platform: process.platform, + arch: process.arch, + producerRuntimeIdentitySha256: FIXTURE_PRODUCER_RUNTIME_IDENTITY, + producerPolicyIdentitySha256: FIXTURE_PRODUCER_CAPABILITY.policyIdentitySha256, + policyVersion: 'managed_dependency_environment_v1' as const, + }; +} + +function fixtureNodeRuntime() { + return { + version: '24.7.0', + abi: '137', + platform: process.platform, + arch: process.arch, + } as const; +} diff --git a/packages/storage/src/managed-dependency-environment.ts b/packages/storage/src/managed-dependency-environment.ts new file mode 100644 index 0000000000..978e9e6c32 --- /dev/null +++ b/packages/storage/src/managed-dependency-environment.ts @@ -0,0 +1,1255 @@ +import { execFile } from 'node:child_process'; +import { createHash, randomUUID } from 'node:crypto'; +import { createReadStream } from 'node:fs'; +import { createRequire } from 'node:module'; +import { + chmod, + cp, + lstat, + mkdir, + open, + readFile, + readdir, + readlink, + realpath, + rename, + rm, + stat, + utimes, +} from 'node:fs/promises'; +import { dirname, isAbsolute, join, normalize, posix, relative, resolve } from 'node:path'; +import type { DatabaseSync } from 'node:sqlite'; +import { tryLock, unlock } from 'fs-native-extensions'; + +const MANAGED_DEPENDENCY_IDENTITY_DOMAIN = 'maka.managed_dependency_environment.v1\0'; +const MANAGED_DEPENDENCY_TREE_DOMAIN = 'maka.managed_dependency_environment.tree.v1\0'; +const AUTHORITY_DATABASE_NAME = 'dependency-environment-authority-v1.sqlite'; +const DEPENDENCY_ROOT_NAME = 'node_modules'; +const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/u; +const MANAGED_DEPENDENCY_PRODUCER_POLICY_DOMAIN = + 'maka.managed_dependency_environment.producer_policy.v1\0'; +const MANAGED_DEPENDENCY_PRODUCER_POLICY_V1 = Object.freeze({ + protocolVersion: 1 as const, + kind: 'hermetic_dependency_builder_v1' as const, + network: 'registry_https_only' as const, + filesystem: 'maka_owned_staging_only' as const, + secrets: 'none' as const, + childProcess: 'verified_runtime_only' as const, + lifecycleScripts: 'disabled' as const, +}); +const activeAuthorityOwners = new Map(); +const RECEIPT_KEYS = [ + 'protocolVersion', + 'environmentId', + 'manifestPath', + 'manifestSha256', + 'lockfilePath', + 'lockfileSha256', + 'packageManagerName', + 'packageManagerVersion', + 'nodeVersion', + 'nodeAbi', + 'platform', + 'arch', + 'producerRuntimeIdentitySha256', + 'producerPolicyIdentitySha256', + 'policyVersion', + 'dependencyRootName', + 'contentTreeSha256', + 'contentBytes', + 'contentEntries', +] as const; +const require = createRequire(import.meta.url); + +export type ManagedDependencyPackageManager = 'npm' | 'pnpm' | 'yarn'; + +export interface ComputeManagedDependencyEnvironmentIdentityInput { + readonly manifestPath: string; + readonly manifestBytes: Uint8Array; + readonly lockfilePath: string; + readonly lockfileBytes: Uint8Array; + readonly packageManagerName: ManagedDependencyPackageManager; + readonly packageManagerVersion: string; + readonly nodeVersion: string; + readonly nodeAbi: string; + readonly platform: NodeJS.Platform; + readonly arch: string; + readonly producerRuntimeIdentitySha256: `sha256:${string}`; + readonly producerPolicyIdentitySha256: `sha256:${string}`; + readonly policyVersion: 'managed_dependency_environment_v1'; +} + +export interface ManagedDependencyEnvironmentIdentityV1 { + readonly protocolVersion: 1; + readonly environmentId: `sha256:${string}`; + readonly manifestPath: string; + readonly manifestSha256: `sha256:${string}`; + readonly lockfilePath: string; + readonly lockfileSha256: `sha256:${string}`; + readonly packageManagerName: ManagedDependencyPackageManager; + readonly packageManagerVersion: string; + readonly nodeVersion: string; + readonly nodeAbi: string; + readonly platform: NodeJS.Platform; + readonly arch: string; + readonly producerRuntimeIdentitySha256: `sha256:${string}`; + readonly producerPolicyIdentitySha256: `sha256:${string}`; + readonly policyVersion: 'managed_dependency_environment_v1'; +} + +export interface ManagedDependencyEnvironmentProducerCapabilityV1 { + readonly protocolVersion: 1; + readonly kind: 'hermetic_dependency_builder_v1'; + readonly runtimeIdentitySha256: `sha256:${string}`; + readonly policyIdentitySha256: `sha256:${string}`; + readonly network: 'registry_https_only'; + readonly filesystem: 'maka_owned_staging_only'; + readonly secrets: 'none'; + readonly childProcess: 'verified_runtime_only'; + readonly lifecycleScripts: 'disabled'; +} + +export interface ManagedDependencyEnvironmentProducerInput { + readonly identity: ManagedDependencyEnvironmentIdentityV1; + readonly outputRoot: string; + readonly scratchRoot: string; + readonly manifestBytes: Uint8Array; + readonly lockfileBytes: Uint8Array; + readonly abortSignal?: AbortSignal; +} + +export interface ManagedDependencyEnvironmentProducer { + readonly capability: ManagedDependencyEnvironmentProducerCapabilityV1; + readonly packageManagerName: ManagedDependencyPackageManager; + readonly packageManagerVersion: string; + readonly nodeRuntime: { + readonly version: string; + readonly abi: string; + readonly platform: NodeJS.Platform; + readonly arch: string; + }; + provision(input: ManagedDependencyEnvironmentProducerInput): Promise; +} + +export function createManagedDependencyEnvironmentProducerCapability( + runtimeIdentitySha256: `sha256:${string}`, +): ManagedDependencyEnvironmentProducerCapabilityV1 { + if (!SHA256_PATTERN.test(runtimeIdentitySha256)) { + throw new TypeError('Managed dependency producer runtime identity must be a SHA-256 digest'); + } + return Object.freeze({ + ...MANAGED_DEPENDENCY_PRODUCER_POLICY_V1, + runtimeIdentitySha256, + policyIdentitySha256: managedDependencyProducerPolicyIdentity(), + }); +} + +export interface CreateManagedDependencyEnvironmentAuthorityInput { + readonly storageRoot: string; + readonly producer: ManagedDependencyEnvironmentProducer; + readonly maxCacheBytes?: number; + readonly failpoint?: (point: ManagedDependencyEnvironmentFailpoint) => void | Promise; +} + +export type ManagedDependencyEnvironmentFailpoint = + | 'before_environment_lease' + | 'after_environment_tree_durable' + | 'after_environment_receipt_durable' + | 'after_environment_publish'; + +export interface AcquireManagedDependencyEnvironmentInput { + readonly manifestBytes: Uint8Array; + readonly lockfileBytes: Uint8Array; + readonly abortSignal?: AbortSignal; +} + +export interface ManagedDependencyEnvironmentLease { + readonly environmentId: `sha256:${string}`; + readonly dependencyRoot: string; + release(): Promise; +} + +export interface ManagedDependencyEnvironmentAuthority { + acquire( + identity: ManagedDependencyEnvironmentIdentityV1, + input: AcquireManagedDependencyEnvironmentInput, + ): Promise; + close(): Promise; +} + +interface ManagedDependencyEnvironmentReceiptV1 extends ManagedDependencyEnvironmentIdentityV1 { + readonly dependencyRootName: typeof DEPENDENCY_ROOT_NAME; + readonly contentTreeSha256: `sha256:${string}`; + readonly contentBytes: number; + readonly contentEntries: number; +} + +interface PublishedManagedDependencyEnvironment { + readonly receipt: ManagedDependencyEnvironmentReceiptV1; + readonly dependencyRoot: string; +} + +interface DependencyReceiptAuthority { + read(digest: string): ManagedDependencyEnvironmentReceiptV1 | undefined; + list(): readonly ManagedDependencyEnvironmentReceiptV1[]; + write(receipt: ManagedDependencyEnvironmentReceiptV1): void; + delete(digest: string): void; + close(): void; +} + +interface DependencyAuthorityOwnerLock { + release(): Promise; +} + +function openDependencyReceiptAuthority(path: string): DependencyReceiptAuthority { + const Database = (require('node:sqlite') as typeof import('node:sqlite')).DatabaseSync; + const database: DatabaseSync = new Database(path); + database.exec('PRAGMA synchronous = FULL'); + const version = Number( + ( + database.prepare('PRAGMA user_version').get() as { + user_version?: unknown; + } + ).user_version, + ); + if (version !== 0 && version !== 1) { + database.close(); + throw new Error(`Unsupported managed dependency receipt authority version ${version}`); + } + database.exec(` + BEGIN IMMEDIATE; + CREATE TABLE IF NOT EXISTS managed_dependency_environment_receipts ( + environment_digest TEXT PRIMARY KEY NOT NULL CHECK ( + length(environment_digest) = 64 AND + environment_digest NOT GLOB '*[^0-9a-f]*' + ), + receipt_json TEXT NOT NULL + ) STRICT; + PRAGMA user_version = 1; + COMMIT; + `); + let closed = false; + const assertOpen = () => { + if (closed) throw new Error('Managed dependency receipt authority is closed'); + }; + return Object.freeze({ + read(digest: string) { + assertOpen(); + requireDigest(digest); + const row = database + .prepare( + 'SELECT receipt_json FROM managed_dependency_environment_receipts WHERE environment_digest = ?', + ) + .get(digest) as { receipt_json?: unknown } | undefined; + if (!row) return undefined; + if (typeof row.receipt_json !== 'string') { + throw new Error('Managed dependency receipt authority contains an invalid row'); + } + return decodeReceipt(JSON.parse(row.receipt_json)); + }, + list() { + assertOpen(); + return Object.freeze( + ( + database + .prepare( + 'SELECT environment_digest, receipt_json FROM managed_dependency_environment_receipts ORDER BY environment_digest', + ) + .all() as Array<{ + environment_digest?: unknown; + receipt_json?: unknown; + }> + ).map((row) => { + if (typeof row.environment_digest !== 'string' || typeof row.receipt_json !== 'string') { + throw new Error('Managed dependency receipt authority contains an invalid row'); + } + const receipt = decodeReceipt(JSON.parse(row.receipt_json)); + if (receipt.environmentId !== `sha256:${row.environment_digest}`) { + throw new Error('Managed dependency receipt row does not match its payload identity'); + } + return receipt; + }), + ); + }, + write(receipt: ManagedDependencyEnvironmentReceiptV1) { + assertOpen(); + const digest = receipt.environmentId.slice('sha256:'.length); + requireDigest(digest); + database.exec('BEGIN IMMEDIATE'); + try { + database + .prepare( + 'INSERT INTO managed_dependency_environment_receipts (environment_digest, receipt_json) VALUES (?, ?)', + ) + .run(digest, JSON.stringify(receipt)); + database.exec('COMMIT'); + } catch (error) { + database.exec('ROLLBACK'); + throw error; + } + }, + delete(digest: string) { + assertOpen(); + requireDigest(digest); + database + .prepare('DELETE FROM managed_dependency_environment_receipts WHERE environment_digest = ?') + .run(digest); + }, + close() { + if (closed) return; + closed = true; + database.close(); + }, + }); +} + +function requireDigest(value: string): void { + if (!/^[0-9a-f]{64}$/u.test(value)) { + throw new Error('Managed dependency receipt digest is invalid'); + } +} + +export function computeManagedDependencyEnvironmentIdentity( + input: ComputeManagedDependencyEnvironmentIdentityInput, +): ManagedDependencyEnvironmentIdentityV1 { + const manifestPath = normalizeTrackedPath(input.manifestPath, 'manifestPath'); + const lockfilePath = normalizeTrackedPath(input.lockfilePath, 'lockfilePath'); + const manifestSha256 = sha256(input.manifestBytes); + const lockfileSha256 = sha256(input.lockfileBytes); + assertIdentityText(input.packageManagerVersion, 'packageManagerVersion'); + assertIdentityText(input.nodeVersion, 'nodeVersion'); + assertIdentityText(input.nodeAbi, 'nodeAbi'); + assertIdentityText(input.platform, 'platform'); + assertIdentityText(input.arch, 'arch'); + assertSha256(input.producerRuntimeIdentitySha256, 'producerRuntimeIdentitySha256'); + assertSha256(input.producerPolicyIdentitySha256, 'producerPolicyIdentitySha256'); + + const canonicalIdentity = JSON.stringify({ + manifestPath, + manifestSha256, + lockfilePath, + lockfileSha256, + packageManagerName: input.packageManagerName, + packageManagerVersion: input.packageManagerVersion, + nodeVersion: input.nodeVersion, + nodeAbi: input.nodeAbi, + platform: input.platform, + arch: input.arch, + producerRuntimeIdentitySha256: input.producerRuntimeIdentitySha256, + producerPolicyIdentitySha256: input.producerPolicyIdentitySha256, + policyVersion: input.policyVersion, + }); + const environmentId = sha256( + Buffer.concat([ + Buffer.from(MANAGED_DEPENDENCY_IDENTITY_DOMAIN, 'utf8'), + Buffer.from(canonicalIdentity, 'utf8'), + ]), + ); + + return Object.freeze({ + protocolVersion: 1, + environmentId, + manifestPath, + manifestSha256, + lockfilePath, + lockfileSha256, + packageManagerName: input.packageManagerName, + packageManagerVersion: input.packageManagerVersion, + nodeVersion: input.nodeVersion, + nodeAbi: input.nodeAbi, + platform: input.platform, + arch: input.arch, + producerRuntimeIdentitySha256: input.producerRuntimeIdentitySha256, + producerPolicyIdentitySha256: input.producerPolicyIdentitySha256, + policyVersion: input.policyVersion, + }); +} + +export async function createManagedDependencyEnvironmentAuthority( + input: CreateManagedDependencyEnvironmentAuthorityInput, +): Promise { + assertProducerCapability(input.producer.capability); + const canonicalStorageRoot = await realpath(input.storageRoot).catch(async () => { + await mkdir(input.storageRoot, { recursive: true }); + return await realpath(input.storageRoot); + }); + const ownerKey = canonicalAuthorityOwnerKey(canonicalStorageRoot); + if (activeAuthorityOwners.has(ownerKey)) { + throw new Error('Managed dependency storage root already has an active owner'); + } + const ownerClaim = {}; + activeAuthorityOwners.set(ownerKey, ownerClaim); + let ownerLock: DependencyAuthorityOwnerLock | undefined; + try { + const managedWorkspacesRoot = await ensureOwnedDirectory( + join(canonicalStorageRoot, 'managed-workspaces'), + canonicalStorageRoot, + ); + ownerLock = await acquireDependencyAuthorityOwnerLock(managedWorkspacesRoot); + return await createManagedDependencyEnvironmentAuthorityForOwner( + input, + canonicalStorageRoot, + ownerKey, + ownerClaim, + ownerLock, + ); + } catch (error) { + try { + await ownerLock?.release(); + } finally { + if (activeAuthorityOwners.get(ownerKey) === ownerClaim) { + activeAuthorityOwners.delete(ownerKey); + } + } + throw error; + } +} + +async function createManagedDependencyEnvironmentAuthorityForOwner( + input: CreateManagedDependencyEnvironmentAuthorityInput, + canonicalStorageRoot: string, + ownerKey: string, + ownerClaim: object, + ownerLock: DependencyAuthorityOwnerLock, +): Promise { + const environmentsRoot = join( + canonicalStorageRoot, + 'managed-workspaces', + 'dependency-environments', + ); + const authorityDatabasePath = join( + canonicalStorageRoot, + 'managed-workspaces', + AUTHORITY_DATABASE_NAME, + ); + const stagingRoot = join(environmentsRoot, '.staging'); + const maxCacheBytes = input.maxCacheBytes ?? 2 * 1024 * 1024 * 1024; + if (!Number.isSafeInteger(maxCacheBytes) || maxCacheBytes < 0) { + throw new TypeError('Managed dependency cache quota must be a non-negative safe integer'); + } + await Promise.all([ensureOwnedDirectory(environmentsRoot, canonicalStorageRoot)]); + await ensureOwnedDirectory(stagingRoot, environmentsRoot); + await cleanupOrphanStaging(stagingRoot); + const receiptAuthority = openDependencyReceiptAuthority(authorityDatabasePath); + try { + await cleanupIncompletePublications(environmentsRoot, receiptAuthority); + } catch (error) { + receiptAuthority.close(); + throw error; + } + const inflight = new Map>(); + const leaseCounts = new Map(); + const pendingCounts = new Map(); + let state: 'open' | 'draining' | 'closed' = 'open'; + let activeAcquisitions = 0; + const acquisitionDrainWaiters = new Set<() => void>(); + let closeTask: Promise | undefined; + let gcTask = Promise.resolve(); + + const beginAcquisition = () => { + if (state !== 'open') { + throw new Error(`Managed dependency environment authority is ${state}`); + } + activeAcquisitions += 1; + let finished = false; + return () => { + if (finished) return; + finished = true; + activeAcquisitions -= 1; + if (activeAcquisitions !== 0) return; + for (const resolveWaiter of acquisitionDrainWaiters) resolveWaiter(); + acquisitionDrainWaiters.clear(); + }; + }; + const waitForAcquisitions = () => + activeAcquisitions === 0 + ? Promise.resolve() + : new Promise((resolveWaiter) => acquisitionDrainWaiters.add(resolveWaiter)); + + const authority: ManagedDependencyEnvironmentAuthority = { + async acquire(identity, source) { + const finishAcquisition = beginAcquisition(); + try { + assertCanonicalIdentity(identity, source); + if ( + identity.packageManagerName !== input.producer.packageManagerName || + identity.packageManagerVersion !== input.producer.packageManagerVersion || + identity.nodeVersion !== input.producer.nodeRuntime.version || + identity.nodeAbi !== input.producer.nodeRuntime.abi || + identity.platform !== input.producer.nodeRuntime.platform || + identity.arch !== input.producer.nodeRuntime.arch || + identity.producerRuntimeIdentitySha256 !== + input.producer.capability.runtimeIdentitySha256 || + identity.producerPolicyIdentitySha256 !== input.producer.capability.policyIdentitySha256 + ) { + throw new Error('Managed dependency producer does not match the requested identity'); + } + assertSourceMatchesIdentity(identity, source); + const digest = identity.environmentId.slice('sha256:'.length); + pendingCounts.set(digest, (pendingCounts.get(digest) ?? 0) + 1); + let artifact: PublishedManagedDependencyEnvironment; + try { + let task = inflight.get(digest); + if (!task) { + task = openOrPublishEnvironment({ + environmentsRoot, + receiptAuthority, + stagingRoot, + identity, + source, + producer: input.producer, + failpoint: input.failpoint, + }).finally(() => inflight.delete(digest)); + inflight.set(digest, task); + } + artifact = await task; + const now = new Date(); + await utimes(dirname(artifact.dependencyRoot), now, now); + await input.failpoint?.('before_environment_lease'); + leaseCounts.set(digest, (leaseCounts.get(digest) ?? 0) + 1); + } finally { + decrementCount(pendingCounts, digest); + } + let released = false; + return Object.freeze({ + environmentId: identity.environmentId, + dependencyRoot: artifact.dependencyRoot, + async release() { + if (released) return; + released = true; + const remaining = (leaseCounts.get(digest) ?? 1) - 1; + if (remaining > 0) leaseCounts.set(digest, remaining); + else leaseCounts.delete(digest); + gcTask = gcTask + .catch(() => undefined) + .then(() => + collectEnvironmentGarbage({ + environmentsRoot, + receiptAuthority, + maxCacheBytes, + leaseCounts, + pendingCounts, + protectedDigest: digest, + }), + ); + await gcTask; + }, + }); + } finally { + finishAcquisition(); + } + }, + close() { + if (state === 'closed') return Promise.resolve(); + if (closeTask) return closeTask; + state = 'draining'; + closeTask = (async () => { + await waitForAcquisitions(); + let gcError: unknown; + try { + await gcTask; + } catch (error) { + gcError = error; + } + if (leaseCounts.size > 0) { + throw new Error('Managed dependency environment authority still has active leases'); + } + receiptAuthority.close(); + state = 'closed'; + try { + await ownerLock.release(); + } finally { + if (activeAuthorityOwners.get(ownerKey) === ownerClaim) { + activeAuthorityOwners.delete(ownerKey); + } + } + if (gcError) throw gcError; + })().catch((error: unknown) => { + if (state === 'draining') state = 'open'; + closeTask = undefined; + throw error; + }); + return closeTask; + }, + }; + return Object.freeze(authority); +} + +function canonicalAuthorityOwnerKey(canonicalStorageRoot: string): string { + const normalized = normalize(canonicalStorageRoot); + return process.platform === 'win32' ? normalized.toLowerCase() : normalized; +} + +async function acquireDependencyAuthorityOwnerLock( + managedWorkspacesRoot: string, +): Promise { + const lockPath = join(managedWorkspacesRoot, 'dependency-environment-authority-v1.lock'); + const handle = await open(lockPath, 'a+', 0o600); + let locked = false; + try { + const [opened, current] = await Promise.all([ + handle.stat({ bigint: true }), + lstat(lockPath, { bigint: true }), + ]); + if ( + !opened.isFile() || + !current.isFile() || + current.isSymbolicLink() || + opened.dev !== current.dev || + opened.ino !== current.ino + ) { + throw new Error('Managed dependency authority owner lock is not a stable file'); + } + await handle.chmod(0o600); + locked = tryLock(handle.fd); + if (!locked) { + throw new Error('Managed dependency storage root already has an active owner'); + } + } catch (error) { + if (locked) unlock(handle.fd); + await handle.close(); + throw error; + } + + let released = false; + return Object.freeze({ + async release() { + if (released) return; + released = true; + try { + unlock(handle.fd); + } finally { + await handle.close(); + } + }, + }); +} + +async function openOrPublishEnvironment(input: { + readonly environmentsRoot: string; + readonly receiptAuthority: DependencyReceiptAuthority; + readonly stagingRoot: string; + readonly identity: ManagedDependencyEnvironmentIdentityV1; + readonly source: AcquireManagedDependencyEnvironmentInput; + readonly producer: ManagedDependencyEnvironmentProducer; + readonly failpoint?: (point: ManagedDependencyEnvironmentFailpoint) => void | Promise; +}): Promise { + const digest = input.identity.environmentId.slice('sha256:'.length); + const artifactRoot = publicationPath(input.environmentsRoot, digest); + const existing = await openPublishedEnvironment( + artifactRoot, + input.environmentsRoot, + input.receiptAuthority, + input.identity, + ); + if (existing) return existing; + + const transactionRoot = join(input.stagingRoot, `${digest}-${randomUUID()}`); + const producerRoot = join(transactionRoot, 'producer'); + const projectRoot = join(producerRoot, 'project'); + const producerOutputRoot = join(projectRoot, DEPENDENCY_ROOT_NAME); + const scratchRoot = join(projectRoot, '.maka-runtime'); + const artifactStagingRoot = join(transactionRoot, 'artifact'); + const dependencyRoot = join(artifactStagingRoot, DEPENDENCY_ROOT_NAME); + await Promise.all([ + mkdir(producerOutputRoot, { recursive: true }), + mkdir(scratchRoot, { recursive: true }), + mkdir(artifactStagingRoot, { recursive: true }), + ]); + try { + await input.producer.provision({ + identity: input.identity, + outputRoot: producerOutputRoot, + scratchRoot, + manifestBytes: input.source.manifestBytes, + lockfileBytes: input.source.lockfileBytes, + ...(input.source.abortSignal ? { abortSignal: input.source.abortSignal } : {}), + }); + const canonicalOutput = await realpath(producerOutputRoot); + if (!isPathWithin(canonicalOutput, producerRoot)) { + throw new Error('Managed dependency producer output escapes its staging authority'); + } + await cp(producerOutputRoot, dependencyRoot, { + recursive: true, + dereference: false, + errorOnExist: true, + force: false, + verbatimSymlinks: true, + }); + await rm(producerRoot, { recursive: true, force: true }); + const content = await hashDependencyTree(dependencyRoot, { durable: true }); + const receipt: ManagedDependencyEnvironmentReceiptV1 = Object.freeze({ + ...input.identity, + dependencyRootName: DEPENDENCY_ROOT_NAME, + contentTreeSha256: content.sha256, + contentBytes: content.bytes, + contentEntries: content.entries, + }); + await syncDirectory(artifactStagingRoot); + await input.failpoint?.('after_environment_tree_durable'); + await rename(artifactStagingRoot, artifactRoot); + await syncDirectory(input.environmentsRoot); + await input.failpoint?.('after_environment_publish'); + input.receiptAuthority.write(receipt); + await input.failpoint?.('after_environment_receipt_durable'); + await rm(transactionRoot, { recursive: true, force: true }); + return await requirePublishedEnvironment( + artifactRoot, + input.environmentsRoot, + input.receiptAuthority, + input.identity, + ); + } catch (error) { + await rm(transactionRoot, { recursive: true, force: true }).catch(() => undefined); + const raced = await openPublishedEnvironment( + artifactRoot, + input.environmentsRoot, + input.receiptAuthority, + input.identity, + ); + if (raced) return raced; + const receiptExists = input.receiptAuthority.read(digest) !== undefined; + if (!receiptExists) { + await rm(artifactRoot, { recursive: true, force: true }).catch(() => undefined); + } + throw error; + } +} + +async function cleanupOrphanStaging(stagingRoot: string): Promise { + const entries = await readdir(stagingRoot, { withFileTypes: true }); + for (const entry of entries) { + const path = join(stagingRoot, entry.name); + const info = await lstat(path); + if (!entry.isDirectory() || info.isSymbolicLink()) { + throw new Error('Managed dependency staging contains an unowned entry'); + } + await rm(path, { recursive: true, force: true }); + } +} + +async function ensureOwnedDirectory(path: string, parentRoot: string): Promise { + await mkdir(path, { recursive: true }); + const info = await lstat(path); + if (!info.isDirectory() || info.isSymbolicLink()) { + throw new Error('Managed dependency authority path is not an owned directory'); + } + const canonical = normalize(await realpath(path)); + const canonicalParent = normalize(await realpath(parentRoot)); + if (!isPathWithin(canonical, canonicalParent)) { + throw new Error('Managed dependency authority path escapes its storage root'); + } + return canonical; +} + +async function cleanupIncompletePublications( + environmentsRoot: string, + receiptAuthority: DependencyReceiptAuthority, +): Promise { + const receipts = new Set(); + for (const receipt of receiptAuthority.list()) { + const digest = receipt.environmentId.slice('sha256:'.length); + if (receipt.environmentId !== `sha256:${digest}`) { + throw new Error('Managed dependency authority receipt has the wrong identity'); + } + receipts.add(digest); + } + const artifacts = new Set(); + for (const entry of await readdir(environmentsRoot, { + withFileTypes: true, + })) { + if (entry.name === '.staging') continue; + if (!entry.isDirectory() || !/^[0-9a-f]{64}$/u.test(entry.name)) { + throw new Error('Managed dependency cache contains an unowned entry'); + } + const info = await lstat(join(environmentsRoot, entry.name)); + if (info.isSymbolicLink()) { + throw new Error('Managed dependency cache contains a reparse point'); + } + artifacts.add(entry.name); + } + for (const digest of artifacts) { + if (!receipts.has(digest)) { + await rm(join(environmentsRoot, digest), { + recursive: true, + force: true, + }); + } + } + for (const digest of receipts) { + if (!artifacts.has(digest)) { + receiptAuthority.delete(digest); + } + } +} + +function publicationPath(environmentsRoot: string, digest: string) { + if (!/^[0-9a-f]{64}$/u.test(digest)) { + throw new Error('Managed dependency environment identity is not a canonical SHA-256 digest'); + } + const artifactRoot = join(environmentsRoot, digest); + if (!isPathWithin(artifactRoot, environmentsRoot)) { + throw new Error('Managed dependency publication path escapes its authority root'); + } + return artifactRoot; +} + +async function openPublishedEnvironment( + artifactRoot: string, + environmentsRoot: string, + receiptAuthority: DependencyReceiptAuthority, + identity: ManagedDependencyEnvironmentIdentityV1, +): Promise { + try { + return await requirePublishedEnvironment( + artifactRoot, + environmentsRoot, + receiptAuthority, + identity, + ); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined; + throw error; + } +} + +async function requirePublishedEnvironment( + artifactRoot: string, + environmentsRoot: string, + receiptAuthority: DependencyReceiptAuthority, + identity: ManagedDependencyEnvironmentIdentityV1, +): Promise { + const artifactInfo = await lstat(artifactRoot); + if (!artifactInfo.isDirectory() || artifactInfo.isSymbolicLink()) { + throw new Error('Managed dependency environment artifact root is not an owned directory'); + } + const canonicalArtifactRoot = normalize(await realpath(artifactRoot)); + if (!isPathWithin(canonicalArtifactRoot, environmentsRoot)) { + throw new Error('Managed dependency environment artifact escapes its authority root'); + } + const artifactEntries = await readdir(artifactRoot); + if (artifactEntries.length !== 1 || artifactEntries[0] !== DEPENDENCY_ROOT_NAME) { + throw new Error('Managed dependency environment artifact contains an unowned entry'); + } + const receipt = receiptAuthority.read(identity.environmentId.slice('sha256:'.length)); + if (!receipt) + throw Object.assign(new Error('Managed dependency receipt is unavailable'), { code: 'ENOENT' }); + if (!sameIdentity(receipt, identity)) { + throw new Error('Managed dependency environment receipt identity does not match the request'); + } + const dependencyRoot = join(artifactRoot, receipt.dependencyRootName); + const dependencyInfo = await lstat(dependencyRoot); + if (!dependencyInfo.isDirectory() || dependencyInfo.isSymbolicLink()) { + throw new Error('Managed dependency environment content root is unavailable'); + } + const content = await hashDependencyTree(dependencyRoot); + if ( + content.sha256 !== receipt.contentTreeSha256 || + content.bytes !== receipt.contentBytes || + content.entries !== receipt.contentEntries + ) { + throw new Error('Managed dependency environment content does not match its receipt'); + } + return Object.freeze({ + receipt, + dependencyRoot: await realpath(dependencyRoot), + }); +} + +function assertSourceMatchesIdentity( + identity: ManagedDependencyEnvironmentIdentityV1, + input: AcquireManagedDependencyEnvironmentInput, +): void { + if ( + sha256(input.manifestBytes) !== identity.manifestSha256 || + sha256(input.lockfileBytes) !== identity.lockfileSha256 + ) { + throw new Error('Managed dependency source bytes do not match the requested identity'); + } +} + +function assertCanonicalIdentity( + identity: ManagedDependencyEnvironmentIdentityV1, + source: AcquireManagedDependencyEnvironmentInput, +): void { + const expected = computeManagedDependencyEnvironmentIdentity({ + manifestPath: identity.manifestPath, + manifestBytes: source.manifestBytes, + lockfilePath: identity.lockfilePath, + lockfileBytes: source.lockfileBytes, + packageManagerName: identity.packageManagerName, + packageManagerVersion: identity.packageManagerVersion, + nodeVersion: identity.nodeVersion, + nodeAbi: identity.nodeAbi, + platform: identity.platform, + arch: identity.arch, + producerRuntimeIdentitySha256: identity.producerRuntimeIdentitySha256, + producerPolicyIdentitySha256: identity.producerPolicyIdentitySha256, + policyVersion: identity.policyVersion, + }); + if (!sameEnvironmentIdentity(expected, identity)) { + throw new Error('Managed dependency environment identity is not canonical'); + } +} + +async function hashDependencyTree( + root: string, + options: { readonly durable?: boolean } = {}, +): Promise<{ + readonly sha256: `sha256:${string}`; + readonly bytes: number; + readonly entries: number; +}> { + const hash = createHash('sha256'); + const counter = { bytes: 0, entries: 0 }; + hash.update(MANAGED_DEPENDENCY_TREE_DOMAIN); + await hashDirectory(root, '', hash, counter, options.durable === true); + if (process.platform === 'win32') await assertNoWindowsAlternateStreams(root); + return Object.freeze({ + sha256: `sha256:${hash.digest('hex')}`, + bytes: counter.bytes, + entries: counter.entries, + }); +} + +async function hashDirectory( + root: string, + relativeRoot: string, + hash: ReturnType, + counter: { bytes: number; entries: number }, + durable: boolean, +) { + const directory = relativeRoot ? join(root, relativeRoot) : root; + const entries = await readdir(directory, { withFileTypes: true }); + entries.sort((left, right) => Buffer.from(left.name).compare(Buffer.from(right.name))); + for (const entry of entries) { + if (entry.name.includes(':') || entry.name.includes('\0')) { + throw new Error('Managed dependency environment contains a non-portable path'); + } + counter.entries += 1; + const relativePath = relativeRoot ? join(relativeRoot, entry.name) : entry.name; + const portablePath = relativePath.replaceAll('\\', '/'); + const absolutePath = join(root, relativePath); + const info = await lstat(absolutePath); + const mode = process.platform === 'win32' ? 0 : info.mode & 0o777; + if (entry.isDirectory()) { + hash.update(`d\0${portablePath}\0${mode}\0`); + await hashDirectory(root, relativePath, hash, counter, durable); + continue; + } + if (entry.isFile()) { + hash.update(`f\0${portablePath}\0${mode}\0${info.size}\0`); + counter.bytes += info.size; + for await (const chunk of createReadStream(absolutePath)) hash.update(chunk as Buffer); + hash.update('\0'); + if (durable) await syncRegularFile(absolutePath, info.mode); + continue; + } + if (entry.isSymbolicLink()) { + if (process.platform === 'win32') { + throw new Error('Managed dependency environment contains a Windows reparse point'); + } + const target = await readlink(absolutePath); + if (isAbsolute(target) || !isPathWithin(resolve(dirname(absolutePath), target), root)) { + throw new Error('Managed dependency environment contains an escaping symbolic link'); + } + hash.update(`l\0${portablePath}\0${target.replaceAll('\\', '/')}\0`); + continue; + } + throw new Error('Managed dependency environment contains an unsupported filesystem entry'); + } + if (durable) await syncDirectory(directory); +} + +async function assertNoWindowsAlternateStreams(root: string): Promise { + const systemRoot = process.env.SystemRoot ?? process.env.WINDIR; + if (!systemRoot) { + throw new Error('Cannot verify Windows alternate streams without SystemRoot'); + } + const powershell = join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'); + const script = [ + '$ErrorActionPreference = "Stop"', + '$root = $env:MAKA_ADS_ROOT', + '$items = @((Get-Item -LiteralPath $root -Force)) + @(Get-ChildItem -LiteralPath $root -Force -Recurse)', + 'foreach ($item in $items) {', + ' $streams = @(Get-Item -LiteralPath $item.FullName -Stream * -ErrorAction SilentlyContinue)', + ' foreach ($stream in $streams) {', + " if ($stream.Stream -ne ':$DATA') { throw 'alternate data stream detected' }", + ' }', + '}', + ].join('; '); + await new Promise((resolvePromise, rejectPromise) => { + execFile( + powershell, + ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', script], + { + windowsHide: true, + timeout: 30_000, + env: { + SystemRoot: systemRoot, + WINDIR: systemRoot, + MAKA_ADS_ROOT: root, + }, + }, + (error) => { + if (error) { + rejectPromise( + new Error('Managed dependency environment contains an alternate data stream', { + cause: error, + }), + ); + } else { + resolvePromise(); + } + }, + ); + }); +} + +function isPathWithin(candidate: string, root: string): boolean { + const path = relative(normalize(root), normalize(candidate)); + return path === '' || (!path.startsWith('..') && !isAbsolute(path)); +} + +function decodeReceipt(value: unknown): ManagedDependencyEnvironmentReceiptV1 { + if (!value || typeof value !== 'object') throw new Error('Invalid dependency receipt'); + const receipt = value as Partial; + const keys = Object.keys(value).sort(); + const expectedKeys = [...RECEIPT_KEYS].sort(); + if ( + keys.length !== expectedKeys.length || + keys.some((key, index) => key !== expectedKeys[index]) || + receipt.protocolVersion !== 1 || + receipt.dependencyRootName !== DEPENDENCY_ROOT_NAME || + typeof receipt.environmentId !== 'string' || + !SHA256_PATTERN.test(receipt.environmentId) || + typeof receipt.contentTreeSha256 !== 'string' || + !SHA256_PATTERN.test(receipt.contentTreeSha256) || + typeof receipt.contentBytes !== 'number' || + !Number.isSafeInteger(receipt.contentBytes) || + receipt.contentBytes < 0 || + typeof receipt.contentEntries !== 'number' || + !Number.isSafeInteger(receipt.contentEntries) || + receipt.contentEntries < 0 || + typeof receipt.manifestSha256 !== 'string' || + !SHA256_PATTERN.test(receipt.manifestSha256) || + typeof receipt.lockfileSha256 !== 'string' || + !SHA256_PATTERN.test(receipt.lockfileSha256) || + typeof receipt.manifestPath !== 'string' || + typeof receipt.lockfilePath !== 'string' || + (receipt.packageManagerName !== 'npm' && + receipt.packageManagerName !== 'pnpm' && + receipt.packageManagerName !== 'yarn') || + typeof receipt.packageManagerVersion !== 'string' || + typeof receipt.nodeVersion !== 'string' || + typeof receipt.nodeAbi !== 'string' || + typeof receipt.platform !== 'string' || + typeof receipt.arch !== 'string' || + typeof receipt.producerRuntimeIdentitySha256 !== 'string' || + !SHA256_PATTERN.test(receipt.producerRuntimeIdentitySha256) || + typeof receipt.producerPolicyIdentitySha256 !== 'string' || + !SHA256_PATTERN.test(receipt.producerPolicyIdentitySha256) || + receipt.policyVersion !== 'managed_dependency_environment_v1' + ) { + throw new Error('Invalid dependency receipt'); + } + return Object.freeze(receipt as ManagedDependencyEnvironmentReceiptV1); +} + +async function collectEnvironmentGarbage(input: { + readonly environmentsRoot: string; + readonly receiptAuthority: DependencyReceiptAuthority; + readonly maxCacheBytes: number; + readonly leaseCounts: ReadonlyMap; + readonly pendingCounts: ReadonlyMap; + readonly protectedDigest: string; +}): Promise { + const artifacts: Array<{ + readonly digest: string; + readonly root: string; + readonly bytes: number; + readonly lastUsedMs: number; + }> = []; + for (const entry of await readdir(input.environmentsRoot, { + withFileTypes: true, + })) { + if (entry.name === '.staging') continue; + if (!entry.isDirectory() || !/^[0-9a-f]{64}$/u.test(entry.name)) { + throw new Error('Managed dependency cache contains an unowned entry'); + } + const root = join(input.environmentsRoot, entry.name); + const receipt = input.receiptAuthority.read(entry.name); + if (!receipt) throw new Error('Managed dependency cache is missing its authority receipt'); + if (receipt.environmentId !== `sha256:${entry.name}`) { + throw new Error('Managed dependency cache directory does not match its receipt'); + } + artifacts.push({ + digest: entry.name, + root, + // Empty files and directories consume filesystem metadata even when + // contentBytes is zero. Charge one conservative 4 KiB unit per entry so + // inode-only trees cannot bypass the cache quota. + bytes: receipt.contentBytes + receipt.contentEntries * 4_096, + lastUsedMs: (await stat(root)).mtimeMs, + }); + } + let totalBytes = artifacts.reduce((sum, artifact) => sum + artifact.bytes, 0); + artifacts.sort( + (left, right) => left.lastUsedMs - right.lastUsedMs || left.digest.localeCompare(right.digest), + ); + for (const artifact of artifacts) { + if (totalBytes <= input.maxCacheBytes) break; + if ( + artifact.digest === input.protectedDigest || + input.leaseCounts.has(artifact.digest) || + input.pendingCounts.has(artifact.digest) + ) + continue; + await rm(artifact.root, { recursive: true, force: true }); + input.receiptAuthority.delete(artifact.digest); + totalBytes -= artifact.bytes; + } +} + +function sameIdentity( + receipt: ManagedDependencyEnvironmentReceiptV1, + identity: ManagedDependencyEnvironmentIdentityV1, +): boolean { + return ( + receipt.environmentId === identity.environmentId && + receipt.manifestPath === identity.manifestPath && + receipt.manifestSha256 === identity.manifestSha256 && + receipt.lockfilePath === identity.lockfilePath && + receipt.lockfileSha256 === identity.lockfileSha256 && + receipt.packageManagerName === identity.packageManagerName && + receipt.packageManagerVersion === identity.packageManagerVersion && + receipt.nodeVersion === identity.nodeVersion && + receipt.nodeAbi === identity.nodeAbi && + receipt.platform === identity.platform && + receipt.arch === identity.arch && + receipt.producerRuntimeIdentitySha256 === identity.producerRuntimeIdentitySha256 && + receipt.producerPolicyIdentitySha256 === identity.producerPolicyIdentitySha256 && + receipt.policyVersion === identity.policyVersion + ); +} + +function sameEnvironmentIdentity( + left: ManagedDependencyEnvironmentIdentityV1, + right: ManagedDependencyEnvironmentIdentityV1, +): boolean { + return ( + left.protocolVersion === right.protocolVersion && + left.environmentId === right.environmentId && + left.manifestPath === right.manifestPath && + left.manifestSha256 === right.manifestSha256 && + left.lockfilePath === right.lockfilePath && + left.lockfileSha256 === right.lockfileSha256 && + left.packageManagerName === right.packageManagerName && + left.packageManagerVersion === right.packageManagerVersion && + left.nodeVersion === right.nodeVersion && + left.nodeAbi === right.nodeAbi && + left.platform === right.platform && + left.arch === right.arch && + left.producerRuntimeIdentitySha256 === right.producerRuntimeIdentitySha256 && + left.producerPolicyIdentitySha256 === right.producerPolicyIdentitySha256 && + left.policyVersion === right.policyVersion + ); +} + +function decrementCount(counts: Map, key: string): void { + const remaining = (counts.get(key) ?? 1) - 1; + if (remaining > 0) counts.set(key, remaining); + else counts.delete(key); +} + +async function syncDirectory(path: string): Promise { + const handle = await open(path, 'r'); + try { + await handle.sync(); + } catch (error) { + if (process.platform !== 'win32') throw error; + } finally { + await handle.close(); + } +} + +async function syncRegularFile(path: string, mode: number): Promise { + if (process.platform !== 'win32') { + await syncOpenedFile(path, 'r'); + return; + } + + const originalMode = mode & 0o777; + const writableMode = originalMode | 0o200; + if (writableMode !== originalMode) await chmod(path, writableMode); + try { + await syncOpenedFile(path, 'r+'); + } finally { + if (writableMode !== originalMode) await chmod(path, originalMode); + } +} + +async function syncOpenedFile(path: string, flags: 'r' | 'r+'): Promise { + const handle = await open(path, flags); + try { + await handle.sync(); + } finally { + await handle.close(); + } +} + +function sha256(value: Uint8Array): `sha256:${string}` { + return `sha256:${createHash('sha256').update(value).digest('hex')}`; +} + +function normalizeTrackedPath(value: string, field: string): string { + assertIdentityText(value, field); + const normalized = posix.normalize(value.replaceAll('\\', '/')); + if ( + normalized === '.' || + normalized.startsWith('/') || + normalized === '..' || + normalized.startsWith('../') + ) { + throw new TypeError(`${field} must be a workspace-relative tracked path`); + } + return normalized; +} + +function assertIdentityText(value: string, field: string): void { + if (!value || value.includes('\0')) { + throw new TypeError(`${field} must be non-empty text without NUL bytes`); + } +} + +function assertSha256(value: string, field: string): void { + if (!SHA256_PATTERN.test(value)) { + throw new TypeError(`${field} must be a SHA-256 digest`); + } +} + +function managedDependencyProducerPolicyIdentity(): `sha256:${string}` { + return sha256( + Buffer.concat([ + Buffer.from(MANAGED_DEPENDENCY_PRODUCER_POLICY_DOMAIN, 'utf8'), + Buffer.from(JSON.stringify(MANAGED_DEPENDENCY_PRODUCER_POLICY_V1), 'utf8'), + ]), + ); +} + +function assertProducerCapability( + capability: ManagedDependencyEnvironmentProducerCapabilityV1, +): void { + const expected = createManagedDependencyEnvironmentProducerCapability( + capability.runtimeIdentitySha256, + ); + if ( + Object.keys(capability).sort().join('\0') !== Object.keys(expected).sort().join('\0') || + Object.entries(expected).some( + ([key, value]) => + capability[key as keyof ManagedDependencyEnvironmentProducerCapabilityV1] !== value, + ) + ) { + throw new Error('Managed dependency producer capability is invalid'); + } +}