feat(runner): qualify native Codex platforms and cache hosted builds

Extract the tested runtime and installed-package prerequisite without changing agent creation defaults or experimental gates.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 15:15:01 -05:00
1 parent 7d9b93aee2
commit ddcd746eb6
40 files changed
+3474 -565

No files matched your search

@@ -1,16 +1,64 @@
import assert from 'node:assert/strict';
import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
import { isWorkspacePackage, parseWorkspaceEntries } from '../build-standalone-public-packages.mjs';
import { GROK_PUBLIC_INSTALL_IMAGE, GROK_PUBLIC_INSTALL_LIFECYCLE, assertInstalledCodexPlatformProvenance, assertNoBundledCodexPayloads, grokConsumerDockerArgs, installedCodexProbeSource } from '../grok-public-install-sandbox.mjs';
import { GROK_PUBLIC_INSTALL_IMAGE, GROK_PUBLIC_INSTALL_LIFECYCLE, assertInstalledCodexPlatformProvenance, assertInstalledNativeConsumerContinuity, assertNoBundledCodexPayloads, grokConsumerDockerArgs, installedCodexProbeSource, installedDaemonProbeSource, macNodeHeadersDirectory, macPublicInstallLifecycleProfile, macInstalledStartupProbeSource } from '../grok-public-install-sandbox.mjs';
import { verifyPiPackagedDaemon } from '../pi-public-install-probe.mjs';
import { runnerBinaryTarget } from '../../packages/paperclip-runner/src/live/runner-binary.ts';
const paths = { assets: '/private/staging/assets', consumer: '/private/staging/consumer', cache: '/private/staging/cache', uid: 1001, gid: 1001 };
const values = (args, flag) => args.flatMap((value, index) => value === flag ? [args[index + 1]] : []);
test('native host startup preserves the consumer lock and both executable byte identities', () => {
const before = { lockSha256: 'a'.repeat(64), runnerSha256: 'b'.repeat(64), codexCommandSha256: 'c'.repeat(64), codexNativeExecutableSha256: 'e'.repeat(64) };
assert.doesNotThrow(() => assertInstalledNativeConsumerContinuity(before, { ...before }));
for (const field of Object.keys(before)) {
assert.throws(() => assertInstalledNativeConsumerContinuity(before, { ...before, [field]: 'd'.repeat(64) }), new RegExp(field));
assert.throws(() => assertInstalledNativeConsumerContinuity({ ...before, [field]: '' }, { ...before, [field]: '' }), new RegExp(field));
}
const source = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
const arm = source.split('async function verifyLinuxArmConsumer')[1].split("if (process.argv[2] === '--linux-arm64-consumer')")[0];
assert.match(arm, /const hostEnv = \{ PATH: .*HOME: home, TMPDIR: hostTmp, LC_ALL: 'C', TZ: 'UTC' \}/);
assert.match(arm, /execFileSync\(process.execPath, \[file\], \{ cwd: consumer, env: hostEnv/);
assert.ok(arm.indexOf('isolated(GROK_PUBLIC_INSTALL_LIFECYCLE)') < arm.indexOf("hostProbe('host-startup-probe.mjs'"));
assert.ok(arm.indexOf('const before =') < arm.indexOf("hostProbe('host-startup-probe.mjs'"));
assert.ok(arm.indexOf("hostProbe('host-startup-probe.mjs'") < arm.indexOf('const after ='));
assert.match(arm, /assertInstalledNativeConsumerContinuity\(before, after\)/);
assert.doesNotMatch(arm, /externallySandboxed|externalSandbox|sudo|sysctl|--privileged|seccomp=unconfined|unshare/);
});
test('Mac offline lifecycle uses only the matching executing Node SDK', () => {
const root = realpathSync(mkdtempSync(join(tmpdir(), 'mac-node-headers-test-')));
try {
const prefix = join(root, 'sdk'), executable = join(prefix, 'bin/node'), include = join(prefix, 'include/node');
mkdirSync(join(prefix, 'bin'), { recursive: true }); mkdirSync(include, { recursive: true });
writeFileSync(executable, 'fixture Node executable');
writeFileSync(join(include, 'node.h'), '#include "node_version.h"\n');
const header = '#define NODE_MAJOR_VERSION 24\n#define NODE_MINOR_VERSION 20\n#define NODE_PATCH_VERSION 0\n';
writeFileSync(join(include, 'node_version.h'), header);
assert.equal(macNodeHeadersDirectory(executable, '24.20.0'), prefix);
assert.throws(() => macNodeHeadersDirectory(executable, '24.19.0'), /must match the executing Node/);
assert.throws(() => macNodeHeadersDirectory(executable, '24.20.0-custom'), /exact release version/);
writeFileSync(join(include, 'node_version.h'), header + '#define NODE_PATCH_VERSION 0\n');
assert.throws(() => macNodeHeadersDirectory(executable, '24.20.0'), /declare one patch version/);
writeFileSync(join(include, 'node_version.h'), header);
rmSync(join(include, 'node.h'));
assert.throws(() => macNodeHeadersDirectory(executable, '24.20.0'), /ENOENT/);
writeFileSync(join(root, 'outside-node.h'), '#include "node_version.h"\n');
symlinkSync(join(root, 'outside-node.h'), join(include, 'node.h'));
assert.throws(() => macNodeHeadersDirectory(executable, '24.20.0'), /inside the executing Node prefix/);
const source = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
assert.ok(source.includes('macNodeHeadersDirectory(process.execPath, process.versions.node)'));
assert.ok(source.includes('consumer, { ...env, npm_config_nodedir: nodeHeaders }'), 'SDK is bound only to the deferred lifecycle environment');
assert.equal(source.split('npm_config_nodedir:').length, 2);
} finally { rmSync(root, { recursive: true, force: true }); }
});
test('lifecycle execution has no network, host credentials, checkout, or elevated privileges', () => {
const args = grokConsumerDockerArgs({ ...paths, command: GROK_PUBLIC_INSTALL_LIFECYCLE });
assert.deepEqual(values(args, '--network'), ['none']);
@@ -85,7 +133,9 @@ test('verification never elevates PR-controlled provisioning or cleanup on the h
test('the image and Grok downloads start before staging and join without weakening the npm checks', () => {
const source = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
const position = fragment => { const index = source.indexOf(fragment); assert.notEqual(index, -1, `verifier must contain ${fragment}`); return index; };
// The optional Mac consumer owns separate scratch and no Linux prefetches.
const linuxSource = source.slice(source.indexOf("assert.equal(process.platform, 'linux'"));
const position = fragment => { const index = linuxSource.indexOf(fragment); assert.notEqual(index, -1, `verifier must contain ${fragment}`); return index; };
// Exactly the pinned consumer image is pulled, for the platform the probes run on.
const imagePull = position("prefetch('image', 'docker', ['pull', '--platform', 'linux/amd64', GROK_PUBLIC_INSTALL_IMAGE])");
// Grok is provisioned into the prefetch directory, never straight into `native/grok`.
@@ -109,7 +159,7 @@ test('the image and Grok downloads start before staging and join without weakeni
// cleanup waits for them before removing the directory they write into.
assert.ok(source.includes("import { startPrefetch, stopPrefetches } from './prefetch-child.mjs'"));
assert.ok(position('await stopPrefetches(prefetches)') < position('rmSync(root, { recursive: true, force: true })'));
assert.equal(source.match(/rmSync\(root/g).length, 1);
assert.equal(linuxSource.match(/rmSync\(root/g).length, 1);
});
test('the bundled-dependency installs run together while the other packages are copied, and pack in order', () => {
@@ -137,7 +187,8 @@ test('the bundled-dependency installs run together while the other packages are
const checks = position("for (const tarball of tarballs) assertNoBundledCodexPayloads(run('tar', ['-tzf', tarball])");
const copied = position('for (const tarball of tarballs) {');
assert.ok(bundledSources < start && start < registered && registered < otherSources && otherSources < join && join < packs && packs < reordered && reordered < checks && checks < copied);
assert.equal(source.match(/run\('npm', \['pack'/g).length, 1, 'only the lifecycle sentinel is packed in-process');
const packagingLane = source.slice(position("} else {\nassert.equal(process.platform, 'linux'"));
assert.equal(packagingLane.match(/run\('npm', \['pack'/g).length, 1, 'only the lifecycle sentinel is packed in-process in the packaging lane');
assert.ok(source.includes("destination: join(root, `pack-${index}`)"), 'each pack writes into its own directory');
// The timing line reports the split staging phase, each install, and the
// pack step with what the packs would have cost one after another.
@@ -201,6 +252,14 @@ test('the installed Codex probe exercises the public export and rejects incomple
const platformPath = `node_modules/@openai/codex-${target}`;
const platform = join(consumer, platformPath); mkdirSync(platform, { recursive: true });
const nativeVendor = join(platform, 'vendor'); mkdirSync(nativeVendor);
const triple = { 'linux-x64': 'x86_64-unknown-linux-musl', 'linux-arm64': 'aarch64-unknown-linux-musl',
'darwin-x64': 'x86_64-apple-darwin', 'darwin-arm64': 'aarch64-apple-darwin', 'win32-x64': 'x86_64-pc-windows-msvc', 'win32-arm64': 'aarch64-pc-windows-msvc' }[target];
const nativeBytes = Buffer.alloc(64);
if (process.platform === 'darwin') { nativeBytes.writeUInt32LE(0xfeedfacf); nativeBytes.writeUInt32LE(process.arch === 'arm64' ? 0x0100000c : 0x01000007, 4); }
else if (process.platform === 'win32') { nativeBytes.set([0x4d, 0x5a]); nativeBytes.writeUInt32LE(40, 60); nativeBytes.writeUInt32LE(0x00004550, 40); nativeBytes.writeUInt16LE(process.arch === 'arm64' ? 0xaa64 : 0x8664, 44); }
else { nativeBytes.set([0x7f, 0x45, 0x4c, 0x46, 2, 1]); nativeBytes.writeUInt16LE(process.arch === 'arm64' ? 183 : 62, 18); }
const nativeFile = join(nativeVendor, triple, 'bin', process.platform === 'win32' ? 'codex.exe' : 'codex');
mkdirSync(join(nativeVendor, triple, 'bin'), { recursive: true }); writeFileSync(nativeFile, nativeBytes, { mode: 0o755 });
const manifest = { name: '@openai/codex', version: `0.160.0-${target}`, os: [process.platform], cpu: [process.arch] };
writeFileSync(join(platform, 'package.json'), JSON.stringify(manifest));
const entry = { name: manifest.name, version: manifest.version,
@@ -209,6 +268,8 @@ test('the installed Codex probe exercises the public export and rejects incomple
writeFileSync(join(consumer, 'package-lock.json'), JSON.stringify(lock));
const server = join(consumer, 'node_modules/@paperclipai/server');
const vendor = join(server, 'dist/vendor/paperclip-runner'), drivers = join(vendor, 'drivers/acpx'); mkdirSync(drivers, { recursive: true });
mkdirSync(join(vendor, 'live'));
writeFileSync(join(vendor, 'live/runner-binary.js'), `export const runnerBinaryTarget = ${runnerBinaryTarget.toString()};`);
writeFileSync(join(server, 'package.json'), JSON.stringify({ name: '@paperclipai/server', type: 'module' }));
const integrity = join(drivers, 'installation-integrity.js');
const security = join(vendor, 'drivers/codex/codex-security-config.js'); mkdirSync(join(vendor, 'drivers/codex'));
@@ -233,6 +294,24 @@ test('the installed Codex probe exercises the public export and rejects incomple
assert.equal(JSON.parse(result.stdout).pinnedCodexCommandVerified, true);
assert.equal(JSON.parse(result.stdout).codexPlatformPackageSource, 'official npm');
assert.equal(JSON.parse(result.stdout).codexConsumerPlatform, target);
assert.equal(JSON.parse(result.stdout).codexCommandSha256, createHash('sha256').update(readFileSync(command)).digest('hex'));
assert.equal(JSON.parse(result.stdout).codexNativeExecutableSha256, createHash('sha256').update(nativeBytes).digest('hex'));
const original = JSON.parse(result.stdout);
nativeBytes[63] = 1; writeFileSync(nativeFile, nativeBytes);
result = run(); assert.equal(result.status, 0, result.stderr);
const changed = JSON.parse(result.stdout);
assert.equal(changed.codexCommandSha256, original.codexCommandSha256, 'native mutation does not change the npm launcher');
assert.notEqual(changed.codexNativeExecutableSha256, original.codexNativeExecutableSha256);
assert.throws(() => assertInstalledNativeConsumerContinuity({ lockSha256: 'a'.repeat(64), runnerSha256: 'b'.repeat(64), ...original },
{ lockSha256: 'a'.repeat(64), runnerSha256: 'b'.repeat(64), ...changed }), /codexNativeExecutableSha256/);
nativeBytes[63] = 0; writeFileSync(nativeFile, nativeBytes);
rmSync(nativeFile);
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /one native executable/);
const foreignNative = join(root, 'foreign-codex'); writeFileSync(foreignNative, nativeBytes, { mode: 0o755 });
symlinkSync(foreignNative, nativeFile);
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /bounded regular file in its selected platform package/);
rmSync(nativeFile); writeFileSync(nativeFile, nativeBytes, { mode: 0o755 });
result = run(); assert.equal(result.status, 0, result.stderr);
assert.equal(JSON.parse(result.stdout).codexQualifiedInstallationVerified, true);
assert.equal(JSON.parse(result.stdout).codexCommandLeaseVerified, true);
assert.equal(JSON.parse(result.stdout).codexNativeSandboxResourcesVerified, true);
@@ -253,6 +332,20 @@ test('the installed Codex probe exercises the public export and rejects incomple
writeFileSync(legacyCommand, '#!/bin/sh\nexit 0\n', { mode: 0o755 });
writeFileSync(index, `export const resolvePinnedCodexCommand = () => ${JSON.stringify(legacyCommand)};`);
result = run(); assert.notEqual(result.status, 0); assert.match(result.stderr, /Selected Codex platform version must match the qualified pin/);
// Native Codex uses protocol compatibility, not the ACPX version allowlist.
// Keep exact official-npm host provenance and the selected resource boundary.
mkdirSync(join(legacy, 'vendor'));
const legacyNative = join(legacy, 'vendor', triple, 'codex', process.platform === 'win32' ? 'codex.exe' : 'codex');
mkdirSync(join(legacy, 'vendor', triple, 'codex'), { recursive: true }); writeFileSync(legacyNative, nativeBytes, { mode: 0o755 });
writeFileSync(legacyCommand, '#!/bin/sh\nprintf "codex-cli 0.156.1\\n"\n', { mode: 0o755 });
writeFileSync(security, securitySource.replaceAll(JSON.stringify(nativeVendor), JSON.stringify(join(legacy, 'vendor'))));
writeFileSync(probe, installedCodexProbeSource(index, consumer, undefined, { nativeCodex: true }));
result = run(); assert.equal(result.status, 0, result.stderr);
assert.equal(JSON.parse(result.stdout).installedCodexVersion, 'codex-cli 0.156.1');
assert.equal(JSON.parse(result.stdout).codexQualifiedInstallationVerified, undefined);
writeFileSync(probe, installedCodexProbeSource(index, consumer, '0.160.0'));
writeFileSync(security, securitySource);
writeFileSync(index, `import { writeFileSync } from 'node:fs'; export const resolvePinnedCodexCommand = () => {
writeFileSync(${JSON.stringify(join(platform, 'package.json'))}, ${JSON.stringify(JSON.stringify({ ...manifest, altered: true }))});
return ${JSON.stringify(command)}; };`);
@@ -308,6 +401,7 @@ test('actual tar member checks allow Codex JavaScript but reject nested platform
assert.doesNotThrow(() => assertNoBundledCodexPayloads(javascript));
for (const payload of [
'package/node_modules/@openai/codex-linux-x64/package.json',
'package/node_modules/@openai/codex-linux-arm64/package.json',
'package/node_modules/bridge/node_modules/@openai/codex-darwin-arm64/package.json',
'package/node_modules/@openai/codex-win32-x64/package.json',
'package/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/codex/codex',
@@ -320,24 +414,129 @@ test('actual tar member checks allow Codex JavaScript but reject nested platform
});
test('consumer provenance rejects bundled, foreign, inconsistent and non-official Codex payloads', () => {
const path = 'node_modules/@openai/codex-linux-x64';
const manifest = { name: '@openai/codex', version: '0.160.0-linux-x64', os: ['linux'], cpu: ['x64'] };
for (const target of ['linux-x64', 'linux-arm64']) {
const path = `node_modules/@openai/codex-${target}`;
const manifest = { name: '@openai/codex', version: `0.160.0-${target}`, os: ['linux'], cpu: [target.slice('linux-'.length)] };
const entry = { version: manifest.version, resolved: `https://registry.npmjs.org/@openai/codex/-/codex-${manifest.version}.tgz`,
integrity: `sha512-${Buffer.alloc(64, 1).toString('base64')}` };
const check = (value = entry, installed = [{ path, manifest }]) => assertInstalledCodexPlatformProvenance({ packages: { [path]: value } }, installed, 'linux-x64');
const check = (value = entry, installed = [{ path, manifest }]) => assertInstalledCodexPlatformProvenance({ packages: { [path]: value } }, installed, target);
assert.equal(check().codexConsumerHostOnly, true);
const legacyManifest = { ...manifest, version: '0.156.1-linux-x64' };
const legacyManifest = { ...manifest, version: `0.156.1-${target}` };
assert.equal(check({ ...entry, version: legacyManifest.version,
resolved: 'https://registry.npmjs.org/@openai/codex/-/codex-0.156.1-linux-x64.tgz' }, [{ path, manifest: legacyManifest }]).codexConsumerHostOnly, true);
assert.throws(() => check(entry, [{ path, manifest: { ...manifest, version: '^0.160.0-linux-x64' } }]), /exact host version/);
resolved: `https://registry.npmjs.org/@openai/codex/-/codex-0.156.1-${target}.tgz` }, [{ path, manifest: legacyManifest }]).codexConsumerHostOnly, true);
assert.throws(() => check(entry, [{ path, manifest: { ...manifest, version: `^0.160.0-${target}` } }]), /exact host version/);
for (const change of [
{ inBundle: true }, { link: true }, { version: '0.156.1-linux-x64' },
{ resolved: 'https://example.com/@openai/codex/-/codex-0.160.0-linux-x64.tgz' },
{ resolved: 'https://registry.npmjs.org/@openai/codex/-/codex-0.156.1-linux-x64.tgz' },
{ resolved: 'https://user@registry.npmjs.org/@openai/codex/-/codex-0.160.0-linux-x64.tgz' },
{ inBundle: true }, { link: true }, { version: `0.156.1-${target}` },
{ resolved: `https://example.com/@openai/codex/-/codex-0.160.0-${target}.tgz` },
{ resolved: `https://registry.npmjs.org/@openai/codex/-/codex-0.156.1-${target}.tgz` },
{ resolved: `https://user@registry.npmjs.org/@openai/codex/-/codex-0.160.0-${target}.tgz` },
{ integrity: undefined }, { integrity: 'sha512-AA==' },
]) assert.throws(() => check({ ...entry, ...change }));
assert.throws(() => check(entry, []), /must install its host/);
assert.throws(() => check(entry, [{ path: 'node_modules/@openai/codex-darwin-arm64', manifest }]), /only the host/);
assert.throws(() => check(entry, [{ path, manifest: { ...manifest, name: 'untrusted' } }]), /identity/);
}
});
test('hosted Mac lifecycle denies OS networking and writes outside owned scratch', () => {
const profile = macPublicInstallLifecycleProfile('/Users/runner/work/_temp/owned-check');
assert.match(profile, /\(deny network\*\)/);
assert.match(profile, /\(deny file-write\*/);
assert.match(profile, /subpath "\/Users\/runner\/work\/_temp\/owned-check"/);
assert.throws(() => macPublicInstallLifecycleProfile('relative'), /absolute/);
assert.throws(() => macPublicInstallLifecycleProfile('/tmp/owned\0bad'), /absolute/);
const source = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
assert.match(source, /process.env.GITHUB_ACTIONS, 'true'/);
assert.match(source, /networkProbe, 'allowed'/);
assert.match(source, /networkProbe, 'denied'/);
assert.match(source, /error.code,'EPERM'/);
assert.match(source, /'rebuild', '--offline', '--ignore-scripts=false', '--dangerously-allow-all-scripts'/);
assert.match(source, /readFileSync\(sentinel, 'utf8'\), 'ok'/);
assert.match(source, /readFileSync\(join\(consumer, 'package-lock.json'\), 'utf8'\), lock/);
assert.match(source, /finally \{ rmSync\(root, \{ recursive: true, force: true \}\); \}/);
});
test('installed daemon proof uses the production host resolver and complete source-bound manifest', () => {
const source = installedDaemonProbeSource('/consumer/server/dist/vendor/paperclip-runner', '/consumer', 'a'.repeat(40));
assert.match(source, /defaultCapabilityRunnerdBinary\(\)/);
assert.match(source, /Object.keys\(manifest.platforms\).sort\(\), targets.sort\(\)/);
assert.match(source, /manifest.sourceRevision, "a{40}"/);
assert.match(source, /runnerBinaryTarget\(bytes\), target/);
assert.match(source, /createHash\('sha256'\)/);
assert.match(source, /metadata.binaryContractVersion, expected.contracts.runnerdArtifact/);
assert.match(source, /'linux-arm64'/);
assert.doesNotMatch(source, /PAPERCLIP_RUNNERD_BINARY|runner\/target\/debug|execFileSync\('paperclip-runnerd'/);
});
test('native ARM clean consumers keep the existing container and installed-runtime boundaries', () => {
const args = grokConsumerDockerArgs({ ...paths, platform: 'linux/arm64', command: GROK_PUBLIC_INSTALL_LIFECYCLE });
assert.deepEqual(values(args, '--platform'), ['linux/arm64']);
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.ok(values(args, '--mount').some(value => value.endsWith('dst=/packages,readonly')));
assert.throws(() => grokConsumerDockerArgs({ ...paths, platform: 'linux/arm/v7', command: [] }), /supported native Linux platform/);
const source = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
const arm = source.split('async function verifyLinuxArmConsumer(')[1].split("if (process.argv[2] === '--linux-arm64-consumer')")[0];
assert.match(arm, /process.platform \+ '-' \+ process.arch, 'linux-arm64'/);
assert.match(arm, /const platform = 'linux\/arm64'/);
assert.match(arm, /isolated\(GROK_PUBLIC_INSTALL_LIFECYCLE\)/);
assert.match(arm, /installedCodexProbeSource\(installed \+ '\/index.js', '\/consumer', undefined, \{ nativeCodex: true \}\)/);
assert.match(arm, /tests\/runner-e2e\/installed-cli.ts/);
assert.match(arm, /process.versions.node.split\('\.'\)\[0\], '24'/);
assert.match(arm, /macInstalledStartupProbeSource\(consumer, hostServer, daemon.sha256, join\(assets, 'installed-cli.ts'\)\)/);
assert.match(arm, /startupEnvironment: 'native host, same installed graph, isolated empty homes'/);
assert.doesNotMatch(arm, /stage-runner|runner\/target|PAPERCLIP_RUNNER_BINARY|prerequisite:/);
});
test('Mac startup wiring normalizes only validated manifest digests and uses server-scoped tooling', () => {
const source = macInstalledStartupProbeSource('/owned/consumer', '/owned/consumer/server', 'sha256:' + 'a'.repeat(64), '/source/tests/runner-e2e/installed-cli.ts');
assert.match(source, /expectedRunnerSha256:"a{64}"/);
assert.doesNotMatch(source, /expectedRunnerSha256:"sha256:/);
for (const digest of ['a'.repeat(64), 'sha256:bad', 'sha512:' + 'a'.repeat(64), 'sha256:' + 'A'.repeat(64)]) {
assert.throws(() => macInstalledStartupProbeSource('/owned/consumer', '/owned/consumer/server', digest, '/source/helper.ts'), /verified SHA-256/);
}
const verifier = readFileSync(new URL('../verify-grok-npm-install.mjs', import.meta.url), 'utf8');
assert.match(verifier, /\['--import', 'tsx', startupFile\], join\(repo, 'server'\)/);
const closedEnvironment = verifier.match(/const env = \{ PATH: `\$\{tools\}[\s\S]*?\};/)[0];
assert.doesNotMatch(closedEnvironment, /NODE_PATH|NODE_OPTIONS|\.\.\.process.env/);
});
test('Pi admission verifies assembled host daemon identity and preserves the flat source-package control', async () => {
const root = realpathSync(mkdtempSync(join(tmpdir(), 'pi-installed-daemon-'))), server = join(root, 'server');
const output = join(server, 'dist/vendor/paperclip-runner'), target = 'linux-x64', source = 'a'.repeat(40);
const binary = join(output, 'bin', target, 'paperclip-runnerd'), manifestPath = join(output, 'bin/release-manifest.json');
const bytes = Buffer.alloc(64); bytes.set([0x7f, 0x45, 0x4c, 0x46, 2, 1]); bytes.writeUInt16LE(62, 18);
const sha256 = value => `sha256:${createHash('sha256').update(value).digest('hex')}`;
const manifest = { schema: 'paperclip.runner.release-binaries.v1', sourceRevision: source,
platforms: { [target]: { path: `${target}/paperclip-runnerd`, sha256: sha256(bytes) } } };
const check = () => verifyPiPackagedDaemon(server, binary, target, runnerBinaryTarget);
try {
mkdirSync(join(output, 'bin', target), { recursive: true });
writeFileSync(binary, bytes); writeFileSync(join(server, 'dist/build-info.json'), JSON.stringify({ commit: source }));
writeFileSync(manifestPath, JSON.stringify(manifest));
await check();
for (const changed of [null, { ...manifest, schema: 'untrusted' }, { ...manifest, sourceRevision: 'b'.repeat(40) },
{ ...manifest, platforms: { [target]: { ...manifest.platforms[target], path: '../outside' } } },
{ ...manifest, platforms: { [target]: { ...manifest.platforms[target], sha256: 'sha256:' + '0'.repeat(64) } } }]) {
writeFileSync(manifestPath, JSON.stringify(changed)); await assert.rejects(check());
}
const wrongHeader = Buffer.from(bytes); wrongHeader.writeUInt16LE(183, 18);
writeFileSync(binary, wrongHeader);
writeFileSync(manifestPath, JSON.stringify({ ...manifest, platforms: { [target]: { ...manifest.platforms[target], sha256: sha256(wrongHeader) } } }));
await assert.rejects(check(), /linux-arm64/);
writeFileSync(binary, bytes); writeFileSync(manifestPath, JSON.stringify(manifest));
const outside = join(root, 'outside'); writeFileSync(outside, bytes);
rmSync(binary); symlinkSync(outside, binary); await assert.rejects(check());
rmSync(binary); writeFileSync(binary, bytes); rmSync(manifestPath);
const flat = join(output, 'bin/paperclip-runnerd'); writeFileSync(flat, bytes);
await verifyPiPackagedDaemon(server, flat, target, runnerBinaryTarget);
await assert.rejects(check(), /strictly equal/);
writeFileSync(manifestPath, 'null');
await assert.rejects(verifyPiPackagedDaemon(server, flat, target, runnerBinaryTarget), /manifest must be an object/);
} finally { rmSync(root, { recursive: true, force: true }); }
});
@@ -1,8 +1,10 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { spawn, spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { tmpdir } from "node:os";
import { fileURLToPath } from "node:url";
import test from "node:test";
import { createServer } from "node:http";
@@ -81,14 +83,14 @@ test("chaos verification isolates callers that verify the same source commit", (
test("canary reuses exact-source proof while stable keeps full verification", () => {
const releaseWorkflow = readWorkflow("release.yml");
const canary = releaseWorkflow.split(" verify_canary:\n")[1].split("\n publish_canary:")[0];
const canary = releaseWorkflow.match(/ verify_canary:\n[\s\S]*?(?=\n [a-z_]+:)/)[0];
assert.match(canary, /github\.repository == 'paperclipai\/paperclip' && github\.event_name == 'push' && github\.ref == 'refs\/heads\/master'/);
assert.match(canary, /actions: read/);
assert.match(canary, /ref: \$\{\{ github\.sha \}\}/);
assert.match(canary, /SOURCE_SHA: \$\{\{ github\.sha \}\}/);
assert.match(canary, /run: node scripts\/cloud-source-verification\.mjs "\$SOURCE_SHA"/);
assert.doesNotMatch(canary, /release-verify\.yml|continue-on-error|always\(\)/);
assert.match(releaseWorkflow, /publish_canary:\n\s+if: github\.event_name == 'push'\n\s+needs: verify_canary/);
assert.match(releaseWorkflow, /publish_canary:\n\s+if: .*github\.event_name == 'push'.*\n\s+needs: \[verify_canary, release_runner_source, release_runner_binaries\]/);
// The stable lane is gated on the stable channel since the nightly lane
// was added; a `needs:` line (for example a preflight job) may sit between
// the gate and the delegation.
@@ -742,6 +744,399 @@ test("direct protocol concurrency override only lowers the configured ceiling",
}
});
test("release daemon production uses exact-source native hosts and no publisher or provider permissions", () => {
const workflow = readWorkflow("release-verify.yml");
const producer = workflow.split(" runner_binaries:\n")[1];
const targets = workflow.split(" runner_binary_targets:\n")[1].split(" runner_binaries:\n")[0];
for (const [target, host] of [["linux-arm64", "ubuntu-24.04-arm"], ["darwin-arm64", "macos-15"], ["darwin-x64", "macos-15-intel"]]) {
assert.ok(targets.includes(`'${target}': '${host}'`));
}
assert.match(producer, /matrix: \$\{\{ fromJSON\(needs.runner_binary_targets.outputs.matrix\) \}\}/);
assert.match(targets, /Unknown exact-source daemon platform contract/);
assert.match(targets, /RELEASE_RUNNER_TARGETS/);
assert.doesNotMatch(targets, /secrets\.|id-token: write|contents: write|npm publish|environment:/);
assert.match(producer, /persist-credentials: false/);
assert.match(producer, /contents: read/);
assert.doesNotMatch(producer, /secrets\.|id-token: write|contents: write|npm publish|environment:/);
assert.match(producer, /\[\[ "\$SOURCE_SHA" =~ \^\[a-f0-9\]\{40\}\$ \]\]/);
assert.match(producer, /git rev-parse HEAD/);
assert.match(producer, /process.platform \+ "-" \+ process.arch/);
assert.match(producer, /\['native', \['build:binary'\]/);
const runner = JSON.parse(readFileSync(path.join(repoRoot, 'packages/paperclip-runner/package.json'), 'utf8'));
assert.match(runner.scripts['build:binary'], /cargo build --release --manifest-path runner\/Cargo.toml --locked/);
assert.match(runner.scripts['build:binary'], /&& node scripts\/stage-runner-binary.mjs$/);
assert.match(producer, /stage-release-runner-binaries.mjs record/);
for (const job of ["runner_chaos_evals", "typecheck", "general_tests", "serialized_tests", "runner_workflow_evals", "verify_paperclip_runner", "build"]) {
assert.ok(workflow.includes(`${job}:\n if: \${{ !inputs.runner_binaries_only }}`));
}
const release = readWorkflow("release.yml");
for (const job of ["publish_canary", "publish_nightly", "publish_beta", "preview_stable", "publish_stable"]) {
const block = release.split(` ${job}:\n`)[1].split(/\n [a-z_]+:\n/)[0];
assert.match(block, /needs: .*release_runner_binaries/);
assert.match(block, /pattern: \$\{\{ needs.release_runner_binaries.outputs.runner_artifact_prefix \}\}-\*/);
assert.match(block, /PAPERCLIP_RELEASE_RUNNER_ARTIFACTS_DIR/);
}
const script = readFileSync(path.join(repoRoot, 'scripts/release.sh'), 'utf8');
assert.ok(script.indexOf('assemble-daemons "$PAPERCLIP_RELEASE_RUNNER_ARTIFACTS_DIR"') < script.indexOf('scripts/build-standalone-public-packages.mjs'));
assert.match(script, /cp -R .*paperclip-runner\/dist\/bin\/\." .*server\/dist\/vendor\/paperclip-runner\/bin\//);
assert.match(script, /elif \[ "\$dry_run" = false \]; then[\s\S]*?release_fail "Public releases require/);
});
function nativeProducer() {
return readWorkflow('release-verify.yml').split(' runner_binaries:\n')[1];
}
test('native dependency caches are target-bound and unmerged master callers cannot save them', () => {
const producer = nativeProducer();
const cache = producer.split(' - name: Cache native Rust dependencies\n')[1].split('\n - ')[0];
assert.match(cache, /Swatinem\/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6/);
assert.match(cache, /workspaces: \$\{\{ steps.native_rust_workspace.outputs.path \}\} -> target/);
assert.match(cache, /shared-key: release-native-v2-\$\{\{ matrix.host \}\}-\$\{\{ matrix.target \}\}-\$\{\{ hashFiles\('packages\/paperclip-runner\/protocol\/\*\*', 'packages\/paperclip-runner\/src\/\*\*', 'packages\/paperclip-runner\/\*.json'\) \}\}/);
for (const option of ['cache-workspace-crates', 'cache-bin', 'cache-on-failure']) {
assert.match(cache, new RegExp(option + ': false'));
}
const expression = cache.match(/save-if: \$\{\{ (.+) \}\}/)?.[1];
assert.ok(expression, 'cache writes need a separate source trust gate');
const canSave = new Function('github', 'inputs', `return (${expression});`);
const github = { repository: 'paperclipai/paperclip', repository_id: '1170821064', event_name: 'push', ref: 'refs/heads/master', sha: 'a'.repeat(40) };
assert.equal(canSave(github, { ref: github.sha }), true);
for (const [overrides, input] of [
[{}, 'b'.repeat(40)], [{}, ''],
[{ event_name: 'workflow_dispatch' }, github.sha],
[{ event_name: 'workflow_dispatch' }, 'b'.repeat(40)],
[{ event_name: 'pull_request', ref: 'refs/pull/123/merge' }, github.sha],
[{ ref: 'refs/heads/topic' }, github.sha],
[{ repository: 'fork/paperclip' }, github.sha],
[{ repository_id: '1' }, github.sha],
]) assert.equal(canSave({ ...github, ...overrides }, { ref: input }), false);
assert.match(producer, /pinned="\$HOME\/paperclip-runner-rust"/);
assert.match(producer, /ln -s "\$GITHUB_WORKSPACE\/packages\/paperclip-runner\/runner" "\$pinned"/);
assert.match(producer, /echo "RUSTUP_TOOLCHAIN=\$toolchain" >> "\$GITHUB_ENV"/);
assert.doesNotMatch(producer, /cache-hit|continue-on-error/);
assert.ok(producer.indexOf('Bind immutable producer source and target') < producer.indexOf('Cache native Rust dependencies'));
assert.ok(producer.indexOf('Build TypeScript contract and host daemon concurrently') < producer.indexOf('Record actual daemon metadata and immutable bytes'));
});
function nativePreparationScript() {
const step = nativeProducer().split(' - name: Build TypeScript contract and host daemon concurrently\n')[1].split('\n - ')[0];
const script = step.match(/node --input-type=module <<'NODE'\n([\s\S]*?)\n NODE/)?.[1];
assert.ok(script);
return script.replace(/^ {10}/gm, '');
}
const fixtureProcessGone = pid => {
try { process.kill(pid, 0); } catch (error) { return error.code === 'ESRCH'; }
try { return /^\d+ \(.*\) Z /.test(readFileSync(`/proc/${pid}/stat`, 'utf8')); } catch { return false; }
};
const waitForFixture = async (predicate, label) => {
for (let attempt = 0; attempt < 200; attempt++) {
if (predicate()) return;
await new Promise(resolve => setTimeout(resolve, 25));
}
assert.fail(label);
};
// Execute the workflow's actual coordinator with two tiny local Node commands.
// Both must cross the start barrier before either can finish; no pnpm, Cargo,
// provider, registry, or Docker build runs in these fixtures.
async function withNativePreparationFixture(mode, check) {
const root = realpathSync(mkdtempSync(path.join(tmpdir(), 'native-preparation-')));
let controller;
try {
for (const directory of ['scripts', 'tools', 'packages/paperclip-runner', 'logs']) {
mkdirSync(path.join(root, directory), { recursive: true });
}
for (const helper of ['prefetch-child.mjs', 'prefetch-supervisor.mjs']) {
copyFileSync(path.join(repoRoot, 'scripts', helper), path.join(root, 'scripts', helper));
}
writeFileSync(path.join(root, 'prepare.mjs'), nativePreparationScript());
writeFileSync(path.join(root, 'tools/pnpm'), `#!${process.execPath}
const { spawn } = require('node:child_process');
const { existsSync, writeFileSync } = require('node:fs');
const { join } = require('node:path');
const root = process.env.FIXTURE_ROOT;
const name = process.argv.includes('build:binary') ? 'native' : 'typescript';
writeFileSync(join(root, name + '.started'), JSON.stringify({ pid: process.pid, cwd: process.cwd(), args: process.argv.slice(2) }));
const wait = setInterval(() => {
if (!existsSync(join(root, 'native.started')) || !existsSync(join(root, 'typescript.started'))) return;
clearInterval(wait);
if (process.env.FIXTURE_MODE === 'success') {
setTimeout(() => { writeFileSync(join(root, name + '.finished'), 'joined'); console.log(name + ' complete'); }, 150);
return;
}
const child = spawn(process.execPath, ['-e', \
"const fs=require('node:fs'); fs.writeFileSync(process.argv[1], String(process.pid)); setInterval(() => fs.writeFileSync(process.argv[2], String(Date.now())), 25)",\
join(root, name + '.descendant'), join(root, name + '.writing')], { stdio: 'inherit' });
child.on('exit', () => process.exit(0));
const barrier = setInterval(() => {
if (!existsSync(join(root, 'native.descendant')) || !existsSync(join(root, 'typescript.descendant'))) return;
clearInterval(barrier);
if (process.env.FIXTURE_MODE === name) { console.error(name + ' failed'); process.exit(23); }
}, 10);
}, 10);
`, { mode: 0o755 });
controller = spawn(process.execPath, ['prepare.mjs'], { cwd: root, env: {
...process.env, PATH: `${path.join(root, 'tools')}:${process.env.PATH}`, RUNNER_TEMP: path.join(root, 'logs'), FIXTURE_ROOT: root, FIXTURE_MODE: mode,
}, stdio: ['ignore', 'pipe', 'pipe'] });
let stdout = '', stderr = '';
controller.stdout.on('data', chunk => { stdout += chunk; });
controller.stderr.on('data', chunk => { stderr += chunk; });
const completed = new Promise((resolve, reject) => {
controller.once('error', reject);
controller.once('close', (status, signal) => resolve({ status, signal, stdout, stderr }));
});
await check({ root, controller, completed });
} finally {
if (controller?.exitCode === null && controller.signalCode === null) controller.kill('SIGKILL');
for (const name of ['native', 'typescript']) {
for (const filename of [name + '.started', name + '.descendant']) {
const file = path.join(root, filename);
if (!existsSync(file)) continue;
const data = readFileSync(file, 'utf8');
const pid = filename.endsWith('.started') ? JSON.parse(data).pid : Number(data);
if (!fixtureProcessGone(pid)) {
try { process.kill(pid, 'SIGKILL'); } catch (error) { if (error.code !== 'ESRCH') throw error; }
}
}
}
rmSync(root, { recursive: true, force: true });
}
}
test('native preparation overlaps independent outputs and joins both before recording', async () => {
await withNativePreparationFixture('success', async ({ root, completed }) => {
const result = await completed;
assert.equal(result.status, 0, result.stderr);
for (const name of ['native', 'typescript']) assert.equal(readFileSync(path.join(root, name + '.finished'), 'utf8'), 'joined');
assert.equal(JSON.parse(readFileSync(path.join(root, 'native.started'), 'utf8')).cwd, path.join(root, 'packages/paperclip-runner'));
assert.deepEqual(JSON.parse(readFileSync(path.join(root, 'native.started'), 'utf8')).args, ['build:binary']);
assert.deepEqual(JSON.parse(readFileSync(path.join(root, 'typescript.started'), 'utf8')).args, ['--filter', '@paperclipai/paperclip-runner', 'build:typescript']);
const results = result.stdout.split('\n').filter(line => line.startsWith('{')).map(line => JSON.parse(line));
assert.deepEqual(results.map(item => [item.build, item.status, item.exitCode]), [['typescript', 'fulfilled', 0], ['native', 'fulfilled', 0]]);
});
});
for (const name of ['typescript', 'native']) {
test(`${name} preparation failure fails the join and retires both owned process trees`, async () => {
await withNativePreparationFixture(name, async ({ root, completed }) => {
const result = await completed;
assert.equal(result.status, 1, result.stderr);
assert.match(result.stderr, new RegExp(name + ' prefetch failed \\(exit 23\\)'));
const results = result.stdout.split('\n').filter(line => line.startsWith('{')).map(line => JSON.parse(line));
assert.equal(results.length, 2);
assert.ok(results.every(item => item.status === 'rejected'));
for (const build of ['native', 'typescript']) {
const pid = Number(readFileSync(path.join(root, build + '.descendant'), 'utf8'));
await waitForFixture(() => fixtureProcessGone(pid), build + ' descendant outlived failed join');
}
});
});
}
test('canceling native preparation retires both supervisors and their descendants', { skip: process.platform === 'win32' }, async () => {
await withNativePreparationFixture('cancel', async ({ root, controller, completed }) => {
await waitForFixture(() => ['native', 'typescript'].every(name => existsSync(path.join(root, name + '.descendant'))), 'both native build trees must start');
controller.kill('SIGTERM');
const result = await completed;
assert.equal(result.signal, 'SIGTERM');
for (const name of ['native', 'typescript']) {
const pid = Number(readFileSync(path.join(root, name + '.descendant'), 'utf8'));
await waitForFixture(() => fixtureProcessGone(pid), name + ' descendant outlived cancellation');
}
});
});
test("premerge native qualification consumes the same Linux-packed npm bytes without publishing", () => {
const smoke = readWorkflow("release-smoke.yml");
assert.match(smoke, /smoke_service:\n\s+if: inputs.qualification_source_sha == ''/);
assert.match(smoke, /smoke:\n\s+if: inputs.qualification_source_sha == ''/);
const qualification = smoke.split(" qualification_daemons:\n")[1];
assert.doesNotMatch(qualification, /secrets\.|npm publish|id-token: write|contents: write|continue-on-error/);
assert.match(qualification, /runner_binaries_only: true/);
assert.match(qualification, /PAPERCLIP_RUNNER_PUBLIC_PACKS_DIR/);
assert.match(qualification, /if: \$\{\{ !cancelled\(\) && steps.pack_identity.outputs.available == 'true' \}\}/);
assert.match(qualification, /name: \$\{\{ inputs.artifact_name \}\}-public-packages/);
assert.match(qualification, /--mac-consumer/);
assert.match(qualification, /--linux-arm64-consumer/);
assert.match(qualification, /fromJSON\(needs.qualification_daemons.outputs.native_consumer_matrix\)/);
assert.match(qualification, /pnpm install --ignore-scripts --no-frozen-lockfile/);
});
test("native ARM qualification prepares its scoped distro sandbox before the existing consumer", () => {
const smoke = readWorkflow("release-smoke.yml");
const native = smoke.split(" qualification_native:\n")[1];
const preparation = native.split(" - name: Prepare Linux ARM sandbox prerequisites\n")[1].split("\n - ")[0];
assert.match(preparation, /if: \$\{\{ matrix\.target == 'linux-arm64' \}\}/);
assert.match(preparation, /timeout-minutes: 5/);
assert.match(preparation, /bash \.qualification-tooling\/scripts\/prepare-linux-arm64-qualification\.sh/);
const tooling = native.split(" - name: Checkout immutable ARM qualification tooling\n")[1].split("\n - ")[0];
assert.match(tooling, /if: \$\{\{ matrix\.target == 'linux-arm64' \}\}/);
assert.match(tooling, /repository: \$\{\{ job\.workflow_repository \}\}/);
assert.match(tooling, /ref: \$\{\{ job\.workflow_sha \}\}/);
assert.match(tooling, /sparse-checkout: scripts\/prepare-linux-arm64-qualification\.sh/);
assert.match(tooling, /sparse-checkout-cone-mode: false/);
assert.match(tooling, /persist-credentials: false/);
assert.doesNotMatch(tooling + preparation, /github\.workflow_sha|github\.sha|ref: (?:master|main)|ref: \$\{\{ inputs\.qualification_source_sha/);
assert.ok(native.indexOf("Checkout immutable ARM qualification tooling") < native.indexOf("Prepare Linux ARM sandbox prerequisites"));
assert.ok(native.indexOf("Prepare Linux ARM sandbox prerequisites") < native.indexOf("Verify host identity and actual clean npm consumer"));
assert.match(native, /path: \|\n\s+\$\{\{ runner\.temp \}\}\/native-public-install\.json\n\s+\$\{\{ runner\.temp \}\}\/paperclip-arm-bwrap-prerequisite\//);
assert.doesNotMatch(smoke.split(" qualification_daemons:\n")[0], /prepare-linux-arm64-qualification/,
"ordinary service and Docker smoke keep their existing setup");
});
test("ARM qualification binds separate product and reusable-workflow revisions even when the product lacks the helper", { skip: process.platform === "win32" }, () => {
const native = readWorkflow("release-smoke.yml").split(" qualification_native:\n")[1];
const step = native.split(" - name: Prepare Linux ARM sandbox prerequisites\n")[1].split("\n - ")[0];
const script = step.split(" run: |\n")[1].split("\n").map(line => line.startsWith(" ") ? line.slice(10) : line).join("\n");
const root = mkdtempSync(path.join(tmpdir(), "arm-separate-tooling-"));
try {
const tools = path.join(root, "tools"); mkdirSync(tools);
const tooling = path.join(root, ".qualification-tooling", "scripts"); mkdirSync(tooling, { recursive: true });
const helper = 'printf "%s\\n" "$TOOLING_SHA" > "$FIXTURE_PREPARED"\n';
writeFileSync(path.join(tooling, "prepare-linux-arm64-qualification.sh"), helper);
writeFileSync(path.join(tools, "git"), '#!/bin/bash\nif [[ "$1" == -C ]]; then printf "%s\\n" "$FIXTURE_TOOLING_HEAD"; else printf "%s\\n" "$FIXTURE_PRODUCT_HEAD"; fi\n', { mode: 0o755 });
const productSha = "a".repeat(40), toolingSha = "b".repeat(40);
for (const [productHead, toolingHead, expectedStatus] of [[productSha, toolingSha, 0], ["c".repeat(40), toolingSha, 1], [productSha, "c".repeat(40), 1], [productSha, "invalid", 1]]) {
const report = path.join(root, "report"); mkdirSync(report);
const prepared = path.join(root, "prepared");
const result = spawnSync("/bin/bash", ["-c", script], { cwd: root, encoding: "utf8", env: {
PATH: `${tools}:${path.dirname(process.execPath)}:/usr/bin:/bin`, RUNNER_TEMP: report,
SOURCE_SHA: productSha, TOOLING_SHA: toolingSha, TOOLING_REPOSITORY: "paperclipai/paperclip",
TOOLING_WORKFLOW_REF: "paperclipai/paperclip/.github/workflows/release-smoke.yml@refs/heads/qualification",
TOOLING_WORKFLOW_PATH: ".github/workflows/release-smoke.yml",
FIXTURE_PRODUCT_HEAD: productHead, FIXTURE_TOOLING_HEAD: toolingHead, FIXTURE_PREPARED: prepared,
// Reusable calls retain the caller context; it must not select tooling.
GITHUB_REPOSITORY: "different/caller", GITHUB_SHA: "d".repeat(40),
} });
assert.equal(result.status, expectedStatus, result.stderr);
assert.equal(existsSync(path.join(root, "scripts", "prepare-linux-arm64-qualification.sh")), false);
assert.equal(existsSync(prepared), expectedStatus === 0);
if (expectedStatus === 0) {
assert.equal(readFileSync(prepared, "utf8").trim(), toolingSha);
const identity = JSON.parse(readFileSync(path.join(report, "paperclip-arm-bwrap-prerequisite", "qualification-identity.json"), "utf8"));
assert.equal(identity.productSourceSha, productSha);
assert.equal(identity.toolingSourceSha, toolingSha);
assert.equal(identity.toolingRepository, "paperclipai/paperclip");
assert.equal(identity.toolingWorkflowPath, ".github/workflows/release-smoke.yml");
assert.equal(identity.helperSha256, createHash("sha256").update(helper).digest("hex"));
}
rmSync(report, { recursive: true, force: true });
rmSync(prepared, { force: true });
}
} finally { rmSync(root, { recursive: true, force: true }); }
});
test("Linux ARM prerequisite setup rejects other hosts before invoking tools and preserves scoped security", { skip: process.platform === "win32" }, () => {
const helper = path.join(repoRoot, "scripts/prepare-linux-arm64-qualification.sh");
const source = readFileSync(helper, "utf8");
const root = mkdtempSync(path.join(tmpdir(), "arm-prerequisite-guard-"));
try {
const tools = path.join(root, "tools"); mkdirSync(tools);
const sentinel = path.join(root, "invoked");
for (const command of ["dpkg", "uname", "mkdir", "mktemp", "sysctl", "sudo"]) {
writeFileSync(path.join(tools, command), '#!/bin/bash\nprintf "%s\\n" "$0" >> "$PREREQUISITE_SENTINEL"\nexit 85\n', { mode: 0o755 });
}
for (const [actions, runnerOs] of [["", "Linux"], ["false", "Linux"], ["true", "macOS"], ["true", "Windows"]]) {
const result = spawnSync("/bin/bash", [helper], { encoding: "utf8", env: {
PATH: tools, GITHUB_ACTIONS: actions, RUNNER_OS: runnerOs, RUNNER_TEMP: root, PREREQUISITE_SENTINEL: sentinel,
} });
assert.equal(result.status, 1);
assert.match(result.stderr, /requires a GitHub Actions Linux runner/);
assert.equal(existsSync(sentinel), false, "host guard must precede every external tool");
assert.equal(existsSync(path.join(root, "paperclip-arm-bwrap-prerequisite")), false);
}
assert.ok(source.indexOf('"${GITHUB_ACTIONS:-}" != true') < source.indexOf(". /etc/os-release"));
assert.match(source, /-o Acquire::AllowInsecureRepositories=false/);
assert.match(source, /download "\$package_name=\$package_version"/);
assert.match(source, /printf '%s %s\\n' "\$package_sha256" "\$package_file" \| sha256sum -c -/);
assert.match(source, /dpkg-deb --fsys-tarfile "\$package_file"/);
assert.match(source, /tar -xO \.\/usr\/bin\/bwrap/);
assert.match(source, /11d39094f044f0cda0febb3ad517b830301da6b2ce929664af09ee9e4dd264f9/);
assert.ok(source.indexOf('test ! -L "$target"') < source.indexOf('sudo install -o root -g root -m 0644 "$stock_profile" "$target"'));
assert.match(source, /timeout 15s sudo "\$parser" -r "\$target"/);
for (const setting of ["userns", "apparmor", "clone"]) assert.ok(source.includes(`test "$after_${setting}" = "$before_${setting}"`));
const aptCommands = source.replace(/\\\n\s*/g, " ").split("\n").filter(line => /\bapt-get\b/.test(line));
assert.equal(aptCommands.length, 2, "only signed-index update and download may invoke APT");
assert.match(aptCommands[0], /\bupdate\b/);
assert.match(aptCommands[1], /\bdownload\b/);
for (const command of aptCommands) assert.doesNotMatch(command, /\b(?:install|upgrade|remove|purge)\b/);
assert.doesNotMatch(source, /apt-get[^\n]* install|\bdpkg(?:-deb)?\s+(?:-i|--install|--unpack)|sysctl\s+(?:-w|--system)|\baa-disable\b|\bsetcap\b|systemctl|--no-sandbox/);
} finally { rmSync(root, { recursive: true, force: true }); }
});
test("exact-source daemon matrices preserve historical three-target sources and add native ARM only for new sources", () => {
const workflow = readWorkflow("release-verify.yml");
const step = workflow.split(' - name: Preserve the immutable source\'s platform contract')[1].split('\n runner_binaries:')[0];
const script = step.split(' run: |\n')[1].split('\n').map(line => line.startsWith(' ') ? line.slice(10) : line).join('\n');
const root = mkdtempSync(path.join(tmpdir(), 'release-target-contract-'));
try {
const tools = path.join(root, 'tools'); mkdirSync(tools);
writeFileSync(path.join(tools, 'git'), '#!/bin/sh\nprintf "%s\\n" "$FIXTURE_HEAD"\n', { mode: 0o755 });
const source = path.join(root, 'packages/paperclip-runner/scripts/stage-release-runner-binaries.mjs');
mkdirSync(path.dirname(source), { recursive: true });
const output = path.join(root, 'outputs');
const historical = ['darwin-arm64', 'darwin-x64', 'linux-x64'];
for (const targets of [historical, [...historical, 'linux-arm64']]) {
writeFileSync(source, `export const RELEASE_RUNNER_TARGETS = ${JSON.stringify(targets)};`);
writeFileSync(output, '');
const result = spawnSync('bash', ['-euo', 'pipefail', '-c', script], { cwd: root, encoding: 'utf8', env: {
PATH: `${tools}:${path.dirname(process.execPath)}:/usr/bin:/bin`, SOURCE_SHA: 'a'.repeat(40), FIXTURE_HEAD: 'a'.repeat(40), GITHUB_OUTPUT: output,
} });
assert.equal(result.status, 0, result.stderr);
const data = Object.fromEntries(readFileSync(output, 'utf8').trim().split('\n').map(line => {
const index = line.indexOf('='); return [line.slice(0, index), JSON.parse(line.slice(index + 1))];
}));
assert.deepEqual(data.matrix.include.map(row => row.target), targets);
assert.deepEqual(data.native_consumers.include.map(row => row.target), targets.filter(target => target !== 'linux-x64'));
assert.ok(data.matrix.include.every(row => row.target !== 'linux-arm64' || row.host === 'ubuntu-24.04-arm'));
assert.equal(data.matrix.include.find(row => row.target === 'linux-x64').host, targets.length === 4 ? 'ubuntu-24.04' : 'ubuntu-latest',
'new GNU libc baseline stays pinned while historical qualification keeps its producer mode');
}
writeFileSync(source, 'export const RELEASE_RUNNER_TARGETS = ["linux-arm64"];'); writeFileSync(output, '');
const result = spawnSync('bash', ['-euo', 'pipefail', '-c', script], { cwd: root, encoding: 'utf8', env: {
PATH: `${tools}:${path.dirname(process.execPath)}:/usr/bin:/bin`, SOURCE_SHA: 'a'.repeat(40), FIXTURE_HEAD: 'a'.repeat(40), GITHUB_OUTPUT: output,
} });
assert.equal(result.status, 1); assert.match(result.stderr, /Unknown exact-source daemon platform contract/);
assert.equal(readFileSync(output, 'utf8'), '');
} finally { rmSync(root, { recursive: true, force: true }); }
});
test("historical promotion sources keep their packaging path while new sources require daemon assembly", () => {
const workflow = readWorkflow("release.yml");
const selector = workflow.split(' - name: Read the exact source packaging contract')[1].split('\n release_runner_binaries:')[0];
const script = selector.split(' run: |\n')[1].split('\n').map(line => line.startsWith(' ') ? line.slice(10) : line).join('\n');
const root = mkdtempSync(path.join(tmpdir(), 'release-source-contract-'));
try {
const tools = path.join(root, 'tools'); mkdirSync(tools);
writeFileSync(path.join(tools, 'git'), '#!/bin/sh\ncase "$1" in\nrev-parse) printf "%s\\n" "$FIXTURE_HEAD";;\nshow) cat "$FIXTURE_SOURCE";;\n*) exit 3;;\nesac\n', { mode: 0o755 });
const source = path.join(root, 'release.sh'), output = path.join(root, 'outputs');
for (const [contents, required] of [['pnpm build\n', false], ['assemble-daemons "$PAPERCLIP_RELEASE_RUNNER_ARTIFACTS_DIR" "$SOURCE_SHA"\n', true]]) {
writeFileSync(source, contents); writeFileSync(output, '');
const result = spawnSync('bash', ['-euo', 'pipefail', '-c', script], { encoding: 'utf8', env: {
PATH: `${tools}:/usr/bin:/bin`, SOURCE_SHA: 'a'.repeat(40), FIXTURE_HEAD: 'a'.repeat(40), FIXTURE_SOURCE: source, GITHUB_OUTPUT: output,
} });
assert.equal(result.status, 0, result.stderr);
assert.match(readFileSync(output, 'utf8'), new RegExp(`required=${required}`));
}
const result = spawnSync('bash', ['-euo', 'pipefail', '-c', script], { encoding: 'utf8', env: {
PATH: `${tools}:/usr/bin:/bin`, SOURCE_SHA: 'a'.repeat(40), FIXTURE_HEAD: 'b'.repeat(40), FIXTURE_SOURCE: source, GITHUB_OUTPUT: output,
} });
assert.equal(result.status, 1, 'Wrong checkout cannot opt out of mandatory assembly');
const unreadable = spawnSync('bash', ['-euo', 'pipefail', '-c', script], { encoding: 'utf8', env: {
PATH: `${tools}:/usr/bin:/bin`, SOURCE_SHA: 'a'.repeat(40), FIXTURE_HEAD: 'a'.repeat(40), FIXTURE_SOURCE: path.join(root, 'absent'), GITHUB_OUTPUT: output,
} });
assert.equal(unreadable.status, 1, 'A failed immutable source read must not masquerade as a legacy source');
} finally { rmSync(root, { recursive: true, force: true }); }
for (const job of ['publish_canary','publish_nightly','publish_beta','preview_stable','publish_stable']) {
const block = workflow.split(` ${job}:\n`)[1].split(/\n [a-z_]+:\n/)[0];
assert.match(block, /needs.release_runner_source.result == 'success'/);
assert.match(block, /needs.release_runner_source.outputs.required == 'false' \|\| needs.release_runner_binaries.result == 'success'/);
assert.match(block, /Download exact-source release daemon receipts\n\s+if: needs.release_runner_source.outputs.required == 'true'/);
}
});
test("canary startup refreshes an incomplete dependency publication, then onboards once", async () => {
+27 -5
View File
@@ -1,5 +1,6 @@
import { readdirSync, statSync } from "node:fs";
import { readFileSync, readdirSync, statSync } from "node:fs";
import path from "node:path";
import { runnerBinaryTarget } from "../packages/paperclip-runner/src/live/runner-binary.ts";
export function resolveNativeRunnerRequirement({ exitCode, stdout }) {
if (exitCode !== 0) {
@@ -38,20 +39,41 @@ function newestMtimeMs(target) {
return newest;
}
export function resolveNativeRunnerDevBinary({
repoRoot,
platform = process.platform,
architecture = process.arch,
}) {
const executable = platform === "win32" ? "paperclip-runnerd.exe" : "paperclip-runnerd";
const packageRoot = path.join(repoRoot, "packages", "paperclip-runner");
const target = path.join(packageRoot, "dist", "bin", `${platform}-${architecture}`, executable);
const flat = path.join(packageRoot, "dist", "bin", executable);
const targetStat = statSync(target, { throwIfNoEntry: false });
const flatStat = statSync(flat, { throwIfNoEntry: false });
// Release targets remain authoritative unless a newer source build matches
// this host. In particular, a build-host flat artifact cannot mask a remotely
// staged Mac target or force a local Cargo build.
if (flatStat?.isFile() && (!targetStat?.isFile() || flatStat.mtimeMs > targetStat.mtimeMs)
&& runnerBinaryTarget(readFileSync(flat)) === `${platform}-${architecture}`) {
return { binary: flat, sourceBuild: true };
}
return targetStat?.isFile() ? { binary: target, sourceBuild: false } : null;
}
export function paperclipRunnerBinaryNeedsBuild({
repoRoot,
nativeRunnerRequired,
configuredBinary,
platform = process.platform,
architecture = process.arch,
}) {
if (!nativeRunnerRequired) return false;
if (configuredBinary?.trim()) return false;
const executable = platform === "win32" ? "paperclip-runnerd.exe" : "paperclip-runnerd";
const selected = resolveNativeRunnerDevBinary({ repoRoot, platform, architecture });
if (!selected) return true;
const binaryStat = statSync(selected.binary);
const packageRoot = path.join(repoRoot, "packages", "paperclip-runner");
const stagedBinary = path.join(packageRoot, "dist", "bin", executable);
const binaryStat = statSync(stagedBinary, { throwIfNoEntry: false });
if (!binaryStat?.isFile()) return true;
const runnerRoot = path.join(packageRoot, "runner");
const buildInputs = [
+25 -17
View File
@@ -8,6 +8,7 @@ import { stdin, stdout } from "node:process";
import { createCapturedOutputBuffer, parseJsonResponseWithLimit } from "./dev-runner-output.ts";
import {
paperclipRunnerBinaryNeedsBuild,
resolveNativeRunnerDevBinary,
resolveNativeRunnerRequirement,
} from "./dev-runner-native-binary.mjs";
import { applyDevRunnerOptions } from "./dev-runner-options.ts";
@@ -171,6 +172,9 @@ const env: NodeJS.ProcessEnv = {
...process.env,
PAPERCLIP_UI_DEV_MIDDLEWARE: explicitUiDevMiddleware ?? (serveBuiltUiForManagedRuntime ? "false" : "true"),
};
// An automatic source-build override must not hide subsequent Rust edits from
// the watcher. Keep the operator's original choice separate from server env.
const configuredRunnerBinary = env.PAPERCLIP_RUNNER_BINARY;
if (mode === "dev") {
env.PAPERCLIP_DEV_SERVER_STATUS_FILE = devServerStatusFilePath;
@@ -573,28 +577,32 @@ async function buildPaperclipRunner() {
process.exit(typescriptResult.code);
}
const nativeRunnerRequired = await getNativeRunnerRequired();
if (
!paperclipRunnerBinaryNeedsBuild({
paperclipRunnerBinaryNeedsBuild({
repoRoot,
nativeRunnerRequired: await getNativeRunnerRequired(),
configuredBinary: env.PAPERCLIP_RUNNER_BINARY,
nativeRunnerRequired,
configuredBinary: configuredRunnerBinary,
})
) {
return;
console.log("[paperclip] building paperclip runner native binary...");
const binaryResult = await runPnpm(
["--filter", "@paperclipai/paperclip-runner", "build:binary"],
{ stdio: "inherit" },
);
if (binaryResult.signal) {
exitForSignal(binaryResult.signal);
return;
}
if (binaryResult.code !== 0) {
console.error("[paperclip] paperclip runner native binary build failed");
process.exit(binaryResult.code);
}
}
console.log("[paperclip] building paperclip runner native binary...");
const binaryResult = await runPnpm(
["--filter", "@paperclipai/paperclip-runner", "build:binary"],
{ stdio: "inherit" },
);
if (binaryResult.signal) {
exitForSignal(binaryResult.signal);
return;
}
if (binaryResult.code !== 0) {
console.error("[paperclip] paperclip runner native binary build failed");
process.exit(binaryResult.code);
if (!configuredRunnerBinary?.trim()) {
const selected = nativeRunnerRequired ? resolveNativeRunnerDevBinary({ repoRoot }) : null;
if (selected?.sourceBuild) env.PAPERCLIP_RUNNER_BINARY = selected.binary;
else delete env.PAPERCLIP_RUNNER_BINARY;
}
}
+129 -11
View File
@@ -1,4 +1,6 @@
import assert from 'node:assert/strict';
import { readFileSync, realpathSync, statSync } from 'node:fs';
import { basename, dirname, join } from 'node:path';
// Keep public-package lifecycle code off the verification host. Resolve and
// cache the public npm graph without scripts, then execute it offline.
@@ -9,6 +11,31 @@ export const GROK_PUBLIC_INSTALL_LIFECYCLE = [
'npm', 'rebuild', '--offline', '--ignore-scripts=false', '--dangerously-allow-all-scripts',
];
// setup-node provides its matching SDK alongside the executing Node binary.
// Deferred lifecycle scripts may read that SDK, but may never download headers.
export function macNodeHeadersDirectory(nodeExecutable, nodeVersion) {
assert.match(nodeVersion, /^\d+\.\d+\.\d+$/, 'Executing Node must have an exact release version');
const executable = realpathSync(nodeExecutable);
assert.equal(basename(executable), 'node', 'Node SDK must belong to the executing Node binary');
assert.ok(statSync(executable).isFile(), 'Executing Node must be a regular file');
const prefix = realpathSync(join(dirname(executable), '..'));
const include = join(prefix, 'include/node');
for (const name of ['node.h', 'node_version.h']) {
const header = join(include, name);
assert.equal(realpathSync(header), header, 'Node SDK headers must remain inside the executing Node prefix');
assert.ok(statSync(header).isFile(), 'Node SDK headers must be regular files');
}
assert.match(readFileSync(join(include, 'node.h'), 'utf8'), /#\s*include\s+"node_version\.h"/, 'Node SDK must include its version header');
const versionHeader = readFileSync(join(include, 'node_version.h'), 'utf8');
const versions = ['MAJOR', 'MINOR', 'PATCH'].map(part => {
const matches = [...versionHeader.matchAll(new RegExp('^#\\s*define\\s+NODE_' + part + '_VERSION\\s+(\\d+)\\s*$', 'gm'))];
assert.equal(matches.length, 1, 'Node SDK must declare one ' + part.toLowerCase() + ' version');
return matches[0][1];
});
assert.equal(versions.join('.'), nodeVersion, 'Node SDK version must match the executing Node');
return prefix;
}
// Inspect the actual npm archive, independently of the staging helper.
export function assertNoBundledCodexPayloads(entries) {
assert.ok(Array.isArray(entries) && entries.length > 0, 'Packed package must have tar members');
@@ -60,10 +87,11 @@ export function assertInstalledCodexPlatformProvenance(lock, installed, target)
// Execute this in the existing offline public consumer, never in the checkout.
// The public index and returned command must both come from the installed graph.
export function installedCodexProbeSource(indexPath, consumerRoot, expectedVersion) {
export function installedCodexProbeSource(indexPath, consumerRoot, expectedVersion, { nativeCodex = false } = {}) {
return `
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { createRequire } from 'node:module';
import { accessSync, constants, existsSync, lstatSync, readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
@@ -118,11 +146,11 @@ export function installedCodexProbeSource(indexPath, consumerRoot, expectedVersi
assert.ok(selectedPlatform, 'Codex wrapper must resolve an official host platform package from its installed graph');
const selectedMetadata = JSON.parse(readFileSync(selectedPlatformManifest, 'utf8'));
assert.deepEqual(selectedMetadata, selectedPlatform.manifest, 'Selected Codex platform metadata must match its scanned provenance');
assert.equal(selectedMetadata.version, ${JSON.stringify(expectedVersion)} + '-' + process.platform + '-' + process.arch,
'Selected Codex platform version must match the qualified pin');
${nativeCodex ? '' : `assert.equal(selectedMetadata.version, ${JSON.stringify(expectedVersion)} + '-' + process.platform + '-' + process.arch,
'Selected Codex platform version must match the qualified pin');`}
const version = execFileSync(command, ['--version'], { timeout: 30_000, maxBuffer: 128 * 1024, encoding: 'utf8',
env: { PATH: '/usr/local/bin:/usr/bin:/bin', HOME: '/tmp', NODE_PATH: '' } }).trim();
assert.equal(version, 'codex-cli ' + ${JSON.stringify(expectedVersion)}, 'Installed Codex version must match the qualified pin');
env: { PATH: process.env.PATH, HOME: process.env.HOME, NODE_PATH: '' } }).trim();
${nativeCodex ? "assert.match(version, /^codex-cli /, 'Installed official Codex command must execute');" : `assert.equal(version, 'codex-cli ' + ${JSON.stringify(expectedVersion)}, 'Installed Codex version must match the qualified pin');`}
const vendor = dirname(index);
const integrityPath = join(vendor, 'drivers/acpx/installation-integrity.js');
const profilesPath = join(vendor, 'drivers/acpx/qualified-profiles.js');
@@ -132,26 +160,116 @@ export function installedCodexProbeSource(indexPath, consumerRoot, expectedVersi
const platformRoot = realpathSync(dirname(selectedPlatformManifest));
const nativeVendor = realpathSync(join(platformRoot, 'vendor'));
assert.ok(contained(nativeVendor) && nativeVendor.startsWith(platformRoot + sep), 'Codex vendor resources must remain inside their installed platform package');
const triple = { 'linux-x64': 'x86_64-unknown-linux-musl', 'linux-arm64': 'aarch64-unknown-linux-musl',
'darwin-x64': 'x86_64-apple-darwin', 'darwin-arm64': 'aarch64-apple-darwin',
'win32-x64': 'x86_64-pc-windows-msvc', 'win32-arm64': 'aarch64-pc-windows-msvc' }[process.platform + '-' + process.arch];
assert.ok(triple, 'Codex native payload requires a known host target');
const nativeName = process.platform === 'win32' ? 'codex.exe' : 'codex';
const nativeCandidates = ['bin', 'codex'].map(directory => join(nativeVendor, triple, directory, nativeName)).filter(existsSync);
assert.equal(nativeCandidates.length, 1, 'Selected official Codex platform must contain one native executable');
const nativeExecutable = realpathSync(nativeCandidates[0]);
const nativeStat = lstatSync(nativeCandidates[0]);
assert.ok(nativeExecutable.startsWith(nativeVendor + sep) && nativeStat.isFile() && !nativeStat.isSymbolicLink()
&& nativeStat.size >= 20 && nativeStat.size <= 512 * 1024 * 1024, 'Codex native executable must be a bounded regular file in its selected platform package');
accessSync(nativeExecutable, constants.X_OK);
const nativeBytes = readFileSync(nativeExecutable);
const { runnerBinaryTarget } = await import(pathToFileURL(join(vendor, 'live/runner-binary.js')).href);
assert.equal(runnerBinaryTarget(nativeBytes), process.platform + '-' + process.arch, 'Codex native executable must match the actual host architecture');
const sandboxSource = { HOME: '/private-provider-home', PATH: '/usr/local/bin:/usr/bin:/bin' };
const readRoots = codexExecutableReadOnlyRoots(sandboxSource, command);
assert.ok(readRoots.includes(nativeVendor), 'Selected native Codex vendor directory must be readable inside its sandbox');
assert.ok(!readRoots.includes(platformRoot) && !readRoots.includes(root) && !readRoots.includes(sandboxSource.HOME), 'Codex sandbox must not grant enclosing npm or credential-home access');
const sandboxArgs = createIsolatedCodexAppServerArgs(sandboxSource, readRoots).join('\\n');
assert.ok(sandboxArgs.includes(JSON.stringify(nativeVendor) + '="read"'), 'Codex sandbox configuration must retain the selected native executable resources');
const { createAcpxPackageJsonResolver, verifyQualifiedAcpxInstallation } = await import(pathToFileURL(integrityPath).href);
${nativeCodex ? '' : `const { createAcpxPackageJsonResolver, verifyQualifiedAcpxInstallation } = await import(pathToFileURL(integrityPath).href);
const { resolveQualifiedAcpxProfile } = await import(pathToFileURL(profilesPath).href);
const serverRoot = resolve(vendor, '../../..');
assert.ok(contained(serverRoot), 'Codex package authority must be the installed server');
const installation = await verifyQualifiedAcpxInstallation(resolveQualifiedAcpxProfile('codex', 'gpt-5.4'),
createAcpxPackageJsonResolver(serverRoot, join(serverRoot, 'package.json')));
const lease = await installation.openCommand();
await lease.close();
console.log(JSON.stringify({ pinnedCodexCommandVerified: true, pinnedCodexVersion: ${JSON.stringify(expectedVersion)},
...provenance, codexSelectedPlatformPath: selectedPlatform.path, codexSelectedPlatformVersion: selectedMetadata.version, codexNativeSandboxResourcesVerified: true, codexQualifiedInstallationVerified: true, codexCommandLeaseVerified: true, providerCalls: 0 }));
await lease.close();`}
console.log(JSON.stringify({ pinnedCodexCommandVerified: true, installedCodexVersion: version,
codexCommandSha256: createHash('sha256').update(readFileSync(command)).digest('hex'),
codexNativeExecutableSha256: createHash('sha256').update(nativeBytes).digest('hex'),
codexNativeExecutablePath: relative(root, nativeExecutable).split(sep).join('/'),
...provenance, codexSelectedPlatformPath: selectedPlatform.path, codexSelectedPlatformVersion: selectedMetadata.version,
codexNativeSandboxResourcesVerified: true,
${nativeCodex ? '' : `pinnedCodexVersion: ${JSON.stringify(expectedVersion)}, codexQualifiedInstallationVerified: true, codexCommandLeaseVerified: true,`}
providerCalls: 0 }));
`;
}
export function grokConsumerDockerArgs({ assets, consumer, cache, command, uid, gid, download = false, prerequisite, temporarySizeMiB = 256, temporaryExecutable = false }) {
/** Installed bytes and consumer resolution must survive host protocol startup. */
export function assertInstalledNativeConsumerContinuity(before, after) {
for (const field of ['lockSha256', 'runnerSha256', 'codexCommandSha256', 'codexNativeExecutableSha256']) {
if (!/^[a-f0-9]{64}$/.test(before[field]) || before[field] !== after[field]) {
throw new Error(`Installed native host startup changed ${field}`);
}
}
}
/** The installed consumer must select its packaged host daemon, never PATH. */
export function installedDaemonProbeSource(outputRoot, consumerRoot, sourceRevision) {
return `
import assert from 'node:assert/strict';
import { createHash } from 'node:crypto';
import { execFileSync } from 'node:child_process';
import { readFileSync, realpathSync } from 'node:fs';
import { isAbsolute, join, relative, sep } from 'node:path';
import { pathToFileURL } from 'node:url';
const root = realpathSync(${JSON.stringify(consumerRoot)}), output = realpathSync(${JSON.stringify(outputRoot)});
const inside = path => { const value = relative(root, realpathSync(path)); return value !== '' && value !== '..' && !value.startsWith('..' + sep) && !isAbsolute(value); };
assert.ok(inside(output));
const { defaultCapabilityRunnerdBinary } = await import(pathToFileURL(join(output, 'live/runnerd-codex-transport.js')).href);
const { runnerBinaryTarget } = await import(pathToFileURL(join(output, 'live/runner-binary.js')).href);
const { parsePaperclipRunnerdBuildMetadata } = await import(pathToFileURL(join(output, 'evals/runnerd-artifact.js')).href);
const { PAPERCLIP_RUNNER_BUILD_METADATA: expected } = await import(pathToFileURL(join(output, 'evals/build-metadata.js')).href);
const manifest = JSON.parse(readFileSync(join(output, 'bin/release-manifest.json'), 'utf8'));
const targets = ['darwin-arm64', 'darwin-x64', 'linux-x64', 'linux-arm64'];
assert.equal(manifest.schema, 'paperclip.runner.release-binaries.v1');
assert.equal(manifest.sourceRevision, ${JSON.stringify(sourceRevision)});
assert.deepEqual(Object.keys(manifest.platforms).sort(), targets.sort());
for (const target of targets) {
const record = manifest.platforms[target];
assert.equal(record.path, target + '/paperclip-runnerd');
const file = join(output, 'bin', record.path); assert.ok(inside(file));
const bytes = readFileSync(file);
assert.equal(runnerBinaryTarget(bytes), target);
assert.equal('sha256:' + createHash('sha256').update(bytes).digest('hex'), record.sha256);
}
const target = process.platform + '-' + process.arch, selected = defaultCapabilityRunnerdBinary();
assert.ok(inside(selected));
assert.equal(realpathSync(selected), realpathSync(join(output, 'bin', manifest.platforms[target].path)), 'Production must select the packaged host daemon');
const metadata = parsePaperclipRunnerdBuildMetadata(JSON.parse(execFileSync(selected, ['--build-metadata'], {
timeout: 15000, maxBuffer: 64 * 1024, encoding: 'utf8', env: { PATH: process.env.PATH, HOME: process.env.HOME, NODE_PATH: '' },
})));
assert.equal(metadata.binaryContractVersion, expected.contracts.runnerdArtifact);
assert.equal(metadata.nativeExecutionVersion, expected.contracts.nativeExecution);
assert.equal(metadata.harnessDriverVersion, expected.contracts.harnessDriver);
assert.equal(metadata.prp.name, expected.prp.name);
assert.ok(metadata.prp.minimumVersion <= expected.prp.maximumVersion && metadata.prp.maximumVersion >= expected.prp.minimumVersion);
console.log(JSON.stringify({ installedRunnerDaemonVerified: true, target, sourceRevision: manifest.sourceRevision,
sha256: manifest.platforms[target].sha256, buildMetadata: metadata, providerCalls: 0 }));
`;
}
// Hosted, credential-free Mac consumers defer only npm lifecycle execution.
// Offline npm flags are not an OS network boundary; sandbox-exec supplies it.
export function macPublicInstallLifecycleProfile(ownedRoot) {
if (!ownedRoot.startsWith('/') || ownedRoot.includes('\0')) throw new Error('Mac lifecycle requires an absolute owned root');
return `(version 1)\n(allow default)\n(deny network*)\n(deny file-write* (require-all (require-not (subpath ${JSON.stringify(ownedRoot)})) (require-not (literal "/dev/null"))))\n`;
}
export function macInstalledStartupProbeSource(consumerRoot, serverRoot, runnerDigest, helperPath) {
if (!/^sha256:[a-f0-9]{64}$/.test(runnerDigest)) throw new Error('Installed Mac startup requires the verified SHA-256 manifest identity');
return `import { probeInstalledCodexStartup } from ${JSON.stringify(helperPath)};
console.log(JSON.stringify(await probeInstalledCodexStartup({consumerRoot:${JSON.stringify(consumerRoot)},
serverRoot:${JSON.stringify(serverRoot)},expectedRunnerSha256:${JSON.stringify(runnerDigest.slice(7))}})));`;
}
export function grokConsumerDockerArgs({ assets, consumer, cache, command, uid, gid, download = false, prerequisite, temporarySizeMiB = 256, temporaryExecutable = false, platform = 'linux/amd64' }) {
if (!['linux/amd64', 'linux/arm64'].includes(platform)) throw new Error('Public-install verification requires a supported native Linux platform');
if (!Number.isSafeInteger(uid) || uid <= 0 || !Number.isSafeInteger(gid) || gid <= 0) {
throw new Error('Public-install verification requires an unprivileged host user');
}
@@ -162,7 +280,7 @@ export function grokConsumerDockerArgs({ assets, consumer, cache, command, uid,
throw new Error('Executable runtime snapshots require an offline verification sandbox');
}
return [
'run', '--rm', '--platform', 'linux/amd64',
'run', '--rm', '--platform', platform,
'--user', `${uid}:${gid}`, '--read-only',
'--cap-drop', 'ALL', '--security-opt', 'no-new-privileges',
'--pids-limit', '256', '--memory', '3g',
+40 -3
View File
@@ -2,10 +2,44 @@
// 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 { createHash } from 'node:crypto';
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';
import { join, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
export async function verifyPiPackagedDaemon(server, daemon, target, runnerBinaryTarget) {
assert.match(target, /^(?:linux|darwin)-(?:x64|arm64)$/);
const output = join(server, 'dist/vendor/paperclip-runner'), manifestPath = join(output, 'bin/release-manifest.json');
let manifest;
try { manifest = JSON.parse(await readFile(manifestPath, 'utf8')); }
catch (error) { if (error.code !== 'ENOENT') throw error; }
let expectedPath = join(output, 'bin/paperclip-runnerd');
if (manifest !== undefined) {
assert.ok(manifest && typeof manifest === 'object' && !Array.isArray(manifest), 'Release manifest must be an object');
assert.equal(await realpath(manifestPath), manifestPath, 'Release manifest must remain inside the installed package');
assert.equal(manifest.schema, 'paperclip.runner.release-binaries.v1');
assert.match(manifest.sourceRevision, /^[a-f0-9]{40}$/);
const build = JSON.parse(await readFile(join(server, 'dist/build-info.json'), 'utf8'));
assert.equal(manifest.sourceRevision, build.commit, 'Packaged daemon source must match the installed server build');
const artifact = manifest.platforms?.[target];
assert.equal(artifact?.path, `${target}/paperclip-runnerd`, 'Packaged daemon must use the known host target path');
assert.match(artifact.sha256, /^sha256:[a-f0-9]{64}$/);
expectedPath = join(output, 'bin', artifact.path);
assert.equal(await realpath(daemon), expectedPath, 'Production must select the installed host daemon');
assert.equal(await realpath(expectedPath), expectedPath, 'Packaged daemon cannot escape through a symlink');
const bytes = await readFile(expectedPath);
assert.equal(`sha256:${createHash('sha256').update(bytes).digest('hex')}`, artifact.sha256);
assert.equal(runnerBinaryTarget(bytes), target);
} else {
// Existing host-only source packages have no multi-target release manifest.
// They must still use their exact normal flat daemon, never an override.
assert.equal(await realpath(daemon), expectedPath);
assert.equal(runnerBinaryTarget(await readFile(expectedPath)), target);
}
}
async function main() {
const [serverDirectory, ...extra] = process.argv.slice(2);
assert.ok(serverDirectory && extra.length === 0, 'Usage: pi-public-install-probe.mjs INSTALLED_SERVER_ROOT');
@@ -25,7 +59,8 @@ assert.equal(QUALIFIED_ACPX_PROFILES.pi.commandDigest, 'sha256:e92078bee3c23bec4
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 { runnerBinaryTarget } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/live/runner-binary.js')));
await verifyPiPackagedDaemon(server, daemon, `${process.platform}-${process.arch}`, runnerBinaryTarget);
const root = await mkdtemp(join(tmpdir(), 'pi-public-install-probe-'));
const evidence = [];
const started = performance.now();
@@ -69,3 +104,5 @@ try {
try { await bundle?.transport.close(); }
finally { clearTimeout(watchdog); await rm(root, { recursive: true, force: true }); }
}
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) await main();
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env bash
# Prepare the disposable GitHub ARM qualification host using stock distro files.
# Extraction skips package maintainer scripts and their global policy changes.
set -euo pipefail
if [[ "${GITHUB_ACTIONS:-}" != true || "${RUNNER_OS:-}" != Linux ]]; then
printf 'Linux ARM qualification preparation requires a GitHub Actions Linux runner.\n' >&2
exit 1
fi
. /etc/os-release
test "$ID" = ubuntu
test "$VERSION_ID" = 24.04
test "$(dpkg --print-architecture)" = arm64
test "$(uname -m)" = aarch64
prerequisite_report_dir=${RUNNER_TEMP:?}/paperclip-arm-bwrap-prerequisite
mkdir -p "$prerequisite_report_dir"
prerequisite_download_dir=$(mktemp -d "${RUNNER_TEMP}/paperclip-arm-stock-packages.XXXXXX")
before_userns=$(sysctl -n kernel.apparmor_restrict_unprivileged_userns)
before_apparmor=$(cat /sys/module/apparmor/parameters/enabled)
before_clone=$(sysctl -n kernel.unprivileged_userns_clone)
test "$before_userns" = 1
test "$before_apparmor" = Y
test "$before_clone" = 1
printf 'restriction=%s apparmor=%s clone=%s\n' \
"$before_userns" "$before_apparmor" "$before_clone" | \
tee "$prerequisite_report_dir/before.txt"
parser=$(readlink -f "$(command -v apparmor_parser)")
test "$parser" = /usr/sbin/apparmor_parser
test "$(stat -c '%u' "$parser")" = 0
dpkg-query -W -f='${Package} ${Version} ${Architecture}\n' \
apparmor libc6 libcap2 libselinux1 | \
tee "$prerequisite_report_dir/existing-package-versions.txt"
dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libc6)" ge 2.38
dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libcap2)" ge 1:2.10
dpkg --compare-versions "$(dpkg-query -W -f='${Version}' libselinux1)" ge 3.1~
timeout 120s sudo apt-get \
-o Acquire::AllowInsecureRepositories=false \
-o APT::Get::AllowUnauthenticated=false update | \
tee "$prerequisite_report_dir/apt-update.txt"
apt-cache policy bubblewrap apparmor-profiles | \
tee "$prerequisite_report_dir/apt-policy.txt"
for package_name in bubblewrap apparmor-profiles; do
package_version=$(apt-cache policy "$package_name" | awk '/Candidate:/ {print $2}')
test -n "$package_version"
test "$package_version" != '(none)'
apt-cache show "$package_name=$package_version" > \
"$prerequisite_report_dir/$package_name-index.txt"
package_sha256=$(awk '/^SHA256:/ {print $2}' \
"$prerequisite_report_dir/$package_name-index.txt")
test "${#package_sha256}" = 64
printf '%s\n' "$package_sha256" | grep -E '^[0-9a-f]{64}$' >/dev/null
(
cd "$prerequisite_download_dir"
timeout 60s apt-get -o APT::Get::AllowUnauthenticated=false \
download "$package_name=$package_version"
)
package_files=("$prerequisite_download_dir"/"$package_name"_*.deb)
test "${#package_files[@]}" = 1
package_file=${package_files[0]}
test -f "$package_file"
printf '%s %s\n' "$package_sha256" "$package_file" | sha256sum -c -
test "$(dpkg-deb -f "$package_file" Package)" = "$package_name"
test "$(dpkg-deb -f "$package_file" Version)" = "$package_version"
dpkg-deb -f "$package_file" Package Version Architecture | \
tee "$prerequisite_report_dir/$package_name-downloaded-version.txt"
if test "$package_name" = bubblewrap; then
test "$(dpkg-deb -f "$package_file" Architecture)" = arm64
dpkg-deb --fsys-tarfile "$package_file" | \
tar -xO ./usr/bin/bwrap > "$prerequisite_download_dir/bwrap"
else
test "$(dpkg-deb -f "$package_file" Architecture)" = all
dpkg-deb --fsys-tarfile "$package_file" | \
tar -xO ./usr/share/apparmor/extra-profiles/bwrap-userns-restrict > \
"$prerequisite_download_dir/bwrap-userns-restrict"
fi
done
stock_binary=$prerequisite_download_dir/bwrap
stock_profile=$prerequisite_download_dir/bwrap-userns-restrict
printf '11d39094f044f0cda0febb3ad517b830301da6b2ce929664af09ee9e4dd264f9 %s\n' \
"$stock_profile" | sha256sum -c -
cat "$stock_profile" | tee "$prerequisite_report_dir/stock-profile.txt"
test "$(od -An -tx1 -N4 "$stock_binary" | tr -d ' \n')" = 7f454c46
readelf -h "$stock_binary" | tee "$prerequisite_report_dir/bwrap-elf-header.txt"
grep -E 'Machine:.*AArch64' "$prerequisite_report_dir/bwrap-elf-header.txt"
# Reject every target symlink, including dangling links, before privileged copy.
test ! -L /usr/bin/bwrap
if test -e /usr/bin/bwrap; then
test -f /usr/bin/bwrap
cmp "$stock_binary" /usr/bin/bwrap
else
sudo install -o root -g root -m 0755 "$stock_binary" /usr/bin/bwrap
fi
test "$(readlink -f "$(command -v bwrap)")" = /usr/bin/bwrap
test "$(stat -c '%u:%g:%a' /usr/bin/bwrap)" = 0:0:755
command -v getcap >/dev/null
test -z "$(getcap /usr/bin/bwrap)"
sha256sum "$stock_binary" /usr/bin/bwrap | \
tee "$prerequisite_report_dir/bwrap-sha256.txt"
timeout 5s /usr/bin/bwrap --version | tee "$prerequisite_report_dir/bwrap-version.txt"
help=$(timeout 5s /usr/bin/bwrap --help)
printf '%s\n' "$help" > "$prerequisite_report_dir/bwrap-help.txt"
printf '%s\n' "$help" | grep -F -- --as-pid-1 >/dev/null
printf '%s\n' "$help" | grep -F -- --perms >/dev/null
target=/etc/apparmor.d/bwrap-userns-restrict
test ! -L "$target"
test ! -L /etc/apparmor.d/local/bwrap-userns-restrict
test ! -L /etc/apparmor.d/local/unpriv_bwrap
test ! -s /etc/apparmor.d/local/bwrap-userns-restrict
test ! -s /etc/apparmor.d/local/unpriv_bwrap
if test -e "$target"; then
test -f "$target"
cmp "$stock_profile" "$target"
else
sudo install -o root -g root -m 0644 "$stock_profile" "$target"
fi
timeout 15s sudo "$parser" -r "$target"
sha256sum "$stock_profile" "$target" | \
tee "$prerequisite_report_dir/profile-sha256.txt"
loaded_profiles=$(sudo cat /sys/kernel/security/apparmor/profiles)
for required_profile in 'bwrap (enforce)' 'unpriv_bwrap (enforce)'; do
printf '%s\n' "$loaded_profiles" | grep -Fx "$required_profile" | \
tee -a "$prerequisite_report_dir/loaded-profiles.txt"
done
after_userns=$(sysctl -n kernel.apparmor_restrict_unprivileged_userns)
after_apparmor=$(cat /sys/module/apparmor/parameters/enabled)
after_clone=$(sysctl -n kernel.unprivileged_userns_clone)
test "$after_userns" = "$before_userns"
test "$after_apparmor" = "$before_apparmor"
test "$after_clone" = "$before_clone"
printf 'restriction=%s apparmor=%s clone=%s\n' \
"$after_userns" "$after_apparmor" "$after_clone" | \
tee "$prerequisite_report_dir/after.txt"
timeout 5s /usr/bin/bwrap --die-with-parent --unshare-user --unshare-net \
--ro-bind / / --cap-drop ALL /bin/true
printf 'stock files materialized without dpkg registration or maintainer scripts; namespace probe passed; no provider processes or turns\n' | \
tee "$prerequisite_report_dir/namespace-probe.txt"
+10
View File
@@ -284,6 +284,16 @@ release_info ""
release_info "==> Step 2/7: Building workspace artifacts..."
cd "$REPO_ROOT"
pnpm build
# Hosted producers supply data-only, hash/source/architecture-bound daemon
# receipts. Assemble after the build (which stages its host binary), before
# public packages are packed. The server vendors this same compiled runner.
if [ -n "${PAPERCLIP_RELEASE_RUNNER_ARTIFACTS_DIR:-}" ]; then
node "$REPO_ROOT/packages/paperclip-runner/scripts/stage-release-runner-binaries.mjs" \
assemble-daemons "$PAPERCLIP_RELEASE_RUNNER_ARTIFACTS_DIR" "$(git rev-parse HEAD)"
cp -R "$REPO_ROOT/packages/paperclip-runner/dist/bin/." "$REPO_ROOT/server/dist/vendor/paperclip-runner/bin/"
elif [ "$dry_run" = false ]; then
release_fail "Public releases require the four hosted runner daemon artifacts; use the supported Release workflow."
fi
node "$REPO_ROOT/scripts/build-standalone-public-packages.mjs"
bash "$REPO_ROOT/scripts/prepare-server-ui-dist.sh"
for pkg_dir in server packages/adapters/claude-local packages/adapters/codex-local; do
+204 -2
View File
@@ -4,17 +4,200 @@
// explicit and separate from npm installation, and is removed in finally.
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { createServer } from 'node:net';
import { tmpdir } from 'node:os';
import { stripTypeScriptTypes } from 'node:module';
import { basename, dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { packConcurrency, packStagedPackages, packTimings } from './pack-staged-packages.mjs';
import { materializePublishManifest } from './prepare-bundled-package.mjs';
import { GROK_PUBLIC_INSTALL_IMAGE, GROK_PUBLIC_INSTALL_LIFECYCLE, assertNoBundledCodexPayloads, grokConsumerDockerArgs, installedCodexProbeSource } from './grok-public-install-sandbox.mjs';
import { GROK_PUBLIC_INSTALL_IMAGE, GROK_PUBLIC_INSTALL_LIFECYCLE, assertInstalledNativeConsumerContinuity, assertNoBundledCodexPayloads, grokConsumerDockerArgs, installedCodexProbeSource, installedDaemonProbeSource, macNodeHeadersDirectory, macPublicInstallLifecycleProfile, macInstalledStartupProbeSource } from './grok-public-install-sandbox.mjs';
import { startPrefetch, stopPrefetches } from './prefetch-child.mjs';
import { retainRunnerQualificationPackages } from './retain-runner-qualification-packages.mjs';
import { bundledInstallTimings, startBundledInstalls } from './stage-bundled-packages.mjs';
const repo = resolve(dirname(fileURLToPath(import.meta.url)), '..');
async function verifyMacConsumer(packageDirectory, sourceRevision, receiptPath) {
assert.equal(process.env.GITHUB_ACTIONS, 'true', 'Mac clean consumers run only on disposable hosted CI');
assert.equal(process.platform, 'darwin');
assert.ok(['arm64', 'x64'].includes(process.arch));
assert.match(sourceRevision, /^[a-f0-9]{40}$/);
const packages = realpathSync(packageDirectory);
const manifest = JSON.parse(readFileSync(join(packages, 'manifest.json'), 'utf8'));
assert.equal(manifest.schema, 'paperclip.runner.public-packages/v1');
assert.equal(manifest.sourceRevision, sourceRevision);
assert.ok(manifest.archives.some(entry => entry.name === '@paperclipai/server'));
assert.ok(manifest.archives.some(entry => entry.name === 'paperclipai'));
assert.equal(new Set(manifest.archives.map(entry => entry.name)).size, manifest.archives.length);
const root = realpathSync(mkdtempSync(join(tmpdir(), 'paperclip-mac-public-install-')));
try {
const consumer = join(root, 'consumer'), home = join(root, 'home'), tools = join(root, 'tools');
for (const directory of [consumer, home, tools, join(root, 'tmp'), join(root, 'cache')]) mkdirSync(directory);
const npmCli = realpathSync(execFileSync('/usr/bin/which', ['npm'], { encoding: 'utf8' }).trim());
symlinkSync(process.execPath, join(tools, 'node'));
// npm subprocesses get a Node-only owned tool directory; no ambient harness
// command can hide missing installed dependencies.
const env = { PATH: `${tools}:/usr/bin:/bin:/usr/sbin:/sbin`, HOME: home, TMPDIR: join(root, 'tmp'),
npm_config_cache: join(root, 'cache'), npm_config_audit: 'false', npm_config_fund: 'false' };
const run = (command, args, cwd = consumer, runEnv = env) => execFileSync(command, args, { cwd, env: runEnv,
timeout: 10 * 60_000, maxBuffer: 32 * 1024 * 1024, encoding: 'utf8' });
const tarballs = manifest.archives.map(entry => {
assert.match(entry.file, /^[A-Za-z0-9._-]+\.tgz$/); assert.match(entry.sha256, /^[a-f0-9]{64}$/);
const path = join(packages, entry.file);
assert.equal(realpathSync(path), path);
assert.equal(createHash('sha256').update(readFileSync(path)).digest('hex'), entry.sha256);
assertNoBundledCodexPayloads(run('/usr/bin/tar', ['-tzf', path]).trim().split('\n'));
return path;
});
writeFileSync(join(consumer, 'package.json'), JSON.stringify({ private: true, type: 'module' }));
const sentinelSource = join(root, 'lifecycle-sentinel'); mkdirSync(sentinelSource);
writeFileSync(join(sentinelSource, 'package.json'), JSON.stringify({ name: 'paperclip-verification-lifecycle-sentinel', version: '1.0.0',
scripts: { postinstall: 'node -e "require(\'node:fs\').writeFileSync(\'lifecycle-ran\', \'ok\')"' } }));
run(process.execPath, [npmCli, 'pack', '--ignore-scripts', '--pack-destination', root], sentinelSource);
const sentinelTar = join(root, readdirSync(root).find(name => name.endsWith('.tgz')));
run(process.execPath, [npmCli, 'install', '--ignore-scripts', '--omit=dev', ...tarballs, sentinelTar]);
const sentinel = join(consumer, 'node_modules/paperclip-verification-lifecycle-sentinel/lifecycle-ran');
assert.equal(existsSync(sentinel), false);
const lock = readFileSync(join(consumer, 'package-lock.json'), 'utf8');
const profile = join(root, 'lifecycle.sb'); writeFileSync(profile, macPublicInstallLifecycleProfile(root));
const listener = createServer(socket => socket.end());
await new Promise((resolve, reject) => { listener.once('error', reject); listener.listen(0, '127.0.0.1', resolve); });
try {
const port = listener.address().port;
const networkProbe = `import assert from 'node:assert/strict'; import net from 'node:net';
const socket = net.connect({host:'127.0.0.1',port:${port}}); socket.setTimeout(3000,()=>process.exit(2));
socket.once('connect',()=>{socket.destroy(); ${"process.argv[1] === 'denied' ? process.exit(3) : process.exit(0)"};});
socket.once('error',error=>{assert.equal(process.argv[1],'denied'); assert.equal(error.code,'EPERM');});`;
run(process.execPath, ['--input-type=module', '-e', networkProbe, 'allowed']);
run('/usr/bin/sandbox-exec', ['-f', profile, process.execPath, '--input-type=module', '-e', networkProbe, 'denied']);
} finally { await new Promise(resolve => listener.close(resolve)); }
const nodeHeaders = macNodeHeadersDirectory(process.execPath, process.versions.node);
run('/usr/bin/sandbox-exec', ['-f', profile, process.execPath, npmCli, 'rebuild', '--offline', '--ignore-scripts=false', '--dangerously-allow-all-scripts', '--foreground-scripts'],
consumer, { ...env, npm_config_nodedir: nodeHeaders });
assert.equal(readFileSync(sentinel, 'utf8'), 'ok');
assert.equal(readFileSync(join(consumer, 'package-lock.json'), 'utf8'), lock);
for (const entry of manifest.archives) {
assert.equal(JSON.parse(readFileSync(join(consumer, 'node_modules', entry.name, 'package.json'))).version, manifest.releaseVersion);
}
const server = join(consumer, 'node_modules/@paperclipai/server'), installed = join(server, 'dist/vendor/paperclip-runner');
const probe = (name, source) => { const file = join(root, name); writeFileSync(file, source); return JSON.parse(run(process.execPath, [file]).trim()); };
const daemon = probe('daemon-probe.mjs', installedDaemonProbeSource(installed, consumer, sourceRevision));
const codex = probe('codex-probe.mjs', installedCodexProbeSource(join(installed, 'index.js'), consumer, undefined, { nativeCodex: true }));
const startupScript = macInstalledStartupProbeSource(consumer, server, daemon.sha256, join(repo, 'tests/runner-e2e/installed-cli.ts'));
const startupFile = join(root, 'startup-probe.mts'); writeFileSync(startupFile, startupScript);
const startup = JSON.parse(run(process.execPath, ['--import', 'tsx', startupFile], join(repo, 'server')).trim());
const receipt = { schema: 'paperclip.runner.mac-public-install.v1', sourceRevision, platform: process.platform, architecture: process.arch,
cleanNpmInstall: true, packageCount: manifest.archives.length, codexPackedPayloadsOmitted: true,
consumerLockPreserved: true, lifecycleSentinelVerified: true, lifecycleNodeHeadersVerified: true, lifecycleNodeVersion: process.versions.node,
lifecycleNetwork: 'sandbox-exec denied (loopback negative control)',
...daemon, ...codex, startup, providerCalls: 0 };
writeFileSync(receiptPath, JSON.stringify(receipt, null, 2) + '\n');
console.log(JSON.stringify(receipt));
} finally { rmSync(root, { recursive: true, force: true }); }
}
async function verifyLinuxArmConsumer(packageDirectory, sourceRevision, receiptPath) {
assert.equal(process.env.GITHUB_ACTIONS, 'true', 'Native clean consumers run only on disposable hosted CI');
assert.equal(process.platform + '-' + process.arch, 'linux-arm64', 'ARM qualification requires a native ARM host');
assert.equal(process.versions.node.split('.')[0], '24', 'ARM host startup requires the qualified Node24 runtime');
assert.match(sourceRevision, /^[a-f0-9]{40}$/);
const packages = realpathSync(packageDirectory);
const manifest = JSON.parse(readFileSync(join(packages, 'manifest.json'), 'utf8'));
assert.equal(manifest.schema, 'paperclip.runner.public-packages/v1');
assert.equal(manifest.sourceRevision, sourceRevision);
assert.ok(manifest.archives.some(entry => entry.name === '@paperclipai/server'));
assert.ok(manifest.archives.some(entry => entry.name === 'paperclipai'));
assert.equal(new Set(manifest.archives.map(entry => entry.name)).size, manifest.archives.length);
const root = realpathSync(mkdtempSync(join(tmpdir(), 'paperclip-arm-public-install-')));
try {
const assets = join(root, 'assets'), consumer = join(root, 'consumer'), cache = join(root, 'cache');
for (const directory of [assets, consumer, cache]) mkdirSync(directory);
const run = (command, args, cwd = root) => execFileSync(command, args, { cwd,
env: { PATH: process.env.PATH, HOME: root, TMPDIR: root }, timeout: 10 * 60_000,
maxBuffer: 32 * 1024 * 1024, encoding: 'utf8' });
const platform = 'linux/arm64';
run('docker', ['pull', '--platform', platform, GROK_PUBLIC_INSTALL_IMAGE]);
const isolated = (command, options = {}) => run('docker', grokConsumerDockerArgs({ assets, consumer, cache,
uid: process.getuid(), gid: process.getgid(), platform, command, ...options }));
for (const entry of manifest.archives) {
assert.match(entry.file, /^[A-Za-z0-9._-]+\.tgz$/); assert.match(entry.sha256, /^[a-f0-9]{64}$/);
const tarball = join(packages, entry.file);
assert.equal(realpathSync(tarball), tarball);
assert.equal(createHash('sha256').update(readFileSync(tarball)).digest('hex'), entry.sha256);
assertNoBundledCodexPayloads(run('tar', ['-tzf', tarball]).trim().split('\n'));
cpSync(tarball, join(assets, entry.file)); chmodSync(join(assets, entry.file), 0o644);
}
writeFileSync(join(consumer, 'package.json'), JSON.stringify({ private: true, type: 'module' }));
const sentinelSource = join(root, 'lifecycle-sentinel'); mkdirSync(sentinelSource);
writeFileSync(join(sentinelSource, 'package.json'), JSON.stringify({ name: 'paperclip-verification-lifecycle-sentinel', version: '1.0.0',
scripts: { postinstall: 'node -e "require(\'node:fs\').writeFileSync(\'lifecycle-ran\', \'ok\')"' } }));
run('npm', ['pack', '--ignore-scripts', '--pack-destination', assets], sentinelSource);
isolated(['npm', 'install', '--ignore-scripts', '--omit=dev', ...readdirSync(assets).filter(file => file.endsWith('.tgz')).map(file => `/packages/${file}`)], { download: true });
const sentinel = join(consumer, 'node_modules/paperclip-verification-lifecycle-sentinel/lifecycle-ran');
assert.equal(existsSync(sentinel), false);
const lock = readFileSync(join(consumer, 'package-lock.json'), 'utf8');
isolated(GROK_PUBLIC_INSTALL_LIFECYCLE);
assert.equal(readFileSync(sentinel, 'utf8'), 'ok');
assert.equal(readFileSync(join(consumer, 'package-lock.json'), 'utf8'), lock);
for (const entry of manifest.archives) {
assert.equal(JSON.parse(readFileSync(join(consumer, 'node_modules', entry.name, 'package.json'))).version, manifest.releaseVersion);
}
const server = '/consumer/node_modules/@paperclipai/server', installed = server + '/dist/vendor/paperclip-runner';
const probe = (name, source, options = {}) => { writeFileSync(join(assets, name), source); return JSON.parse(isolated(['node', '/packages/' + name], options).trim()); };
const daemon = probe('daemon-probe.mjs', installedDaemonProbeSource(installed, '/consumer', sourceRevision));
assert.equal(daemon.target, 'linux-arm64');
const codex = probe('codex-probe.mjs', installedCodexProbeSource(installed + '/index.js', '/consumer', undefined, { nativeCodex: true }));
// Reuse the existing zero-turn hook as read-only verifier source. All
// production imports still resolve from the installed consumer graph.
cpSync(join(repo, 'tests/runner-e2e/installed-cli.ts'), join(assets, 'installed-cli.ts'));
writeFileSync(join(assets, 'types.js'), stripTypeScriptTypes(readFileSync(join(repo, 'tests/runner-e2e/types.ts'), 'utf8')));
// The restricted install container deliberately forbids nested namespaces.
// Keep its lifecycle/no-network proof, then test the exact same bind-mounted
// graph on the native host using Codex's normal read-only namespace policy.
const home = join(root, 'host-home'), hostTmp = join(root, 'host-tmp');
for (const directory of [home, hostTmp]) mkdirSync(directory, { mode: 0o700 });
const hostEnv = { PATH: `${dirname(process.execPath)}:/usr/bin:/bin:/usr/sbin:/sbin`, HOME: home, TMPDIR: hostTmp, LC_ALL: 'C', TZ: 'UTC' };
const hostProbe = (name, source) => {
const file = join(assets, name); writeFileSync(file, source);
return JSON.parse(execFileSync(process.execPath, [file], { cwd: consumer, env: hostEnv,
timeout: 120_000, maxBuffer: 1024 * 1024, encoding: 'utf8' }).trim());
};
const hostServer = join(consumer, 'node_modules/@paperclipai/server'), hostInstalled = join(hostServer, 'dist/vendor/paperclip-runner');
const hostDaemon = () => hostProbe('host-daemon-probe.mjs', installedDaemonProbeSource(hostInstalled, consumer, sourceRevision));
const hostCodex = () => hostProbe('host-codex-probe.mjs', installedCodexProbeSource(join(hostInstalled, 'index.js'), consumer, undefined, { nativeCodex: true }));
const digest = value => createHash('sha256').update(value).digest('hex');
const beforeCodex = hostCodex();
const before = { lockSha256: digest(lock), runnerSha256: hostDaemon().sha256.slice(7), codexCommandSha256: beforeCodex.codexCommandSha256, codexNativeExecutableSha256: beforeCodex.codexNativeExecutableSha256 };
assert.equal(before.runnerSha256, daemon.sha256.slice(7), 'Docker and host must select the same packaged daemon bytes');
assert.equal(before.codexCommandSha256, codex.codexCommandSha256, 'Docker and host must select the same official Codex command bytes');
assert.equal(before.codexNativeExecutableSha256, codex.codexNativeExecutableSha256, 'Docker and host must select the same official native Codex payload bytes');
const hostHeader = process.report.getReport().header;
assert.match(hostHeader.glibcVersionRuntime || '', /^\d+\.\d+$/, 'Native host startup must report its actual GNU libc ABI');
const startup = hostProbe('host-startup-probe.mjs', macInstalledStartupProbeSource(consumer, hostServer, daemon.sha256, join(assets, 'installed-cli.ts')));
const afterCodex = hostCodex();
const after = { lockSha256: digest(readFileSync(join(consumer, 'package-lock.json'))), runnerSha256: hostDaemon().sha256.slice(7), codexCommandSha256: afterCodex.codexCommandSha256, codexNativeExecutableSha256: afterCodex.codexNativeExecutableSha256 };
assertInstalledNativeConsumerContinuity(before, after);
assert.equal(startup.runnerSha256, before.runnerSha256);
assert.equal(startup.codexSha256, before.codexCommandSha256);
const receipt = { schema: 'paperclip.runner.linux-arm64-public-install.v1', sourceRevision, platform: process.platform, architecture: process.arch,
cleanNpmInstall: true, packageCount: manifest.archives.length, codexPackedPayloadsOmitted: true,
consumerImage: GROK_PUBLIC_INSTALL_IMAGE, consumerImagePlatform: platform,
consumerLockPreserved: true, lifecycleSentinelVerified: true, lifecycleNetwork: 'none',
startupEnvironment: 'native host, same installed graph, isolated empty homes',
startupHost: { nodeVersion: process.versions.node, nodeModulesAbi: process.versions.modules, glibcVersionRuntime: hostHeader.glibcVersionRuntime, kernelRelease: hostHeader.osRelease },
installedIdentityBefore: before, installedIdentityAfter: after,
...daemon, ...codex, startup, providerCalls: 0 };
writeFileSync(receiptPath, JSON.stringify(receipt, null, 2) + '\n');
console.log(JSON.stringify(receipt));
} finally { rmSync(root, { recursive: true, force: true }); }
}
if (process.argv[2] === '--linux-arm64-consumer') {
assert.equal(process.argv.length, 6, 'Usage: --linux-arm64-consumer PACKAGE_DIRECTORY SOURCE_SHA RECEIPT_PATH');
await verifyLinuxArmConsumer(process.argv[3], process.argv[4], process.argv[5]);
} else if (process.argv[2] === '--mac-consumer') {
assert.equal(process.argv.length, 6, 'Usage: --mac-consumer PACKAGE_DIRECTORY SOURCE_SHA RECEIPT_PATH');
await verifyMacConsumer(process.argv[3], process.argv[4], process.argv[5]);
} else {
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-'));
const prerequisite = join(root, 'native/grok');
@@ -146,6 +329,24 @@ try {
timing.stage_pack_check_ms = Math.round(performance.now() - checksStarted);
timing.stage_pack_ms = Math.round(performance.now() - packStarted);
Object.assign(timing, packTimings(packs, concurrency));
// Preserve the exact Linux-produced public tarballs for independent Mac
// consumers even if a later Linux runtime probe fails. This is a packaging
// receipt, never an installation or provider-admission pass.
if (process.env.PAPERCLIP_RUNNER_PUBLIC_PACKS_DIR) {
const output = resolve(process.env.PAPERCLIP_RUNNER_PUBLIC_PACKS_DIR);
assert.equal(JSON.parse(readFileSync(join(repo, 'server/dist/build-info.json'))).commit, sourceRevision);
assert.ok(!existsSync(output), 'Never replace retained public packages');
mkdirSync(output, { recursive: true });
const archives = [...needed].map((name, index) => {
const file = basename(tarballs[index]); cpSync(tarballs[index], join(output, file));
return { name, file, sha256: createHash('sha256').update(readFileSync(tarballs[index])).digest('hex') };
});
writeFileSync(join(output, 'manifest.json'), JSON.stringify({
schema: 'paperclip.runner.public-packages/v1', sourceRevision, releaseVersion,
publicInstallationVerified: false, providerCalls: 0, archives,
}, null, 2) + '\n');
}
const assets = join(root, 'assets'); mkdirSync(assets, { mode: 0o755 });
const consumer = join(root, 'consumer'); mkdirSync(consumer);
const cache = join(root, 'cache'); mkdirSync(cache);
@@ -259,3 +460,4 @@ try {
await stopPrefetches(prefetches);
rmSync(root, { recursive: true, force: true });
}
}