Build checked portable package artifacts and shared runtime assets

This commit is contained in:
Timothy J. Aveni
2026-09-20 00:42:19 -07:00
parent 60b1bdb39f
commit ab41e67dc5
8 changed files with 216 additions and 2 deletions
+1
View File
@@ -75,6 +75,7 @@
runHook preInstall runHook preInstall
mkdir -p "$out" mkdir -p "$out"
install -Dm644 nix/checked-package.nix "$out/share/checked-package.nix" install -Dm644 nix/checked-package.nix "$out/share/checked-package.nix"
install -Dm644 nix/checked-artifacts.nix "$out/share/checked-artifacts.nix"
substitute nix/checked-candidate.nix "$out/share/checked-candidate.nix" \ substitute nix/checked-candidate.nix "$out/share/checked-candidate.nix" \
--replace-fail '@nixpkgs@' '${pkgs.path}' --replace-fail '@nixpkgs@' '${pkgs.path}'
cp --reflink=auto --recursive grammar "$out/grammar" cp --reflink=auto --recursive grammar "$out/grammar"
+41
View File
@@ -0,0 +1,41 @@
# Candidate artifact evaluation. This is separate from the old server activation path;
# the new coordinator consumes its manifest/receipt, not the legacy server ABI.
{
source,
plan,
generator,
core,
sharedRuntime,
typescript,
packageRevisionId,
system ? builtins.currentSystem,
}:
let
packageSource = builtins.path {
path = /. + source;
name = "quixos-package-source";
filter =
path: _:
!(builtins.elem (baseNameOf path) [
".git"
".jj"
]);
};
package = builtins.getFlake (
"path:" + builtins.unsafeDiscardStringContext (toString packageSource)
);
checked =
package.quixosPackages.${system}.checkedArtifacts
or (throw "Package ${packageRevisionId} lacks checkedArtifacts for the portable execution world.");
in
checked {
inherit packageRevisionId;
plan = builtins.path {
path = /. + plan;
name = "candidate-execution-plan.json";
};
generator = builtins.storePath generator;
core = builtins.storePath core;
sharedRuntime = builtins.storePath sharedRuntime;
typescript = builtins.storePath typescript;
}
+15
View File
@@ -99,6 +99,13 @@ export interface ArtifactResponse {
headers: Record<string, string>; headers: Record<string, string>;
body: Uint8Array; body: Uint8Array;
} }
/** Shared runtime imports use content identity, independent of the importing package/world.
* The file remains covered by every importing implementation's checked manifest. */
export function contentArtifactUrl(file: Pick<ArtifactFile, "path" | "digest">): string {
if (!/^[0-9a-f]{64}$/.test(file.digest)) fail(file.path, "Invalid content digest");
artifactPath(file.path);
return `/__quixos/artifacts/content/${file.digest}/${encodeURIComponent(path.basename(file.path))}`;
}
/** This registry is placed behind the workspace gateway's existing authentication. /** This registry is placed behind the workspace gateway's existing authentication.
* Lookup uses an explicit URL table; request paths never become filesystem paths. */ * Lookup uses an explicit URL table; request paths never become filesystem paths. */
export class BrowserArtifactGateway { export class BrowserArtifactGateway {
@@ -124,6 +131,14 @@ export class BrowserArtifactGateway {
) )
fail(url, "Conflicting browser asset route"); fail(url, "Conflicting browser asset route");
this.routes.set(url, { revision: manifest.packageRevisionId, file: Object.freeze({ ...file }) }); this.routes.set(url, { revision: manifest.packageRevisionId, file: Object.freeze({ ...file }) });
// Exact shared-file names are a packaging convention, never a path lookup capability.
if (file.path.startsWith("share/quixos/shared/")) {
const contentUrl = contentArtifactUrl(file);
const existing = this.routes.get(contentUrl);
if (existing && (existing.file.bytes !== file.bytes || existing.file.mediaType !== file.mediaType))
fail(contentUrl, "Conflicting shared asset metadata");
this.routes.set(contentUrl, { revision: manifest.packageRevisionId, file: Object.freeze({ ...file }) });
}
} }
entries.push({ entries.push({
url: prefix + implementation.path.split("/").map(encodeURIComponent).join("/"), url: prefix + implementation.path.split("/").map(encodeURIComponent).join("/"),
+4
View File
@@ -2,3 +2,7 @@ export * from "./plan.js";
export * from "./artifacts.js"; export * from "./artifacts.js";
export * from "./ports.js"; export * from "./ports.js";
export * from "./files.js"; export * from "./files.js";
export * from "./receipts.js";
export { generatePortableTypeScriptBindings } from "../bindings/portable.js";
+5 -2
View File
@@ -3,7 +3,8 @@
export const transactionPortAbi = "quixos-transaction-ports-v1"; export const transactionPortAbi = "quixos-transaction-ports-v1";
export type PortAction = export type PortAction =
| { kind: "state"; primitive: "read" | "write" | "edit" } | { kind: "state"; primitive: "read" | "write" | "edit" }
| { kind: "edge"; primitive: "resolve" | "connect" | "disconnect" } | { kind: "edge"; primitive: "resolve" | "disconnect" }
| { kind: "edge"; primitive: "connect"; key: string }
| { kind: "interface"; operationId: string; key?: string } | { kind: "interface"; operationId: string; key?: string }
| { kind: "query" } | { kind: "query" }
| { kind: "creation"; key: string }; | { kind: "creation"; key: string };
@@ -19,6 +20,7 @@ export interface PortInvocation {
} }
export interface PackageInvocation { export interface PackageInvocation {
abi: typeof transactionPortAbi; abi: typeof transactionPortAbi;
workspace: string;
frame: string; frame: string;
invocation: string; invocation: string;
exportId: string; exportId: string;
@@ -66,7 +68,8 @@ export function checkPortInvocation(value: unknown): PortInvocation {
action.kind === "state" action.kind === "state"
? exact(["kind", "primitive"]) && ["read", "write", "edit"].includes(action.primitive as string) ? exact(["kind", "primitive"]) && ["read", "write", "edit"].includes(action.primitive as string)
: action.kind === "edge" : action.kind === "edge"
? exact(["kind", "primitive"]) && ["resolve", "connect", "disconnect"].includes(action.primitive as string) ? (exact(["kind", "primitive"]) && ["resolve", "disconnect"].includes(action.primitive as string)) ||
(exact(["kind", "primitive", "key"]) && action.primitive === "connect" && identifier(action.key))
: action.kind === "interface" : action.kind === "interface"
? (exact(["kind", "operationId"]) || (exact(["kind", "operationId", "key"]) && identifier(action.key))) && ? (exact(["kind", "operationId"]) || (exact(["kind", "operationId", "key"]) && identifier(action.key))) &&
identifier(action.operationId) identifier(action.operationId)
+102
View File
@@ -0,0 +1,102 @@
import { createHash } from "node:crypto";
import { generatePortableTypeScriptBindings } from "../bindings/portable.js";
import { type ExecutionPlan, ExecutionPlanError, hexDigest } from "./plan.js";
import { type ArtifactReader, type PackageArtifacts } from "./artifacts.js";
import { loadArtifactWorld, type LoadedArtifactWorld } from "./files.js";
export interface ArtifactReceipt {
schemaVersion: 1;
packageRevisionId: string;
bindingDigest: string;
sourceDigest: string;
manifestDigest: string;
generatorDigest: string;
generated: { target: string; path: string; digest: string; bytes: number }[];
}
const hashBytes = (bytes: Uint8Array) => createHash("sha256").update(bytes).digest("hex");
/** Deterministic expected receipt. A trusted build must also typecheck and bundle these exact sources.
* This is consistency evidence inside a trusted Nix build path, not remote build attestation. */
export function expectedArtifactReceipt(
plan: ExecutionPlan,
manifest: PackageArtifacts,
generatorDigest: string,
): { receipt: ArtifactReceipt; sources: Map<string, Uint8Array> } {
if (!/^[0-9a-f]{64}$/.test(generatorDigest)) throw new Error("Invalid trusted generator digest");
const pkg = plan.definition.packages.find((p) => p.revision === manifest.packageRevisionId);
const schemaPackage = plan.definition.bindingSchema.packages.find((p) => p.revisionId === manifest.packageRevisionId);
if (
!pkg ||
!schemaPackage ||
manifest.bindingDigest !== pkg.bindingDigest ||
manifest.sourceDigest !== pkg.sourceDigest
)
throw new ExecutionPlanError(
"INVALID_RECEIPT",
manifest.packageRevisionId,
"Receipt package/schema/source mismatch",
);
const available = new Set(manifest.implementations.map((i) => i.target));
const targets = [
...new Set(
schemaPackage.exports.flatMap((e) => (e.execution && e.execution.kind !== "remote" ? e.execution.targets : [])),
),
]
.filter((t) => available.has(t))
.sort();
const sources = new Map<string, Uint8Array>();
const generated = targets.map((target) => {
if (!/^[a-zA-Z0-9_-]+$/.test(target)) throw new Error("Unsupported generated target filename");
const path = `share/quixos/generated/${target}.ts`;
const bytes = new TextEncoder().encode(
generatePortableTypeScriptBindings(plan.definition.bindingSchema, pkg.revision, { target }),
);
sources.set(path, bytes);
return { target, path, digest: hashBytes(bytes), bytes: bytes.length };
});
return {
receipt: {
schemaVersion: 1,
packageRevisionId: pkg.revision,
bindingDigest: pkg.bindingDigest,
sourceDigest: pkg.sourceDigest,
manifestDigest: hexDigest(manifest),
generatorDigest,
generated,
},
sources,
};
}
export async function checkArtifactReceipt(
plan: ExecutionPlan,
manifest: PackageArtifacts,
receipt: unknown,
generatorDigest: string,
read: ArtifactReader,
): Promise<void> {
const expected = expectedArtifactReceipt(plan, manifest, generatorDigest);
if (hexDigest(receipt) !== hexDigest(expected.receipt))
throw new ExecutionPlanError(
"INVALID_RECEIPT",
manifest.packageRevisionId,
"Receipt differs from the trusted compiler/schema/artifact expectation",
);
for (const file of expected.receipt.generated) {
const actual = await read(manifest.packageRevisionId, file.path, file.bytes);
if (actual.length !== file.bytes || hashBytes(actual) !== file.digest)
throw new ExecutionPlanError("INVALID_RECEIPT", file.path, "Generated bindings were replaced or omitted");
}
}
/** Activation uses this entrypoint with its trusted generator identity, not the manifest-only loader. */
export async function loadCheckedArtifactWorld(
plan: ExecutionPlan,
roots: Readonly<Record<string, string>>,
generatorDigest: string,
): Promise<LoadedArtifactWorld> {
const loaded = await loadArtifactWorld(plan, roots);
for (const manifest of loaded.manifests) {
const bytes = await loaded.read(manifest.packageRevisionId, "share/quixos/build-receipt.json", 16 * 1024 * 1024);
const receipt: unknown = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
await checkArtifactReceipt(plan, manifest, receipt, generatorDigest, loaded.read);
}
return loaded;
}
+28
View File
@@ -10,6 +10,7 @@ import {
loadArtifactWorld, loadArtifactWorld,
BrowserArtifactGateway, BrowserArtifactGateway,
implementationDigest, implementationDigest,
contentArtifactUrl,
} from "../src/execution-world/index.js"; } from "../src/execution-world/index.js";
import { loadReaderWorkspace } from "./reader-fixture.js"; import { loadReaderWorkspace } from "./reader-fixture.js";
import { buildArtifactFixture } from "./artifact-fixture.js"; import { buildArtifactFixture } from "./artifact-fixture.js";
@@ -52,6 +53,8 @@ test("gateway serves only declared browser assets with correct Wasm MIME and con
digest: createHash("sha256").update(wasm).digest("hex"), digest: createHash("sha256").update(wasm).digest("hex"),
mediaType: "application/wasm", mediaType: "application/wasm",
}); });
const shared = { ...browser.files[0]!, path: "share/quixos/shared/core.mjs" };
browser.files.push(shared);
const { contentDigest, ...body } = browser; const { contentDigest, ...body } = browser;
browser.contentDigest = implementationDigest(body); browser.contentDigest = implementationDigest(body);
for (const implementation of fixture.manifest.implementations) for (const implementation of fixture.manifest.implementations)
@@ -65,6 +68,7 @@ test("gateway serves only declared browser assets with correct Wasm MIME and con
const loaded = await loadArtifactWorld(plan, { [fixture.manifest.packageRevisionId]: root }); const loaded = await loadArtifactWorld(plan, { [fixture.manifest.packageRevisionId]: root });
const gateway = new BrowserArtifactGateway(loaded); const gateway = new BrowserArtifactGateway(loaded);
assert.equal(gateway.entries.length, 1); assert.equal(gateway.entries.length, 1);
assert.deepEqual((await gateway.response(contentArtifactUrl(shared)))?.body, Buffer.from(fixture.bytes));
const entry = gateway.entries[0]!; const entry = gateway.entries[0]!;
const response = await gateway.response(entry.url); const response = await gateway.response(entry.url);
assert.equal(response?.status, 200); assert.equal(response?.status, 200);
@@ -86,3 +90,27 @@ test("gateway serves only declared browser assets with correct Wasm MIME and con
await rm(root, { recursive: true, force: true }); await rm(root, { recursive: true, force: true });
} }
}); });
test("checked receipts bind regenerated code, generator identity and manifests", async () => {
const { expectedArtifactReceipt, checkArtifactReceipt } = await import("../src/execution-world/receipts.js");
const plan = compileExecutionPlan(await loadReaderWorkspace());
const fixture = buildArtifactFixture(plan);
const generator = "a".repeat(64);
const { receipt, sources } = expectedArtifactReceipt(plan, fixture.manifest, generator);
const read = async (_revision: string, file: string) => sources.get(file)!;
await checkArtifactReceipt(plan, fixture.manifest, receipt, generator, read);
await assert.rejects(
checkArtifactReceipt(plan, fixture.manifest, { ...receipt, generatorDigest: "b".repeat(64) }, generator, read),
/trusted compiler/,
);
await assert.rejects(
checkArtifactReceipt(plan, fixture.manifest, { ...receipt, manifestDigest: "b".repeat(64) }, generator, read),
/trusted compiler/,
);
await assert.rejects(
checkArtifactReceipt(plan, fixture.manifest, receipt, generator, async () =>
new TextEncoder().encode("substituted"),
),
/replaced or omitted/,
);
});
+20
View File
@@ -11,3 +11,23 @@ for (const target of ["browser", "server"])
generatePortableTypeScriptBindings(plan.definition.bindingSchema, "package:reader-runtime@1", { target }), generatePortableTypeScriptBindings(plan.definition.bindingSchema, "package:reader-runtime@1", { target }),
); );
await writeFile("dist/portable-reader/plan.json", JSON.stringify(plan)); await writeFile("dist/portable-reader/plan.json", JSON.stringify(plan));
const serviceWorkspace = structuredClone(plan.definition.workspace);
serviceWorkspace.atoms = [];
serviceWorkspace.conformances = [];
serviceWorkspace.constructors = [];
serviceWorkspace.interfaceImports = [];
serviceWorkspace.sharedAttachments = [];
serviceWorkspace.packageImports[0]!.queries = [];
serviceWorkspace.packageImports[0]!.checkedQueries = [];
const original = serviceWorkspace.packageImports[0]!.exports.find((e) => e.kind === "function")!;
serviceWorkspace.packageImports[0]!.exports = [
{
...original,
kind: "function",
execution: { kind: "remote" },
inputType: { kind: "scalar", name: "string" },
outputType: { kind: "scalar", name: "string" },
dependencyPorts: [],
},
];
await writeFile("dist/portable-reader/service-plan.json", JSON.stringify(compileExecutionPlan(serviceWorkspace)));