Files
PaperClipAI/server/src/services/native-runtime/remote-provider-pack-verification.test.ts
T
DottaandPaperclip 9ae4c25e7e perf(runner): reuse compatible protected provider packs (#17749)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner starts provider runtimes in remote execution
environments.
> - Sandbox images already contain qualified provider packs.
> - Startup required the image pack to match the entire current app
release manifest.
> - An unrelated app commit could therefore cause pack verification,
transfer, and installation again.
> - This pull request selects protected packs by their provider
interface and platform.
> - The benefit is no extra pack probe, directory scan, version command,
link, or upload on the compatible image path.

## Linked Issues or Issue Description

**What happened?**

Starting a remote task can reject a compatible image pack because its
source revision or unrelated provider differs from the controller. Pack
replacement added about 16 seconds in the investigated startup. Startup
also repeated full content checks.

**Expected behavior**

Use an already qualified, protected pack when its selected provider
interface and target platform are compatible. Keep the selected artifact
identity for process launch and recovery.

**Steps to reproduce**

1. Install a qualified provider pack in a sandbox image.
2. Start a controller built from another app commit.
3. Start a remote OpenCode task. The old complete-manifest comparison
rejects the image pack even when the provider interface still works.

Related repairs: #16493 reduced transfer cost. #17512 retained exact
artifact authority during reattachment. This change preserves that
recovery rule and changes fresh-launch pack selection.

## What Changed

- Add explicit OpenCode and ACPX interface revisions to produced pack
manifests. App Git SHAs and dependency pins remain provenance.
- Read bounded protected-installation metadata in the existing final
runner metadata query. Accept only root-owned paths that the execution
user cannot write, including ancestors and manifest. Reject writable
aliases.
- Select and prepare the pack in server-owned environment setup before
constructing the existing transport. Bind provider launch paths and
digests from the selected installation. Save that identity for exact
reattachment.
- Keep full content verification for uploads, writable installations,
and legacy packs. Reuse successful verification within one startup. Keep
older attached runners compatible with the optional metadata request.
- Seal image packs and document the one-time image refresh that enables
reuse across later compatible releases. Keep selected ACPX agent
qualification checks.

## Verification

- Red/green regression: the old code rejects a protected pack from
another app revision with `manifest mismatch`; the fixed path selects it
without executing verification commands.
- Native startup regression: select the installed pack's own Node digest
with one existing runner metadata request and no pack verification or
version commands.
- Targeted pack, artifact, recovery, execution-driver, and Rust metadata
tests pass. Incompatible interfaces/platforms, malformed identities,
writable roots, path aliases, and exact reattachment are covered.
- Final revision `91dc1676ff67bdbe5866c8586daac10240530cca`: workspace
typecheck and build pass. All 53 current-head checks are successful,
with no pending or failed checks. Greptile is 5/5 with no open findings.
- The broad local `pnpm test:run` attempt reported a workspace-runtime
conflict test failure; its isolated rerun passed. The broad run was
stopped after that failure. Full CI now passes, including the server
test shards. This is not a claim that the full local run passed.
- Linux filesystem evidence: the production metadata functions read a
real root-owned preinstalled pack as a non-root user in 16 microseconds
median and 20 microseconds p95 across 100 warm reads. This measures
metadata admission, not model or hosted response latency.
- All 910 affected server tests and 57 Copilot attestation tests pass
locally on the final implementation. Full CI covers the unchanged Runner
transport. No provider qualification hash was refreshed.
- Built the actual Linux daemon at
`416e7ec7425d0d3565b4a5e1808002d650a60414` and ran its metadata command
as UID 65534. The protected installation returned its real manifest with
`immutable: true`; writable alias and missing-pack cases returned
`immutable: false`. This is metadata/permission evidence, not provider
execution.
- Hosted rollout and a hosted end-to-end latency measurement have not
run.

## Risks

- Existing images need one rebuild to publish the compatibility
declaration. Packs without it keep the legacy content-verification path.
- Fast admission relies on image publication producing a qualified
protected tree. Root execution and task-writable installations retain
full verification.
- Bump an interface revision when required runner/pack behavior changes
incompatibly. Deploy a new image to roll out compatible runtime fixes or
upgrades.
- This removes provider-pack preparation delay. Other runner startup,
context, model, and finalization costs remain separate.

## Model Used

OpenAI Codex, configured model `gpt-6.1-sol`, reasoning effort `xhigh`,
with tool use and code execution. The context-window size is not exposed
in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-26 14:43:12 -05:00

107 lines
6.8 KiB
TypeScript

import { createHash } from "node:crypto";
import { execFile } from "node:child_process";
import { mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { promisify } from "node:util";
import type { CommandManagedRuntimeRunner } from "@paperclipai/adapter-utils/command-managed-runtime";
import { describe, expect, it, vi } from "vitest";
import { canonicalProviderPackJson, verifyRemoteProviderPack, type ProviderPackIdentity } from "./remote-provider-pack-verification.js";
const sha = (text: string) => `sha256:${createHash("sha256").update(text).digest("hex")}`;
const manifest = (revision: string): ProviderPackIdentity => {
const payload = {
compatibility: { opencode: 1, acpx: 1 },
pins: { opencode: "1.18.34", nodeMinimum: "24.11.0" },
target: { platform: "linux", architecture: "x64" },
runnerSourceRevision: revision.repeat(40),
artifacts: Object.fromEntries(Object.entries({
nodeCommand: "node_modules/node/bin/node", opencodeCommand: "node_modules/.bin/opencode",
productionLock: "pnpm-lock.yaml", opencodeExecutable: "node_modules/opencode-ai/bin/opencode.exe",
opencodeProxy: "dist/cli/opencode-app-server-proxy.cjs", acpxSidecar: "dist/cli/acpx-runtime-sidecar.cjs",
grokLauncher: "dist/providers/grok/launcher.cjs",
}).map(([name, path]) => [name, { path, sha256: sha(`${revision}-${name}`) }])) as ProviderPackIdentity["payload"]["artifacts"],
};
return { schema: "paperclip-runner/remote-provider-pack/v1", payload, digest: sha(canonicalProviderPackJson(payload)) };
};
describe("installed provider pack compatibility", () => {
it("reuses a protected pack across app revisions without executing verification commands", async () => {
const installed = manifest("a");
const current = structuredClone(installed);
current.payload.runnerSourceRevision = "b".repeat(40);
current.digest = sha(canonicalProviderPackJson(current.payload));
const root = await mkdtemp(join(tmpdir(), "installed-pack-compatibility-"));
await mkdir(dirname(join(root, installed.payload.artifacts.nodeCommand.path)), { recursive: true });
await symlink(process.execPath, join(root, installed.payload.artifacts.nodeCommand.path));
await writeFile(join(root, "provider-pack.json"), JSON.stringify(installed));
const execute = vi.fn(async (request: Parameters<CommandManagedRuntimeRunner["execute"]>[0]) => {
try {
const result = await promisify(execFile)(request.command, request.args ?? [], { cwd: root, timeout: 2_000 });
return { ...result, exitCode: 0, signal: null, timedOut: false, pid: null, startedAt: new Date().toISOString() };
} catch (error) {
const failed = error as Error & { code: number; stdout: string; stderr: string };
if (typeof failed.code !== "number") throw error;
return { stdout: failed.stdout, stderr: failed.stderr, exitCode: failed.code, signal: null, timedOut: false, pid: null, startedAt: new Date().toISOString() };
}
});
const input = { runner: { execute }, cwd: root, packRoot: root, manifest: current };
try {
// Exercise the old complete-manifest comparison in a real Node process.
// Only provenance changed; provider bytes and interface are identical.
await expect(verifyRemoteProviderPack(input)).rejects.toThrow("manifest mismatch");
expect(execute).toHaveBeenCalledOnce();
execute.mockClear();
const selected = await verifyRemoteProviderPack({ ...input,
installation: { root, manifest: installed, immutable: true }, provider: { kind: "opencode" } });
expect(selected).toEqual(installed);
expect(execute).not.toHaveBeenCalled();
} finally {
await rm(root, { recursive: true, force: true });
}
});
const reseal = (pack: ProviderPackIdentity) => ({ ...pack, digest: sha(canonicalProviderPackJson(pack.payload)) });
const check = (installed: ProviderPackIdentity, options: { immutable?: boolean; requireExactIdentity?: boolean } = {}) => {
const execute = vi.fn();
return verifyRemoteProviderPack({ runner: { execute }, cwd: "/workspace", packRoot: "/opt/paperclip-runner/provider-pack",
manifest: manifest("b"), installation: { root: "/opt/paperclip-runner/provider-pack", manifest: installed, immutable: options.immutable ?? true },
provider: { kind: "opencode" }, requireExactIdentity: options.requireExactIdentity });
};
it.each(["interface", "platform", "schema", "artifact_path", "digest", "legacy"])("rejects an incompatible %s", async field => {
let pack = manifest("a");
if (field === "interface") pack.payload.compatibility!.opencode += 1;
if (field === "legacy") delete pack.payload.compatibility;
if (field === "platform") pack.payload.target.architecture = "arm64";
if (field === "schema") pack.schema = "unknown";
if (field === "artifact_path") pack.payload.artifacts.nodeCommand.path = "../node";
pack = reseal(pack);
if (field === "digest") pack.digest = sha("corrupt");
await expect(check(pack)).rejects.toThrow("runner_remote_provider_artifact_incompatible");
});
it("keeps writable installations at the content-verification boundary", async () => {
await expect(check(manifest("b"), { immutable: false })).rejects.toThrow("installation is incompatible");
});
it("does not replace an admitted pack during reattachment", async () => {
await expect(check(manifest("a"), { requireExactIdentity: true })).rejects.toThrow("incompatible");
await expect(check(manifest("b"), { requireExactIdentity: true })).resolves.toEqual(manifest("b"));
});
it("ignores unrelated provider and dependency changes for OpenCode", async () => {
const pack = manifest("a");
pack.payload.compatibility!.acpx += 1;
pack.payload.pins.acpx = "different-version";
pack.payload.acpxProfileDigests = { codex: sha("different-codex") };
await expect(check(reseal(pack))).resolves.toEqual(reseal(pack));
});
it("checks only the selected ACPX profile", async () => {
const current = manifest("b"), installed = manifest("a");
current.payload.acpxProfileDigests = { claude: sha("claude"), codex: sha("codex") };
installed.payload.acpxProfileDigests = { claude: sha("claude"), codex: sha("other-codex") };
const input = { runner: { execute: vi.fn() }, cwd: "/workspace", packRoot: "/opt/paperclip-runner/provider-pack",
manifest: reseal(current), installation: { root: "/opt/paperclip-runner/provider-pack", manifest: reseal(installed), immutable: true },
provider: { kind: "acpx" as const, agent: "claude" } };
await expect(verifyRemoteProviderPack(input)).resolves.toEqual(reseal(installed));
await expect(verifyRemoteProviderPack({ ...input, provider: { kind: "acpx", agent: "codex" } })).rejects.toThrow("profile is incompatible");
});
});