@kungfu-tech/buildchain 3.0.2-alpha.4 → 3.0.2-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +1 -0
  2. package/actions/github-artifact-attestation/README.md +10 -0
  3. package/actions/promote-buildchain-ref/README.md +7 -0
  4. package/bin/buildchain.mjs +5 -0
  5. package/bin/internal/trust-release-cli.mjs +74 -3
  6. package/dist/site/artifact-schemas.json +5 -1
  7. package/dist/site/buildchain-contract.json +79 -28
  8. package/dist/site/buildchain-site.json +110 -27
  9. package/dist/site/capability-registry.json +8 -7
  10. package/dist/site/cli-registry.json +12 -0
  11. package/dist/site/controller-registry.json +40 -4
  12. package/dist/site/kfd-claims.json +205 -19
  13. package/dist/site/kfd-upstream-aggregate.json +1 -1
  14. package/dist/site/manual-registry.json +19 -5
  15. package/dist/site/node-api-registry.json +22 -9
  16. package/dist/site/page-registry.json +91 -17
  17. package/dist/site/public-surface-audit.json +132 -17
  18. package/dist/site/publication-authority-registry.json +27 -2
  19. package/dist/site/publication-registry.json +4 -4
  20. package/dist/site/release-model.json +2 -1
  21. package/dist/site/release-passport-check-manifest.json +1 -0
  22. package/dist/site/release-provenance.json +1 -0
  23. package/dist/site/schemas/release-passport-v1.schema.json +6 -0
  24. package/dist/site/site-manifest.json +17 -9
  25. package/dist/site/workflow-registry.json +91 -9
  26. package/docs/MAP.md +3 -1
  27. package/docs/binary-distribution.md +7 -0
  28. package/docs/cli.md +9 -0
  29. package/docs/dev-alpha-candidate-patrol.md +51 -13
  30. package/docs/github-artifact-attestation.md +219 -0
  31. package/docs/release-passport.md +13 -0
  32. package/docs/reusable-build-surface.md +6 -0
  33. package/package.json +2 -1
  34. package/packages/core/buildchain-contract.js +6 -0
  35. package/packages/core/buildchain-kfd-claims.js +5 -0
  36. package/packages/core/buildchain-publication-authority.js +1 -0
  37. package/packages/core/github-artifact-attestation.js +642 -0
  38. package/packages/core/index.js +19 -0
  39. package/packages/core/publication-authority.js +1 -1
  40. package/packages/core/release-passport-contract.js +2 -0
  41. package/packages/core/release-passport.js +60 -3
  42. package/scripts/check-inventory.mjs +4 -0
  43. package/scripts/create-github-artifact-attestation-policy.mjs +62 -0
  44. package/scripts/dev-alpha-candidate-patrol.mjs +471 -71
  45. package/scripts/generate-channel-promotion-workflow.mjs +6 -0
  46. package/scripts/generate-site-bundle.mjs +12 -0
  47. package/scripts/publish-github-artifact-attestation-evidence.mjs +201 -0
  48. package/scripts/release-candidate-resolver.mjs +12 -0
  49. package/scripts/stage-github-artifact-attestation-inputs.mjs +65 -0
@@ -21,6 +21,9 @@ import {
21
21
  import { createSurfaceTimestampPolicy } from "./surface-manifest.js";
22
22
  import { validatePublishEvidence as validateTransactionPublishEvidence } from "./publish-transaction.js";
23
23
  import { normalizeControllerReceiptReferences } from "./controller-evidence.js";
24
+ import {
25
+ normalizeGitHubArtifactAttestationPolicy,
26
+ } from "./github-artifact-attestation.js";
24
27
 
25
28
  export const RELEASE_PASSPORT_CONTRACT = "kungfu-buildchain-release-passport";
26
29
  export const ARTIFACT_EVIDENCE_CONTRACT = "kungfu-buildchain-artifact-evidence";
@@ -1166,6 +1169,8 @@ export function createReleasePassport({
1166
1169
  kfdAgentHubEvidencePath = "",
1167
1170
  controllerReceipts = [],
1168
1171
  controllerReceiptReferences = [],
1172
+ githubArtifactAttestations = [],
1173
+ checkedAt = "",
1169
1174
  } = {}) {
1170
1175
  const normalizedTag = nonEmptyString(tag, "tag");
1171
1176
  const artifactEvidence = createArtifactEvidence({ assets, repository, tag: normalizedTag, sourceSha, workflow });
@@ -1185,7 +1190,7 @@ export function createReleasePassport({
1185
1190
  ? normalizeEvidenceDocument(distTagPromotionEvidence, "distTagPromotionEvidence")
1186
1191
  : undefined;
1187
1192
  const normalizedImpact = normalizeImpactLedger(impact, { tag: normalizedTag, line });
1188
- const generatedAt = nowIso();
1193
+ const generatedAt = optionalString(checkedAt) || nowIso();
1189
1194
  const kfd1Metadata = resolveKfd1Metadata();
1190
1195
  const normalizedKfd1 = kfd1?.passportSection ? kfd1 : undefined;
1191
1196
  const normalizedKfd3 = kfd3?.passportSection ? kfd3 : undefined;
@@ -1231,6 +1236,8 @@ export function createReleasePassport({
1231
1236
  : [],
1232
1237
  requirePassed: true,
1233
1238
  });
1239
+ const normalizedGitHubArtifactAttestations = (githubArtifactAttestations || [])
1240
+ .map(normalizeGitHubArtifactAttestationPolicy);
1234
1241
  const publishArtifacts = normalizedPublishEvidence?.artifacts || [];
1235
1242
  const normalizedPublishSummary = normalizePublishSummary({
1236
1243
  packageSet: normalizedPackageSet,
@@ -1360,6 +1367,9 @@ export function createReleasePassport({
1360
1367
  ...(normalizedKfdAgentHub ? { kfdAgentHub: normalizedKfdAgentHub } : {}),
1361
1368
  ...(invariantPassports ? { invariantPassports } : {}),
1362
1369
  ...(normalizedControllerReceipts.length > 0 ? { controllerReceipts: normalizedControllerReceipts } : {}),
1370
+ ...(normalizedGitHubArtifactAttestations.length > 0
1371
+ ? { githubArtifactAttestations: normalizedGitHubArtifactAttestations }
1372
+ : {}),
1363
1373
  versionImpact: normalizedImpact.versionImpact,
1364
1374
  surfaceImpacts: normalizedImpact.surfaceImpacts,
1365
1375
  artifacts: [
@@ -1450,11 +1460,13 @@ export function collectGitHubReleasePassport({
1450
1460
  invariantPassportCommand = "",
1451
1461
  kfdAgentHubEvidenceJson = "",
1452
1462
  controllerReceiptReferences = [],
1463
+ githubArtifactAttestationPolicyJsons = [],
1453
1464
  basePassportJson = "",
1454
1465
  requireBaseKfd = false,
1455
1466
  releaseJsonExtra = "",
1456
1467
  publishJson = "",
1457
1468
  workflow = {},
1469
+ checkedAt = "",
1458
1470
  } = {}) {
1459
1471
  const release = parseJsonInput(releaseJson, {}, { cwd, label: "releaseJson" });
1460
1472
  const releaseExtra = parseJsonInput(releaseJsonExtra, {}, { cwd, label: "releaseJsonExtra" });
@@ -1526,6 +1538,13 @@ export function collectGitHubReleasePassport({
1526
1538
  undefined,
1527
1539
  { cwd, label: "kfdAgentHubEvidenceJson" },
1528
1540
  );
1541
+ const githubArtifactAttestationPolicies = (githubArtifactAttestationPolicyJsons || [])
1542
+ .filter(Boolean)
1543
+ .map((policyJson) => parseJsonInput(policyJson, undefined, {
1544
+ cwd,
1545
+ label: "githubArtifactAttestationPolicyJsons entry",
1546
+ }))
1547
+ .map(normalizeGitHubArtifactAttestationPolicy);
1529
1548
  const kfd3ArtifactWitnesses = [
1530
1549
  ...kfd3ArtifactWitnessMetas.map((meta) => meta.value),
1531
1550
  ...(kfd3ArtifactCommandMeta.value ? [kfd3ArtifactCommandMeta.value] : []),
@@ -1541,6 +1560,7 @@ export function collectGitHubReleasePassport({
1541
1560
  ];
1542
1561
  const resolvedTag = tag || release.tag_name || release.name || "";
1543
1562
  const resolvedOutputDir = path.resolve(cwd, outputDir);
1563
+ const resolvedCheckedAt = optionalString(checkedAt) || nowIso();
1544
1564
  const productMechanism = defaultProductMechanism({ repository, productName });
1545
1565
  const artifactEvidence = createArtifactEvidence({ assets, repository, tag: resolvedTag, sourceSha, workflow });
1546
1566
  const bundledPublishEvidencePath = publishEvidenceMeta.value ? "evidence.json" : "";
@@ -1568,7 +1588,7 @@ export function collectGitHubReleasePassport({
1568
1588
  matrixRoot: kfdSupportMatrixMeta.sha256 ? `sha256:${kfdSupportMatrixMeta.sha256}` : "",
1569
1589
  gateResults: kfdProductGateMetas.map((meta) => meta.value),
1570
1590
  expectedSourceSha: sourceSha,
1571
- checkedAt: nowIso(),
1591
+ checkedAt: resolvedCheckedAt,
1572
1592
  })
1573
1593
  : undefined;
1574
1594
  const passport = mergeAuthoritativePassportBase(createReleasePassport({
@@ -1623,9 +1643,11 @@ export function collectGitHubReleasePassport({
1623
1643
  : undefined,
1624
1644
  kfdAgentHubEvidencePath: kfdAgentHubEvidenceMeta.value ? "kfd-agent-hub-evidence.json" : "",
1625
1645
  controllerReceiptReferences,
1646
+ githubArtifactAttestations: githubArtifactAttestationPolicies,
1626
1647
  publishEvidencePath: publishEvidenceMeta.path ? path.relative(resolvedOutputDir, publishEvidenceMeta.path).split(path.sep).join("/") : "",
1627
1648
  transactionStatePath: transactionMeta.path ? path.relative(resolvedOutputDir, transactionMeta.path).split(path.sep).join("/") : "",
1628
1649
  workflow,
1650
+ checkedAt: resolvedCheckedAt,
1629
1651
  }), basePassportMeta.value, { requireKfd: requireBaseKfd });
1630
1652
  if (bundledPublishEvidencePath) {
1631
1653
  passport.evidence.publishEvidence = bundledPublishEvidencePath;
@@ -1639,7 +1661,7 @@ export function collectGitHubReleasePassport({
1639
1661
  productMechanism,
1640
1662
  kfdAgentHubEvidence: kfdAgentHubEvidenceMeta.value,
1641
1663
  kfdSupportEvidence: kfdSupport,
1642
- checkedAt: nowIso(),
1664
+ checkedAt: resolvedCheckedAt,
1643
1665
  });
1644
1666
  const files = {
1645
1667
  "product-mechanism.json": productMechanism,
@@ -1972,6 +1994,39 @@ export function createReleaseCheckReport({
1972
1994
  issues.push(issue("error", "kfdSupport.section", "KFD support evidence is present without a release-passport projection"));
1973
1995
  }
1974
1996
 
1997
+ for (const [index, value] of (passport?.githubArtifactAttestations || []).entries()) {
1998
+ try {
1999
+ const policy = normalizeGitHubArtifactAttestationPolicy(value);
2000
+ if (policy.caller.sourceSha !== String(passport?.release?.sourceSha || "").toLowerCase()) {
2001
+ issues.push(issue(
2002
+ "error",
2003
+ `githubArtifactAttestations[${index}].caller.sourceSha`,
2004
+ "attestation policy source SHA must match passport.release.sourceSha",
2005
+ ));
2006
+ }
2007
+ const artifact = (passport?.artifacts || []).find((entry) => entry.name === policy.subject.name);
2008
+ if (!artifact) {
2009
+ issues.push(issue(
2010
+ "error",
2011
+ `githubArtifactAttestations[${index}].subject.name`,
2012
+ `attestation subject ${policy.subject.name} is absent from the Release Passport artifacts`,
2013
+ ));
2014
+ } else if (artifact.sha256 !== policy.subject.digest.sha256) {
2015
+ issues.push(issue(
2016
+ "error",
2017
+ `githubArtifactAttestations[${index}].subject.digest`,
2018
+ `attestation subject ${policy.subject.name} digest differs from the Release Passport artifact`,
2019
+ ));
2020
+ }
2021
+ } catch (error) {
2022
+ issues.push(issue(
2023
+ "error",
2024
+ `githubArtifactAttestations[${index}]`,
2025
+ error.message,
2026
+ ));
2027
+ }
2028
+ }
2029
+
1975
2030
  const tag = passport?.release?.tag || "";
1976
2031
  if (!tag) {
1977
2032
  issues.push(issue("error", "release.tag", "release.tag is required"));
@@ -2422,6 +2477,7 @@ export async function verifyReleasePassport({
2422
2477
  productMechanismLocation = "",
2423
2478
  kfdAgentHubEvidenceLocation = "",
2424
2479
  kfdSupportEvidenceLocation = "",
2480
+ checkedAt = nowIso(),
2425
2481
  } = {}) {
2426
2482
  const passport = await readJsonFromLocation(passportLocation);
2427
2483
  const basePath = /^https?:\/\//.test(passportLocation) ? passportLocation : path.resolve(passportLocation);
@@ -2462,6 +2518,7 @@ export async function verifyReleasePassport({
2462
2518
  productMechanism,
2463
2519
  kfdAgentHubEvidence,
2464
2520
  kfdSupportEvidence,
2521
+ checkedAt,
2465
2522
  });
2466
2523
  }
2467
2524
 
@@ -68,6 +68,7 @@ const requiredPaths = [
68
68
  "docs/product-mechanism.md",
69
69
  "docs/readme-badges.md",
70
70
  "docs/release-passport.md",
71
+ "docs/github-artifact-attestation.md",
71
72
  "docs/shifu-gate-profiles.md",
72
73
  "docs/auditable-demo.md",
73
74
  "docs/release-propagation.md",
@@ -358,6 +359,9 @@ if (rootPackage.exports?.["./kfd-gate"] !== "./packages/core/kfd-gate.js") {
358
359
  if (rootPackage.exports?.["./release-passport"] !== "./packages/core/release-passport.js") {
359
360
  throw new Error("root package must export @kungfu-tech/buildchain/release-passport");
360
361
  }
362
+ if (rootPackage.exports?.["./github-artifact-attestation"] !== "./packages/core/github-artifact-attestation.js") {
363
+ throw new Error("root package must export @kungfu-tech/buildchain/github-artifact-attestation");
364
+ }
361
365
  if (rootPackage.exports?.["./release-passport-contract"] !== "./packages/core/release-passport-contract.js") {
362
366
  throw new Error("root package must export @kungfu-tech/buildchain/release-passport-contract");
363
367
  }
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+
7
+ import {
8
+ GITHUB_ARTIFACT_ATTESTATION_WORKFLOW,
9
+ createGitHubArtifactAttestationPolicy,
10
+ githubArtifactAttestationSha256File,
11
+ } from "../packages/core/github-artifact-attestation.js";
12
+
13
+ function env(name, fallback = "") {
14
+ return String(process.env[name] || fallback).trim();
15
+ }
16
+
17
+ function required(name) {
18
+ const value = env(name);
19
+ if (!value) throw new Error(`${name} is required`);
20
+ return value;
21
+ }
22
+
23
+ const cwd = path.resolve(env("BUILDCHAIN_SOURCE_CWD", "."));
24
+ const subjectRelativePath = required("BUILDCHAIN_GITHUB_ATTESTATION_SUBJECT_PATH").replace(/\\/g, "/");
25
+ const subjectPath = path.resolve(cwd, subjectRelativePath);
26
+ const manifestPath = path.resolve(required("BUILDCHAIN_GITHUB_ATTESTATION_PLATFORM_MANIFEST"));
27
+ const outputPath = path.resolve(required("BUILDCHAIN_GITHUB_ATTESTATION_POLICY_OUTPUT"));
28
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
29
+ const manifestEntry = (manifest.files || []).find((entry) => (
30
+ String(entry.path || "").replace(/\\/g, "/") === subjectRelativePath
31
+ ));
32
+ if (!manifestEntry) throw new Error(`platform manifest does not contain ${subjectRelativePath}`);
33
+ const digest = githubArtifactAttestationSha256File(subjectPath);
34
+ const size = fs.statSync(subjectPath).size;
35
+ if (`sha256:${String(manifestEntry.sha256 || "").toLowerCase()}` !== digest || Number(manifestEntry.size) !== size) {
36
+ throw new Error("subject bytes do not match the final platform manifest");
37
+ }
38
+ const manifestDigest = githubArtifactAttestationSha256File(manifestPath);
39
+ const runtimeSha = required("BUILDCHAIN_RUNTIME_SHA").toLowerCase();
40
+ const signerSha = required("BUILDCHAIN_GITHUB_ATTESTATION_SIGNER_SHA").toLowerCase();
41
+ const policy = createGitHubArtifactAttestationPolicy({
42
+ subject: { name: path.basename(subjectPath), path: subjectRelativePath, size, digest },
43
+ caller: {
44
+ repository: required("BUILDCHAIN_SOURCE_REPOSITORY"),
45
+ sourceSha: required("BUILDCHAIN_SOURCE_SHA").toLowerCase(),
46
+ sourceTreeSha: required("BUILDCHAIN_SOURCE_TREE_SHA").toLowerCase(),
47
+ },
48
+ signer: {
49
+ repository: "kungfu-systems/buildchain",
50
+ workflowPath: GITHUB_ARTIFACT_ATTESTATION_WORKFLOW,
51
+ workflowDigest: signerSha,
52
+ },
53
+ build: {
54
+ platform: required("BUILDCHAIN_PLATFORM_ID"),
55
+ platformManifestDigest: manifestDigest,
56
+ runnerReceiptRoot: manifestDigest,
57
+ buildchainRuntimeSha: runtimeSha,
58
+ },
59
+ });
60
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
61
+ fs.writeFileSync(outputPath, `${JSON.stringify(policy, null, 2)}\n`);
62
+ process.stdout.write(`github-artifact-attestation-policy=${outputPath}\n`);