Reconcile Pi prerequisites with the integrated runtime while holding admission

This commit is contained in:
Dotta committed 2026-10-08 15:01:33 -05:00
1 parent 20ce062701
commit f620e7157b
313 files changed
+13086 -2187

No files matched your search

@@ -38,6 +38,33 @@ test('only the scripts-disabled dependency download gets network access', () =>
assert.ok(values(args, '--env').includes('npm_config_ignore_scripts=true'));
});
test('Pi assembly gets bounded scratch capacity while preserving sandbox restrictions', () => {
const args = grokConsumerDockerArgs({ ...paths, download: true, temporarySizeMiB: 2048, command: ['node', '/consumer/node_modules/paperclipai/dist/index.js', 'runtime', 'setup', 'pi'] });
assert.deepEqual(values(args, '--tmpfs'), ['/tmp:rw,nosuid,nodev,noexec,size=2048m,mode=1777']);
assert.deepEqual(values(args, '--memory'), ['3g']);
assert.ok(args.includes('--read-only'));
assert.deepEqual(values(args, '--cap-drop'), ['ALL']);
assert.deepEqual(values(args, '--security-opt'), ['no-new-privileges']);
for (const temporarySizeMiB of [0, 255, 2049, Infinity, '2048']) {
assert.throws(() => grokConsumerDockerArgs({ ...paths, temporarySizeMiB, command: ['node'] }), /bounded/);
}
});
test('only an offline runtime probe can execute its verified scratch snapshot', () => {
const command = ['node', '/packages/pi-public-install-probe.mjs', '/consumer/node_modules/@paperclipai/server'];
const args = grokConsumerDockerArgs({ ...paths, command, temporarySizeMiB: 2048, temporaryExecutable: true });
assert.deepEqual(values(args, '--tmpfs'), ['/tmp:rw,nosuid,nodev,exec,size=2048m,mode=1777']);
assert.deepEqual(values(args, '--network'), ['none']);
assert.deepEqual(values(args, '--user'), ['1001:1001']);
assert.ok(args.includes('--read-only'));
assert.deepEqual(values(args, '--cap-drop'), ['ALL']);
assert.deepEqual(values(args, '--security-opt'), ['no-new-privileges']);
assert.throws(() => grokConsumerDockerArgs({ ...paths, command, download: true, temporaryExecutable: true }), /offline/);
assert.throws(() => grokConsumerDockerArgs({ ...paths, command, temporaryExecutable: 'true' }), /offline/);
const source = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
assert.ok(source.includes("temporarySizeMiB: 2048, temporaryExecutable: true"));
});
test('the separately provisioned executable is exposed read-only to the offline probe', () => {
const prerequisite = '/private/staging/native/grok';
const args = grokConsumerDockerArgs({ ...paths, prerequisite, command: ['node', '/packages/probe.mjs', 'present'] });
@@ -0,0 +1,44 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import { retainRunnerQualificationPackages } from '../retain-runner-qualification-packages.mjs';
test('retention preserves public archives and runs the normal plugin prepack without modifying source', () => {
const root = mkdtempSync(join(tmpdir(), 'qualification-retention-test-'));
const sourceRevision = '1'.repeat(40), releaseVersion = '0.0.0-qualification.test';
const put = (relative, content) => {
const file = join(root, relative); mkdirSync(join(file, '..'), { recursive: true });
writeFileSync(file, content); return file;
};
const json = (relative, value) => put(relative, JSON.stringify(value));
try {
json('server/dist/build-info.json', { commit: sourceRevision });
const originalPlugin = JSON.parse(readFileSync(new URL('../../packages/plugins/sandbox-providers/daytona/package.json', import.meta.url)));
const pluginManifest = json('packages/plugins/sandbox-providers/daytona/package.json', originalPlugin);
put('packages/plugins/sandbox-providers/daytona/dist/index.js', 'export const retained = true;\n');
json('packages/plugins/sdk/package.json', { name: '@paperclipai/plugin-sdk', version: '0.3.1' });
put('scripts/generate-plugin-package-json.mjs', readFileSync(new URL('../generate-plugin-package-json.mjs', import.meta.url)));
json('packages/paperclip-runner/package.json', { name: '@paperclipai/paperclip-runner', version: '0.0.0', private: true, files: ['dist'] });
put('packages/paperclip-runner/dist/cli/eval-session.js', 'export const evaluation = true;\n');
const publicArchives = ['@paperclipai/server', 'paperclipai', '@paperclipai/plugin-sdk'].map((name, i) => ({ name, file: put(`public-${i}.tgz`, `unchanged archive ${name}`) }));
const output = join(root, 'retained');
const result = retainRunnerQualificationPackages({ repo: root, output, sourceRevision, releaseVersion, publicArchives });
assert.equal(result.archives.length, 5);
for (const archive of result.archives) assert.equal(createHash('sha256').update(readFileSync(join(output, archive.file))).digest('hex'), archive.sha256);
for (const archive of publicArchives) assert.deepEqual(readFileSync(join(output, archive.file.split('/').at(-1))), readFileSync(archive.file));
const plugin = result.archives.find(a => a.name === '@paperclipai/plugin-daytona');
const packedManifest = JSON.parse(execFileSync('tar', ['-xOf', join(output, plugin.file), 'package/package.json']));
assert.equal(packedManifest.dependencies['@paperclipai/plugin-sdk'], releaseVersion);
assert.equal(packedManifest.exports['.'].import, './dist/index.js');
assert.equal(execFileSync('tar', ['-xOf', join(output, plugin.file), 'package/dist/index.js'], { encoding: 'utf8' }), 'export const retained = true;\n');
assert.deepEqual(JSON.parse(readFileSync(pluginManifest)), originalPlugin);
assert.equal(existsSync(join(root, 'packages/plugins/sandbox-providers/daytona/package.dev.json')), false);
assert.throws(() => retainRunnerQualificationPackages({ repo: root, output, sourceRevision, releaseVersion, publicArchives }), /Never replace/);
assert.throws(() => retainRunnerQualificationPackages({ repo: root, output: join(root, 'wrong-source'), sourceRevision: '2'.repeat(40), releaseVersion, publicArchives }));
assert.equal(existsSync(join(root, 'wrong-source')), false);
} finally { rmSync(root, { recursive: true, force: true }); }
});
@@ -0,0 +1,14 @@
#!/usr/bin/env node
/** Run after the final server build and Linux daemon/provider-pack assembly. */
import { createHash } from 'node:crypto';
import { writeFile, realpath } from 'node:fs/promises';
import { resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
const [directory, sourceRevision, ...extra] = process.argv.slice(2);
if (!directory || !/^[a-f0-9]{40}$/.test(sourceRevision ?? '') || extra.length) throw new Error('Usage: node scripts/create-runner-remote-companion.mjs DIRECTORY SOURCE_SHA');
const root = await realpath(directory);
const { createRemotePiCompanionManifest } = await import(pathToFileURL(resolve('server/dist/services/native-runtime/remote-pi-companion.js')).href);
const manifest = await createRemotePiCompanionManifest(root, sourceRevision);
const bytes = JSON.stringify(manifest, null, 2) + '\n';
await writeFile(resolve(root, 'companion.json'), bytes, { flag: 'wx', mode: 0o644 });
console.log(JSON.stringify({ directory: root, sourceRevision, target: manifest.target, manifestSha256: createHash('sha256').update(bytes).digest('hex'), providerPackDigest: manifest.providerPackDigest, daemonSha256: manifest.daemonSha256 }));
+8 -3
View File
@@ -7,18 +7,23 @@ export const GROK_PUBLIC_INSTALL_LIFECYCLE = [
'npm', 'rebuild', '--offline', '--ignore-scripts=false', '--dangerously-allow-all-scripts',
];
export function grokConsumerDockerArgs({ assets, consumer, cache, command, uid, gid, download = false, prerequisite, temporarySizeMb = 256 }) {
export function grokConsumerDockerArgs({ assets, consumer, cache, command, uid, gid, download = false, prerequisite, temporarySizeMiB = 256, temporaryExecutable = false }) {
if (!Number.isSafeInteger(uid) || uid <= 0 || !Number.isSafeInteger(gid) || gid <= 0) {
throw new Error('Public-install verification requires an unprivileged host user');
}
if (!Number.isSafeInteger(temporarySizeMb) || temporarySizeMb < 256 || temporarySizeMb > 2048) throw new Error('Invalid bounded public-install temporary size');
if (!Number.isSafeInteger(temporarySizeMiB) || temporarySizeMiB < 256 || temporarySizeMiB > 2048) {
throw new Error('Public-install temporary storage must be bounded between 256 and 2048 MiB');
}
if (typeof temporaryExecutable !== 'boolean' || (temporaryExecutable && download)) {
throw new Error('Executable runtime snapshots require an offline verification sandbox');
}
return [
'run', '--rm', '--platform', 'linux/amd64',
'--user', `${uid}:${gid}`, '--read-only',
'--cap-drop', 'ALL', '--security-opt', 'no-new-privileges',
'--pids-limit', '256', '--memory', '3g',
'--network', download ? 'bridge' : 'none',
'--tmpfs', `/tmp:rw,nosuid,nodev,size=${temporarySizeMb}m,mode=1777`,
'--tmpfs', `/tmp:rw,nosuid,nodev,${temporaryExecutable ? 'exec' : 'noexec'},size=${temporarySizeMiB}m,mode=1777`,
'--env', 'HOME=/tmp', '--env', 'npm_config_cache=/cache',
'--env', 'npm_config_nodedir=/usr/local',
'--env', 'npm_config_audit=false', '--env', 'npm_config_fund=false',
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env node
// Run in a credential-free public npm consumer after `paperclipai runtime setup pi`.
// Use the server's normal packaged daemon resolver; never submit a prompt.
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, readFile, realpath, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
const [serverDirectory, ...extra] = process.argv.slice(2);
assert.ok(serverDirectory && extra.length === 0, 'Usage: pi-public-install-probe.mjs INSTALLED_SERVER_ROOT');
for (const key of Object.keys(process.env)) {
assert.ok(!/(?:API_KEY|TOKEN|SECRET|PASSWORD)$/.test(key), `Unexpected credential variable: ${key}`);
}
assert.equal(process.env.PAPERCLIP_RUNNER_BINARY, undefined);
assert.equal(process.env.NODE_OPTIONS, undefined);
assert.equal(process.env.NODE_PATH, undefined);
const server = await realpath(serverDirectory);
assert.equal(JSON.parse(await readFile(join(server, 'package.json'), 'utf8')).name, '@paperclipai/server');
const { resolvePaperclipRunnerBinary } = await import(pathToFileURL(join(server, 'dist/services/native-runtime/native-codex-runner.js')));
const { createCapabilityRunnerdCodexTransport } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/live/runnerd-codex-transport.js')));
const { QUALIFIED_ACPX_PROFILES } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/drivers/acpx/qualified-profiles.js')));
assert.equal(QUALIFIED_ACPX_PROFILES.pi.agentProfileVersion, 19);
assert.equal(QUALIFIED_ACPX_PROFILES.pi.commandDigest, 'sha256:b7647ebf97f802ca053ec3384c912bf0e8d18eba308d27397bb1d95a37220825');
assert.equal(Object.hasOwn(QUALIFIED_ACPX_PROFILES.pi, 'qualificationModel'), false);
assert.equal(Object.hasOwn(QUALIFIED_ACPX_PROFILES.pi, 'reportedModelId'), false);
const daemon = resolvePaperclipRunnerBinary();
assert.equal(await realpath(daemon), join(server, 'dist/vendor/paperclip-runner/bin/paperclip-runnerd'));
const root = await mkdtemp(join(tmpdir(), 'pi-public-install-probe-'));
const evidence = [];
const started = performance.now();
const watchdog = setTimeout(() => { console.error('Pi public-install probe exceeded its cleanup deadline'); process.exit(1); }, 85_000);
let bundle;
let failure;
let settledMs;
try {
const workspace = join(root, 'workspace'); await mkdir(workspace);
bundle = createCapabilityRunnerdCodexTransport({
provider: 'acpx', acpxAgent: 'pi', piThinkingLevel: 'low', acpxPermissionMode: 'deny-all',
runnerBinary: daemon, stateDirectory: join(root, 'state'),
environment: { PATH: '/usr/bin:/bin', LANG: 'C.UTF-8' },
onEvidence: value => evidence.push(value),
});
try {
await bundle.transport.request('initialize', { clientInfo: { name: 'pi-public-install-verification', version: '1' } });
await bundle.transport.request('thread/start', {
cwd: workspace, model: 'openrouter/deepseek/deepseek-v4-flash-0731',
baseInstructions: 'Credential-free admission probe. No prompt is submitted.',
permissions: 'paperclip-runner-workspace-read-only', dynamicTools: [],
});
} catch (error) { failure = String(error); }
settledMs = Math.round(performance.now() - started);
await bundle.transport.close();
assert.match(failure ?? '', /session\.open failed/);
assert.match(failure, /retryable=false, classification=session_ensure_failed/);
assert.doesNotMatch(failure, /timed out/);
assert.ok(settledMs < 60_000, `Pi admission took ${settledMs} ms`);
assert.ok(evidence.some(item => item.runnerExited === true && item.runnerExitCode === 0));
for (const item of evidence) assert.deepEqual(item.childEnvironmentKeys, ['LANG', 'PATH']);
const state = JSON.parse(await readFile(join(root, 'state/runner/acpx-provider-state.json'), 'utf8'));
assert.equal(state.descriptor.agent, 'pi');
assert.equal(state.descriptor.agentRuntimeVersion, '1.0.0');
assert.equal(state.descriptor.piThinkingLevel, 'low');
assert.equal(state.activeTurnId, null);
assert.equal(state.identity, null);
assert.equal(state.providerExitUnconfirmed, false);
console.log(JSON.stringify({ schema: 'paperclip.pi.public-npm-install.v1', target: `${process.platform}-${process.arch}`, normalPackagedDaemon: true, exactPiProfile: 19, runtime: '1.0.0', credentials: 'none', promptCalls: 0, settledMs, cleanRunnerExit: true }));
} finally {
try { await bundle?.transport.close(); }
finally { clearTimeout(watchdog); await rm(root, { recursive: true, force: true }); }
}
@@ -0,0 +1,73 @@
// Retain normal build/pack outputs for installed Product E2E and Runner evals.
// This runs only after the clean public installation and Pi admission pass.
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { basename, join, resolve } from 'node:path';
export function retainRunnerQualificationPackages({ repo, output, sourceRevision, releaseVersion, publicArchives, env = process.env }) {
assert.match(sourceRevision, /^[a-f0-9]{40}$/);
assert.equal(JSON.parse(readFileSync(join(repo, 'server/dist/build-info.json'))).commit, sourceRevision);
assert.ok(publicArchives.some(a => a.name === '@paperclipai/server'));
assert.ok(publicArchives.some(a => a.name === 'paperclipai'));
assert.ok(publicArchives.some(a => a.name === '@paperclipai/plugin-sdk'));
assert.equal(new Set(publicArchives.map(a => a.name)).size, publicArchives.length);
output = resolve(output);
assert.ok(!existsSync(output), 'Never replace retained qualification packages');
mkdirSync(output, { recursive: true });
const staging = mkdtempSync(join(tmpdir(), 'paperclip-qualification-pack-'));
const archives = [];
const hash = file => createHash('sha256').update(readFileSync(file)).digest('hex');
const retain = (name, file) => {
const destination = join(output, basename(file));
assert.ok(!existsSync(destination), 'Duplicate archive filename');
cpSync(file, destination);
archives.push({ name, file: basename(file), sha256: hash(destination) });
};
const pack = (directory, name, ignoreScripts) => {
const previous = new Set(readdirSync(staging));
execFileSync('npm', ['pack', ...(ignoreScripts ? ['--ignore-scripts'] : []), '--pack-destination', staging], {
cwd: directory, env: { ...env, npm_config_cache: join(staging, 'npm-cache'), npm_config_ignore_scripts: String(ignoreScripts) }, maxBuffer: 32 * 1024 * 1024,
});
const files = readdirSync(staging).filter(file => file.endsWith('.tgz') && !previous.has(file));
assert.equal(files.length, 1);
retain(name, join(staging, files[0]));
};
try {
for (const archive of publicArchives) retain(archive.name, archive.file);
// Match the plugin's normal release prepack. Its source manifest omits
// the SDK dependency; the hook adds the publishable SDK version.
const pluginRelative = 'packages/plugins/sandbox-providers/daytona';
const plugin = join(staging, pluginRelative);
mkdirSync(plugin, { recursive: true });
cpSync(join(repo, pluginRelative, 'dist'), join(plugin, 'dist'), { recursive: true });
const pluginManifest = JSON.parse(readFileSync(join(repo, pluginRelative, 'package.json')));
writeFileSync(join(plugin, 'package.json'), JSON.stringify({ ...pluginManifest, version: releaseVersion }));
mkdirSync(join(staging, 'scripts'));
cpSync(join(repo, 'scripts/generate-plugin-package-json.mjs'), join(staging, 'scripts/generate-plugin-package-json.mjs'));
mkdirSync(join(staging, 'packages/plugins/sdk'), { recursive: true });
const sdk = JSON.parse(readFileSync(join(repo, 'packages/plugins/sdk/package.json')));
writeFileSync(join(staging, 'packages/plugins/sdk/package.json'), JSON.stringify({ ...sdk, version: releaseVersion }));
pack(plugin, pluginManifest.name, false);
// The separate private eval SDK is already built by pnpm build. Packing
// it does not substitute for the public server's packaged runner.
const runner = join(staging, 'runner');
mkdirSync(runner);
const runnerManifest = JSON.parse(readFileSync(join(repo, 'packages/paperclip-runner/package.json')));
for (const file of runnerManifest.files) {
const input = join(repo, 'packages/paperclip-runner', file);
if (existsSync(input)) cpSync(input, join(runner, file), { recursive: true });
}
assert.ok(existsSync(join(runner, 'dist/cli/eval-session.js')));
writeFileSync(join(runner, 'package.json'), JSON.stringify(runnerManifest));
pack(runner, runnerManifest.name, true);
assert.equal(new Set(archives.map(a => a.name)).size, archives.length);
const receipt = { schema: 'paperclip.runner.qualification-packages/v1', sourceRevision, releaseVersion, publicInstallationVerified: true, piAdmissionVerified: true, providerCalls: 0, archives };
writeFileSync(join(output, 'manifest.json'), JSON.stringify(receipt, null, 2) + '\n');
return receipt;
} finally {
rmSync(staging, { recursive: true, force: true });
}
}
+25
View File
@@ -10,6 +10,7 @@ import { basename, dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { materializePublishManifest, prepareBundledPackage } from './prepare-bundled-package.mjs';
import { GROK_PUBLIC_INSTALL_IMAGE, GROK_PUBLIC_INSTALL_LIFECYCLE, grokConsumerDockerArgs } from './grok-public-install-sandbox.mjs';
import { retainRunnerQualificationPackages } from './retain-runner-qualification-packages.mjs';
const repo = resolve(dirname(fileURLToPath(import.meta.url)), '..');
assert.equal(process.platform, 'linux', 'Run this verification on disposable EC2 Linux, not a developer host');
const root = mkdtempSync(join(tmpdir(), 'paperclip-grok-public-install-'));
@@ -30,6 +31,7 @@ try {
}
}
visit('@paperclipai/server');
visit('paperclipai');
// Match release.sh's unified versioning in temporary staging directories.
// Source manifests remain untouched, including independently versioned SDKs.
run(process.execPath, [join(repo, 'scripts/build-standalone-public-packages.mjs')], repo);
@@ -115,6 +117,29 @@ try {
run(process.execPath, [join(repo, 'packages/paperclip-runner/scripts/provision-grok.mjs'), prerequisite]);
isolated(['node', '/packages/probe.mjs', 'present'], { prerequisite });
console.log(JSON.stringify({ schema: 'paperclip.grok.public-npm-install.v1', sourceRevision, releaseVersion, lifecycleScriptsEnabled: true, lifecycleSentinelVerified: true, lifecycleNetwork: 'none', consumerImage: GROK_PUBLIC_INSTALL_IMAGE, consumerUid, consumerLockPreserved: true, cleanNpmInstall: true, packageCount: needed.size, builtinLauncherPresent: true, separateGrokPackage: false, npmProvisionedBinary: false, missingPrerequisiteRejected: true, provisionedBinaryVerified: true, commandLeaseVerified: true, providerCalls: 0 }));
// Exercise Pi's public CLI and installed server, never a private workspace
// package or binary override. Public dependency downloads are explicit and
// isolated; the actual admission probe runs without a network or credentials.
const piProbe = join(assets, 'pi-public-install-probe.mjs');
cpSync(join(repo, 'scripts/pi-public-install-probe.mjs'), piProbe); chmodSync(piProbe, 0o644);
// Pi assembles a bundled runtime in scratch space before atomic publication.
// Keep the existing sandbox and memory bound; only this download needs more
// temporary capacity than the smaller offline lifecycle probes.
const setup = isolated(['node', '/consumer/node_modules/paperclipai/dist/index.js', 'runtime', 'setup', 'pi'], { download: true, temporarySizeMiB: 2048 }).toString();
const receipt = JSON.parse(setup.trim());
assert.equal(receipt.status, 'installed_verified');
assert.equal(receipt.target, 'linux-x64');
assert.equal(readFileSync(join(consumer, 'package-lock.json'), 'utf8'), consumerLock, 'Pi setup must preserve the consumer dependency graph');
// Verified launch leases also materialize the runtime in private scratch.
// Docker defaults tmpfs to noexec. The offline admission probe must execute
// its verified private snapshot while keeping lifecycle/download scratch noexec.
console.log(isolated(['node', '/packages/pi-public-install-probe.mjs', '/consumer/node_modules/@paperclipai/server'], { temporarySizeMiB: 2048, temporaryExecutable: true }).toString().trim());
if (process.env.PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR) {
const retained = retainRunnerQualificationPackages({ repo, output: process.env.PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR, sourceRevision, releaseVersion, env,
publicArchives: [...needed].map((name, index) => ({ name, file: tarballs[index] })),
});
console.log(JSON.stringify(retained));
}
} finally {
rmSync(root, { recursive: true, force: true });
}