Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f3fdfe64e | ||
|
|
0a7bdf7657 | ||
|
|
137dcaa101 | ||
|
|
a86de3fe5b | ||
|
|
165510341d | ||
|
|
b406738249 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
|
||||
"sourceCommit": "c4319441699b7b8c5af7e70dade6e4ad8cccddea",
|
||||
"sourceCommit": "d1ab9c912133511cfd3de65550965e0e8806678f",
|
||||
"sourcePath": "quixos-protocol",
|
||||
"exportName": "quixos-protocol",
|
||||
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
||||
|
||||
@@ -36,33 +36,37 @@ export async function loadCheckedInstallationCandidate(
|
||||
if (!rootPattern.test(root) || !(await lstat(root)).isDirectory())
|
||||
throw Error("Candidate must be an immutable Nix output directory");
|
||||
const scope = (resources.budget ?? candidateMetadataBudget).acquire(resources.owner ?? root);
|
||||
let releaseValidation = () => {};
|
||||
try {
|
||||
// Validate the duplicated source contract by digest, then discard its tree
|
||||
// before allocating the substantially larger checked execution plan.
|
||||
const workspaceDigest = await (async () => {
|
||||
const input = await readJson(root, "candidate.json", scope);
|
||||
try {
|
||||
const { executionContracts: _legacyContracts, ...workspace } = input.value;
|
||||
return hexDigest(workspace);
|
||||
} finally {
|
||||
input.releaseTree();
|
||||
}
|
||||
})();
|
||||
// Sequential reads avoid partially rejected Promise.all() work allocating
|
||||
// after the failed candidate's reservation has been released.
|
||||
const values = [];
|
||||
for (const file of [
|
||||
"candidate.json",
|
||||
"execution-plan.json",
|
||||
"execution-artifacts.json",
|
||||
"source.json",
|
||||
"entry.json",
|
||||
"kernel.json",
|
||||
"queries.json",
|
||||
"graph-execution.json",
|
||||
]) {
|
||||
const parsed = await readJson(root, file, scope);
|
||||
values.push(parsed.value);
|
||||
// candidate.json duplicates the checked plan for validation. Nothing in
|
||||
// the installed candidate retains this input tree after validation.
|
||||
if (file === "candidate.json") releaseValidation = parsed.releaseTree;
|
||||
}
|
||||
const [candidate, plan, artifacts, source, entryRequest, kernel, queries, graph] = values;
|
||||
const { executionContracts: _legacyContracts, ...workspace } = candidate;
|
||||
const [plan, artifacts, source, entryRequest, kernel, queries] = values;
|
||||
if (
|
||||
plan?.definition?.codec !== "camino-execution-plan-v1" ||
|
||||
hexDigest(plan.definition) !== plan.digest ||
|
||||
hexDigest(workspace) !== hexDigest(plan.definition.workspace)
|
||||
workspaceDigest !== hexDigest(plan.definition.workspace)
|
||||
)
|
||||
throw new ExecutionPlanError(
|
||||
"CANDIDATE_PLAN_MISMATCH",
|
||||
@@ -113,14 +117,24 @@ export async function loadCheckedInstallationCandidate(
|
||||
"Stored interpreter input must match the checked build and plan",
|
||||
);
|
||||
const expectedGraph = graphExecutionRepresentation(plan, installation, kernel, queries);
|
||||
if (hexDigest(graph) !== hexDigest(expectedGraph) || artifacts.execution !== hexDigest(graph))
|
||||
throw new ExecutionPlanError(
|
||||
"CANDIDATE_EXECUTION_MISMATCH",
|
||||
root,
|
||||
"Stored graph execution differs from checked inputs",
|
||||
);
|
||||
// The stored graph embeds the same kernel/query programs. Validate it in a
|
||||
// short-lived scope and retain the representation that shares those trees.
|
||||
await (async () => {
|
||||
const input = await readJson(root, "graph-execution.json", scope);
|
||||
try {
|
||||
const digest = hexDigest(input.value);
|
||||
if (digest !== hexDigest(expectedGraph) || artifacts.execution !== digest)
|
||||
throw new ExecutionPlanError(
|
||||
"CANDIDATE_EXECUTION_MISMATCH",
|
||||
root,
|
||||
"Stored graph execution differs from checked inputs",
|
||||
);
|
||||
} finally {
|
||||
input.releaseTree();
|
||||
}
|
||||
})();
|
||||
const provenance = {
|
||||
workspaceRevision: workspace.id as string,
|
||||
workspaceRevision: plan.definition.workspace.id as string,
|
||||
source: { repository: source.repository as string, commit: source.commit as string },
|
||||
generator: artifacts.generator as string,
|
||||
packages: (plan as ExecutionPlan).definition.packages.map((pkg) => ({
|
||||
@@ -142,9 +156,9 @@ export async function loadCheckedInstallationCandidate(
|
||||
return {
|
||||
reference,
|
||||
workspace: plan.definition.workspace.workspaceId,
|
||||
world: hexDigest(graph),
|
||||
world: hexDigest(expectedGraph),
|
||||
plan: kernel.plan as string,
|
||||
graphExecution: graph,
|
||||
graphExecution: expectedGraph,
|
||||
provenance,
|
||||
entry,
|
||||
kernel,
|
||||
@@ -161,8 +175,6 @@ export async function loadCheckedInstallationCandidate(
|
||||
} catch (error) {
|
||||
scope.release();
|
||||
throw error;
|
||||
} finally {
|
||||
releaseValidation();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ export class CandidateMetadataBudget {
|
||||
private bytes = 0;
|
||||
private scopes = 0;
|
||||
private readonly owners = new Map<string, number>();
|
||||
constructor(limits = { bytes: 512 * 1024 * 1024, ownerBytes: 256 * 1024 * 1024, candidates: 128 }) {
|
||||
// A cutover holds both the serving and proposed installation plus transient
|
||||
// parsing space. The 57-repository reader retains ~133 MiB per installation;
|
||||
// a 256 MiB owner ceiling cannot admit its ordinary atomic replacement.
|
||||
// Keep the aggregate ceiling independent and leave room for parsing overlap.
|
||||
constructor(limits = { bytes: 512 * 1024 * 1024, ownerBytes: 384 * 1024 * 1024, candidates: 128 }) {
|
||||
for (const value of Object.values(limits))
|
||||
if (!Number.isSafeInteger(value) || value < 1) throw Error("INVALID_CANDIDATE_METADATA_BUDGET");
|
||||
if (limits.ownerBytes > limits.bytes) throw Error("INVALID_CANDIDATE_METADATA_BUDGET");
|
||||
|
||||
@@ -26,13 +26,41 @@ export interface ImportFinding {
|
||||
export const restrictedSDKSpecifier = "quixos:restricted-sdk/v1";
|
||||
export const restrictedSDKPath = "share/quixos/restricted/core.mjs";
|
||||
|
||||
/** Compare trusted SDK syntax without build-machine paths in source comments.
|
||||
* Keep literal/directive spelling and every executable AST node. This is SDK
|
||||
* compatibility only: artifact files and the executed host SDK still have exact
|
||||
* byte digests, and authored SDK bytes are never loaded by the restricted host.
|
||||
*/
|
||||
export function restrictedSDKIdentity(source: string): string {
|
||||
if (Buffer.byteLength(source) > 2 * 1024 * 1024) throw Error("RESTRICTED_SDK_ANALYSIS_LIMIT");
|
||||
const syntax = parse(source, { sourceType: "module", attachComment: false });
|
||||
const metadata = new Set([
|
||||
"start",
|
||||
"end",
|
||||
"loc",
|
||||
"comments",
|
||||
"leadingComments",
|
||||
"innerComments",
|
||||
"trailingComments",
|
||||
"parenStart",
|
||||
]);
|
||||
let nodes = 0;
|
||||
const encoded = JSON.stringify(syntax, (key, value) => {
|
||||
if (metadata.has(key)) return undefined;
|
||||
if (value && typeof value === "object" && typeof value.type === "string" && ++nodes > 100000)
|
||||
throw Error("RESTRICTED_SDK_ANALYSIS_LIMIT");
|
||||
return value;
|
||||
});
|
||||
return hexDigest({ codec: "quixos-restricted-sdk-syntax-v1", syntax: JSON.parse(encoded) });
|
||||
}
|
||||
|
||||
/** Mechanical candidate checks, not authorization or an engine-safety proof.
|
||||
* The caller must first verify the manifest and all declared file digests. */
|
||||
export async function inspectRestrictedImplementation(
|
||||
revision: string,
|
||||
implementation: ArtifactImplementation,
|
||||
read: ArtifactReader,
|
||||
qualifiedSDKDigest?: string,
|
||||
qualifiedSDKIdentity?: string,
|
||||
): Promise<ImportFinding[]> {
|
||||
const entry = implementation.files.find((file) => file.path === implementation.path);
|
||||
const sdk = implementation.files.find((file) => file.path === restrictedSDKPath);
|
||||
@@ -48,10 +76,18 @@ export async function inspectRestrictedImplementation(
|
||||
!/^[$A-Z_a-z][$\w]*$/.test(implementation.registryExport ?? "")
|
||||
)
|
||||
return [{ code: "RESTRICTED_CLOSURE", detail: "Expected one closed module and the restricted SDK", line: null }];
|
||||
if (qualifiedSDKDigest && sdk.digest !== qualifiedSDKDigest)
|
||||
return [{ code: "RESTRICTED_SDK_UNSUPPORTED", detail: "SDK differs from the qualified host profile", line: null }];
|
||||
if (entry.bytes > 1024 * 1024 || sdk.bytes > 2 * 1024 * 1024)
|
||||
return [{ code: "ANALYSIS_LIMIT", detail: "Restricted closure exceeds the source budget", line: null }];
|
||||
if (qualifiedSDKIdentity) {
|
||||
const source = Buffer.from(await read(revision, sdk.path, sdk.bytes)).toString("utf8");
|
||||
try {
|
||||
if (restrictedSDKIdentity(source) !== qualifiedSDKIdentity) throw Error("SDK syntax differs");
|
||||
} catch {
|
||||
return [
|
||||
{ code: "RESTRICTED_SDK_UNSUPPORTED", detail: "SDK differs from the qualified host profile", line: null },
|
||||
];
|
||||
}
|
||||
}
|
||||
return inspectJavaScriptSurface(Buffer.from(await read(revision, entry.path, entry.bytes)).toString("utf8"), [
|
||||
restrictedSDKSpecifier,
|
||||
]);
|
||||
|
||||
@@ -4,6 +4,62 @@ import { mkdtemp, writeFile, symlink, rm } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { CandidateMetadataBudget, readCandidateMetadata } from "../src/execution-world/metadata.js";
|
||||
import { loadCheckedInstallationCandidate } from "../src/execution-world/candidate.js";
|
||||
|
||||
test(
|
||||
"a real checked application can stage its replacement within the default metadata budget",
|
||||
{
|
||||
skip: !process.env.QX_CHECKED_CANDIDATE,
|
||||
},
|
||||
async () => {
|
||||
const root = process.env.QX_CHECKED_CANDIDATE!;
|
||||
const budget = new CandidateMetadataBudget();
|
||||
const first = await loadCheckedInstallationCandidate(root, {}, { budget, owner: "reader" });
|
||||
try {
|
||||
const retained = budget.snapshot().bytes;
|
||||
const second = await loadCheckedInstallationCandidate(root, {}, { budget, owner: "reader" });
|
||||
try {
|
||||
assert.equal(budget.snapshot().bytes, retained * 2);
|
||||
assert.deepEqual(second.reference, first.reference, "Compaction preserves exact checked identity");
|
||||
assert.equal(second.graphExecution.kernel, second.kernel, "Graph representation shares the validated program");
|
||||
assert.equal(second.graphExecution.queries, second.queries);
|
||||
// A deliberately tighter pool admits the serving installation but must
|
||||
// reject replacement parsing without leaking a candidate or tree lease.
|
||||
const tight = new CandidateMetadataBudget({
|
||||
bytes: retained + 1,
|
||||
ownerBytes: retained + 1,
|
||||
candidates: 2,
|
||||
});
|
||||
const blocker = tight.acquire("reader");
|
||||
blocker.charge(retained);
|
||||
try {
|
||||
await assert.rejects(
|
||||
loadCheckedInstallationCandidate(root, {}, { budget: tight, owner: "reader" }),
|
||||
/CANDIDATE_METADATA_LIMIT/,
|
||||
);
|
||||
assert.equal(tight.snapshot().bytes, retained);
|
||||
assert.equal(tight.snapshot().candidates, 1);
|
||||
} finally {
|
||||
blocker.release();
|
||||
}
|
||||
const other = budget.acquire("other");
|
||||
try {
|
||||
assert.throws(() => other.charge(budget.limits.bytes), /CANDIDATE_METADATA_LIMIT/);
|
||||
assert.equal(budget.snapshot().bytes, retained * 2, "Rejected admission must not leak charges");
|
||||
} finally {
|
||||
other.release();
|
||||
}
|
||||
} finally {
|
||||
second.metadata.release();
|
||||
}
|
||||
assert.equal(budget.snapshot().bytes, retained, "The serving candidate survives failed or completed staging");
|
||||
} finally {
|
||||
first.metadata.release();
|
||||
}
|
||||
assert.equal(budget.snapshot().bytes, 0);
|
||||
assert.equal(budget.snapshot().candidates, 0);
|
||||
},
|
||||
);
|
||||
|
||||
test("concurrent candidate reservations enforce aggregate and owner limits and release exactly once", () => {
|
||||
const budget = new CandidateMetadataBudget({ bytes: 1000, ownerBytes: 600, candidates: 3 });
|
||||
|
||||
@@ -6,10 +6,64 @@ import {
|
||||
compileExecutionPlan,
|
||||
hexDigest,
|
||||
inspectJavaScriptSurface,
|
||||
inspectRestrictedImplementation,
|
||||
restrictedSDKIdentity,
|
||||
restrictedSDKPath,
|
||||
type ArtifactImplementation,
|
||||
reportExecutionPlacement,
|
||||
type ExecutionInstallation,
|
||||
} from "../src/execution-world/index.js";
|
||||
|
||||
test("restricted SDK identity ignores build comments and positions, preserving executable syntax", () => {
|
||||
const source = 'export function value() { return ("hello"); }';
|
||||
const x86 = "// /nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-core/dist/portable.js\n" + source;
|
||||
const arm = "// /nix/store/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb-core/dist/portable.js\n\n" + source;
|
||||
assert.equal(restrictedSDKIdentity(x86), restrictedSDKIdentity(arm));
|
||||
for (const changed of [
|
||||
'export function value() { return ("goodbye"); }',
|
||||
'export function value() { return\n("hello"); }',
|
||||
'export function value() { fetch("hello"); }',
|
||||
'import x from "node:fs"; ' + source,
|
||||
'export function value() { return "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-core"; }',
|
||||
])
|
||||
assert.notEqual(restrictedSDKIdentity(source), restrictedSDKIdentity(changed));
|
||||
assert.notEqual(restrictedSDKIdentity('"use strict";'), restrictedSDKIdentity('"use\\x20strict";'));
|
||||
assert.notEqual(restrictedSDKIdentity('const value="// comment";'), restrictedSDKIdentity('const value="";'));
|
||||
assert.throws(() => restrictedSDKIdentity("const ???"));
|
||||
assert.throws(() => restrictedSDKIdentity(" ".repeat(2 * 1024 * 1024 + 1)), /ANALYSIS_LIMIT/);
|
||||
});
|
||||
|
||||
test("restricted admission accepts the same SDK syntax and rejects changed SDK behavior", async () => {
|
||||
const source = "export const registry = {};";
|
||||
const sdk = 'export const value = "trusted";';
|
||||
const implementation: ArtifactImplementation = {
|
||||
id: "restricted",
|
||||
target: "server",
|
||||
compatibility: "quixos-restricted-module-v1",
|
||||
path: "module.mjs",
|
||||
ports: "quixos-transaction-ports-v1",
|
||||
registryExport: "registry",
|
||||
exports: ["value"],
|
||||
contentDigest: "a".repeat(64),
|
||||
files: [
|
||||
{ path: "module.mjs", digest: "b".repeat(64), bytes: source.length, mediaType: "text/javascript" },
|
||||
{ path: restrictedSDKPath, digest: "c".repeat(64), bytes: sdk.length, mediaType: "text/javascript" },
|
||||
],
|
||||
};
|
||||
const check = (candidateSDK: string) => {
|
||||
implementation.files[1]!.bytes = Buffer.byteLength(candidateSDK);
|
||||
return inspectRestrictedImplementation(
|
||||
"package:test@1",
|
||||
implementation,
|
||||
async (_revision, path) => Buffer.from(path === restrictedSDKPath ? candidateSDK : source),
|
||||
restrictedSDKIdentity(sdk),
|
||||
);
|
||||
};
|
||||
assert.deepEqual(await check("// a different build path\n" + sdk), []);
|
||||
assert.equal((await check('export const value = "untrusted";'))[0]?.code, "RESTRICTED_SDK_UNSUPPORTED");
|
||||
assert.equal((await check("export const ???"))[0]?.code, "RESTRICTED_SDK_UNSUPPORTED");
|
||||
});
|
||||
|
||||
test("compiled-source inspection finds authority imports and ambient facilities without executing code", () => {
|
||||
const surface = inspectJavaScriptSurface(
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user