Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion packages/code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,13 +1100,59 @@ Existing definitions without `reuse` retain the startup behavior. Fresh conversa
instances use the same preparation contract, with independent checkout receipts.
This does not attach another checkout's `node_modules`, provision linked lanes on
command admission, or recheck existing instances on every command. It does not
deduplicate installed dependencies between worktrees or enforce disk quotas.
deduplicate installed dependencies between worktrees unless snapshots below are
configured, or enforce disk quotas.

Shared tool cache grants below do not attach another checkout's installed dependency
tree. Keep monorepo links and mutable outputs checkout-local. Do not broaden the
sandbox root or symlink another branch's full installation. Shared download caches
alone do not reduce installed `node_modules` copies.

## Copy-on-write installed dependencies

For matching fresh checkouts on the **same clone-capable filesystem**, add a
private snapshot store to the readiness contract:

```yaml
setup:
command: npm ci
timeoutMs: 300000
reuse:
inputs: [package.json, package-lock.json]
checkCommand: test -x node_modules/.bin/tsc
snapshot:
store: /srv/lia-state/dependency-snapshots
paths: [node_modules]
maxBytes: 4294967296
maxFiles: 200000
```

Create the external store as the worker account with mode `0700`. It must not
overlap any source root, definition, credential or shared tool cache. Commands
cannot read or write it. Use a separate store per project/trust domain. The
portable key includes project identity, declared input bytes, recipe, policy,
Node ABI and platform, but not the checkout inode. Kernel locks serialize setup
for one key; different keys remain independent. Incomplete clones are never
published. Cancellation is checked during bounded traversal.

Matching snapshots restore only when **every** declared `node_modules` directory
is missing. The sandboxed readiness check must pass before accepting the restore.
Existing directories are never replaced by restoration; ordinary setup handles
repair. Include nested workspace installations explicitly. Relative checkout-local
package links are preserved; absolute/escaping links, hard-linked files, links into
Git/worktree control paths and special files are rejected. Hardlink-based package
manager layouts need a different adapter; this snapshot mode targets npm copies.
Changing one restored installation cannot modify the snapshot or another checkout.

This requires APFS clones or Linux reflinks (for example a suitably configured
XFS/Btrfs volume). The worker verifies cloning before installation and rejects
unsupported filesystems instead of silently making full copies or writable hard
links. Ordinary ext4 workers should leave snapshots disabled and can still use
checkout-local preparation receipts and shared downloads. This is **not** Python
virtualenv relocation, automatic preparation of manually created linked lanes,
or a hard quota on arbitrary commands. Validate all install/postinstall inputs
and path-independent artifacts before opting in.

## Shared tool and download resources

Explicit operator-managed stores can live outside the checkouts:
Expand Down
20 changes: 18 additions & 2 deletions packages/code/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,13 @@ async function run(
...environments.map(environment => environment.path), ...rootQuarantinePaths.values(),
].filter((path): path is string => path != null));
await assertEnvironmentDefinitionsOutsideRoots(resourceRoots, roots);
const snapshotStores = environments.flatMap(environment => environment.snapshotStore ? [environment.snapshotStore] : []);
await assertEnvironmentResourceIsolation(snapshotStores.map(store => ({ kind: 'npm-cache' as const, path: store.store, access: 'read-only' as const })), roots.map(root => root.root), [
identityPath, preparationDirectory, github.privateKeyPath, ...environments.map(environment => environment.path),
...rootQuarantinePaths.values(), ...environments.flatMap(environment => (environment.resources ?? []).map(resource => resource.path)),
].filter((path): path is string => path != null));
await assertEnvironmentDefinitionsOutsideRoots(snapshotStores.map(store => ({ path: store.store, sourceParents: store.controlPaths,
definition: { name: 'dependency-store', root: store.store }, fingerprint: '' })), roots);
const preparationReceipt = (root: string) => join(preparationDirectory,
`${createHash('sha256').update(JSON.stringify([codeApiUrl, workerId, root])).digest('hex')}.json`);
// Keep an admission boundary even when trusted-VM checkout routing uses a
Expand Down Expand Up @@ -1073,6 +1080,7 @@ async function run(
protectedPaths: [
identityPath,
...(environments.some(environment => environment.definition.setup?.reuse) ? [preparationDirectory] : []),
...snapshotStores.map(store => store.store),
...environments.map(environment => environment.path),
...rootQuarantinePaths.values(),
github.privateKeyPath,
Expand Down Expand Up @@ -1163,10 +1171,11 @@ async function run(
...(nativeCommandSandbox instanceof NativeWorkspaceCommandPool
? {
prepareInstance: async (instance, signal) => {
const setup = environments.find(
const environment = environments.find(
(environment) =>
environment.definition.name === instance.sourceWorkspaceId,
)?.definition.setup;
);
const setup = environment?.definition.setup;
if (!setup) return;
if (admittedGitHubRepositories) {
admittedGitHubRepositories.set(
Expand All @@ -1181,6 +1190,8 @@ async function run(
workspaceRoot: instance.root,
});
await prepareCodeEnvironment({
snapshotStore: environment!.snapshotStore,
snapshotScope: environment!.definition.repo ?? environment!.definition.name,
root: instance.root, identity: instance.identity, setup,
receiptPath: preparationReceipt(instance.root),
context: JSON.stringify([serializeNativeSrtCommandPolicy(commandPolicy), commandAllowedDomains, github.policyIdentity,
Expand Down Expand Up @@ -1353,6 +1364,11 @@ async function run(
await guard.assertAvailable();
let armed = false;
const preparation = await prepareCodeEnvironment({
snapshotStore: environment.snapshotStore,
snapshotScope: environment.definition.repo ?? environment.definition.name,
beforeMutation: async () => {
if (!armed) { await guard.arm('Dependency restoration did not settle', 'setup'); armed = true; }
},
root: environment.definition.root,
identity: roots.find(root => root.id === id)!.identity!,
setup, receiptPath: preparationReceipt(environment.definition.root),
Expand Down
267 changes: 267 additions & 0 deletions packages/code/src/dependency-snapshots.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
mkdtemp,
mkdir,
writeFile,
readFile,
lstat,
link,
realpath,
rm,
symlink,
rename,
readdir,
} from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
parseDependencySnapshot,
loadDependencySnapshot,
publishDependencySnapshot,
restoreDependencySnapshot,
withDependencySnapshot,
} from './dependency-snapshots.js';
import { prepareCodeEnvironment } from './environment-preparation.js';
import { captureWorkspaceRootIdentity } from './root-identity.js';
import { NativeProcessWorkspaceCommandSandbox } from './native-process.js';
import type { EnvironmentPreparationOptions } from './environment-preparation.js';

async function fixture(t: test.TestContext) {
const directory = await realpath(
await mkdtemp(join(tmpdir(), 'dependency-snapshot-')),
);
t.after(() => rm(directory, { recursive: true, force: true }));
const a = join(directory, 'a'),
b = join(directory, 'b'),
storePath = join(directory, 'store');
for (const path of [a, b, storePath]) await mkdir(path, { mode: 0o700 });
const store = await loadDependencySnapshot(
parseDependencySnapshot({ store: storePath, paths: ['node_modules'] }),
);
const ai = await captureWorkspaceRootIdentity(a),
bi = await captureWorkspaceRootIdentity(b);
return { directory, a, b, store, ai, bi, key: 'a'.repeat(64) };
}

test('snapshot schema restricts installed directories, overlapping paths and finite bounds', () => {
const valid = {
store: '/private/store',
paths: ['node_modules', 'packages/api/node_modules'],
};
assert.equal(parseDependencySnapshot(valid).maxFiles, 200_000);
for (const paths of [
['.git'],
['../node_modules'],
['node_modules', 'node_modules/pkg/node_modules'],
['node_modules', 'node_modules'],
])
assert.throws(() => parseDependencySnapshot({ ...valid, paths }));
assert.throws(() =>
parseDependencySnapshot({ ...valid, maxBytes: Infinity }),
);
assert.throws(() =>
parseDependencySnapshot({ ...valid, store: 'relative' }),
);
});

// These assertions exercise real filesystem cloning, not a mocked successful copy.
test(
'real clone snapshots isolate writable files, preserve checkout-local links and never replace installations',
{
skip:
process.platform !== 'darwin' &&
process.env.LIBRECHAT_CODE_LIVE_SNAPSHOT_TESTS !== '1',
},
async t => {
const { a, b, store, ai, bi, key } = await fixture(t);
await mkdir(join(a, 'node_modules'));
await writeFile(
join(a, 'node_modules', 'package.js'),
'module.exports = 42',
);
await mkdir(join(a, 'packages'));
await mkdir(join(b, 'packages'));
await symlink('../packages', join(a, 'node_modules', 'local'));
await withDependencySnapshot(store, key, () =>
publishDependencySnapshot(store, key, a, ai),
);
assert.equal(
await withDependencySnapshot(store, key, () =>
restoreDependencySnapshot(store, key, b, bi),
),
true,
);
assert.notEqual(
(await lstat(join(a, 'node_modules', 'package.js'))).ino,
(await lstat(join(b, 'node_modules', 'package.js'))).ino,
);
await writeFile(join(b, 'node_modules', 'package.js'), 'changed');
assert.equal(
await readFile(join(a, 'node_modules', 'package.js'), 'utf8'),
'module.exports = 42',
);
assert.equal(
await readFile(join(store.store, key, '0', 'package.js'), 'utf8'),
'module.exports = 42',
);
assert.equal(await restoreDependencySnapshot(store, key, b, bi), false);
assert.equal(
await readFile(join(b, 'node_modules', 'package.js'), 'utf8'),
'changed',
);
},
);

test('unsafe links and bounded traversal never publish a partial snapshot', async t => {
const { a, store, ai, key } = await fixture(t);
await mkdir(join(a, 'node_modules'));
await symlink('/etc/passwd', join(a, 'node_modules', 'escape'));
await assert.rejects(
publishDependencySnapshot(store, key, a, ai),
/checkout-local/,
);
assert.deepEqual(await readdir(store.store), []);
await rm(join(a, 'node_modules', 'escape'));
await writeFile(join(a, 'node_modules', 'large'), 'large');
await assert.rejects(
publishDependencySnapshot({ ...store, maxBytes: 1 }, key, a, ai),
/maxBytes/,
);
assert.deepEqual(await readdir(store.store), []);
});

test('hardlinks cannot copy outside file contents into a readable dependency snapshot', async t => {
const { a, store, ai, key, directory } = await fixture(t);
await mkdir(join(a, 'node_modules'));
const outside = join(directory, 'private-input');
await writeFile(outside, 'private');
await link(outside, join(a, 'node_modules', 'alias'));
await assert.rejects(
publishDependencySnapshot(store, key, a, ai),
/hard-linked/,
);
assert.deepEqual(await readdir(store.store), []);
});

test('kernel lock serializes publishers, supports cancellation and rejects a replaced store', async t => {
const { store, key } = await fixture(t);
let release!: () => void;
const blocked = new Promise<void>(resolve => {
release = resolve;
});
let entered!: () => void;
const started = new Promise<void>(resolve => {
entered = resolve;
});
const first = withDependencySnapshot(store, key, async () => {
entered();
await blocked;
});
await started;
const controller = new AbortController();
const second = withDependencySnapshot(
store,
key,
async () => assert.fail('cancelled lock ran'),
controller.signal,
);
controller.abort();
await assert.rejects(second);
release();
await first;
await rename(store.store, `${store.store}-old`);
await mkdir(store.store, { mode: 0o700 });
await assert.rejects(
withDependencySnapshot(store, key, async () => {}),
/changed after admission/,
);
});

test(
'real native preparation shares dependencies between checkouts, invalidates changed inputs and denies store access',
{
skip:
process.env.LIBRECHAT_CODE_LIVE_SRT_TESTS !== '1' ||
(process.platform !== 'darwin' &&
process.env.LIBRECHAT_CODE_LIVE_SNAPSHOT_TESTS !== '1'),
},
async t => {
const { a, b, store, directory, ai, bi } = await fixture(t);
for (const root of [a, b])
await writeFile(join(root, 'package-lock.json'), 'version-one');
const commands: string[] = [];
const setup: EnvironmentPreparationOptions['setup'] = {
command:
"mkdir -p node_modules; printf 'module.exports = 42' > node_modules/package.js",
timeoutMs: 5000,
reuse: {
inputs: ['package-lock.json'],
checkCommand: 'test -f node_modules/package.js',
checkTimeoutMs: 3000,
snapshot: {
store: store.store,
paths: store.paths,
maxBytes: store.maxBytes,
maxFiles: store.maxFiles,
},
},
};
const sandboxes = [ai, bi].map(
identity =>
new NativeProcessWorkspaceCommandSandbox({
workspaceRoot: identity.path,
workspaceIdentity: identity,
homeDirectory: directory,
protectedPaths: [store.store],
allowedDomains: [],
}),
);
t.after(async () => {
for (const sandbox of sandboxes) await sandbox.close();
});
const run = (index: number) => {
const identity = [ai, bi][index];
return prepareCodeEnvironment({
root: identity.path,
identity,
setup,
context: 'policy',
snapshotStore: store,
snapshotScope: 'project',
receiptPath: join(store.store, `receipt-${index}.json`),
execute: async (command, timeoutMs) => {
commands.push(command);
return sandboxes[index].execute({
protocolVersion: 1,
operation: 'execute_command',
workspaceId: 'primary',
command,
timeoutMs,
maxOutputBytes: 8192,
});
},
});
};
assert.equal(await run(0), 'prepared');
assert.equal(await run(1), 'restored');
assert.equal(
commands.filter(command => command === setup.command).length,
1,
);
await writeFile(join(b, 'package-lock.json'), 'version-two');
assert.equal(await run(1), 'prepared');
assert.equal(
commands.filter(command => command === setup.command).length,
2,
);
const denied = await sandboxes[0].execute({
protocolVersion: 1,
operation: 'execute_command',
workspaceId: 'primary',
command: `ls '${store.store}'`,
timeoutMs: 3000,
});
assert.notEqual(denied.exitCode, 0);
},
);
Loading