diff --git a/check-receipt.mjs b/check-receipt.mjs new file mode 100644 index 0000000..5e2d602 --- /dev/null +++ b/check-receipt.mjs @@ -0,0 +1,17 @@ +import fs from "node:fs"; +import crypto from "node:crypto"; +const [schemaPath, packageRevisionId, bindingOutput, generatorPath, output] = process.argv.slice(2); +if (!schemaPath || !packageRevisionId || !bindingOutput || !generatorPath || !output) throw new Error("Missing candidate check receipt inputs"); +const canonical = (value) => Array.isArray(value) ? value.map(canonical) : value && typeof value === "object" + ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => [key, canonical(entry)])) : value; +const hash = (value) => `sha256:${crypto.createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`; +const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8")); +if (!schema.packages.some((entry) => entry.revisionId === packageRevisionId)) throw new Error("Checked binding schema lacks the package"); +const generated = fs.readFileSync(bindingOutput, "utf8"); +if (generated !== fs.readFileSync(".qx-checked-bindings", "utf8")) throw new Error("Build replaced candidate-generated bindings; its check is not evidence for this candidate"); +const receipt = { + schemaVersion: 1, packageRevisionId, success: true, bindingSchema: schema, + bindingSchemaDigest: hash(schema), generatedDigest: hash(generated), + checkerDigest: hash({ generatorPath, compiler: JSON.parse(fs.readFileSync("node_modules/typescript/package.json", "utf8")), lock: fs.readFileSync("yarn.lock", "utf8") }), +}; +fs.writeFileSync(output, `${JSON.stringify(receipt, null, 2)}\n`, { flag: "wx" }); diff --git a/quixos-package-helpers.nix b/quixos-package-helpers.nix index 6ff117a..bd4ac77 100644 --- a/quixos-package-helpers.nix +++ b/quixos-package-helpers.nix @@ -539,6 +539,9 @@ EOF # An exact, compiler-produced BindingSchema JSON artifact and a backend. # Other language helpers can consume the same schema with their own generator/runtime. bindings ? null, + # A dedicated entrypoint calling SDK serveMigration; never start the + # normal package server in the isolated migration execution boundary. + migrationEntrypoint ? null, nativeBuildInputs ? [ ], devShellPackages ? [ ], devShellHook ? "", @@ -661,6 +664,17 @@ EOF ${lib.optionalString (installConfig ? descriptorPath && descriptorPath != null) '' cp ${lib.escapeShellArg descriptorPath} "$out/${descriptorPath}" ''} + ${lib.optionalString (bindingConfig != null) '' + install -m 0444 quixos-check.json "$out/quixos-check.json" + ''} + ${lib.optionalString (migrationEntrypoint != null) '' + install -Dm444 migration.mjs "$out/libexec/${serverLibexecName}/migration.mjs" + cat > "$out/bin/migrate" <