@kungfu-tech/buildchain 4.1.0-alpha.0 → 4.1.0-alpha.2

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 (295) hide show
  1. package/AGENTS.md +30 -41
  2. package/CONTRIBUTING.md +6 -7
  3. package/architecture/action-taxonomy.json +5 -21
  4. package/architecture/agent-change-map.md +74 -56
  5. package/architecture/build-orchestration.json +8 -1
  6. package/architecture/ci-lane-change-budget.json +179 -69
  7. package/architecture/floating-consumer-policy.json +7 -17
  8. package/architecture/internal-capabilities.json +58 -72
  9. package/architecture/maintainability-debt.json +138 -52
  10. package/architecture/maintainability-policy.json +6 -15
  11. package/architecture/promotion-job-topology.json +39 -20
  12. package/architecture/release-topology.json +26 -37
  13. package/architecture/runtime-entry.json +26 -0
  14. package/architecture/runtime-resume-lineage.json +23 -0
  15. package/architecture/stage-capsule-qualification.json +2 -2
  16. package/architecture/universal-workflow-bootstrap.json +10 -25
  17. package/architecture/universal-workflow-capability-policy.json +61 -0
  18. package/architecture/universal-workflow-fault-campaign.json +18 -24
  19. package/architecture/workflow-taxonomy.json +0 -11
  20. package/contracts/fixtures/{v4-runtime-ref-resume-authority-v1 → runtime-resume-lineage-v2}/scenario.json +2 -3
  21. package/contracts/fixtures/v4-floating-consumer-policy-v1/cases.json +2 -2
  22. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  23. package/contracts/promotion-invocation-v1.schema.json +0 -44
  24. package/contracts/promotion-request-v1.schema.json +0 -39
  25. package/contracts/runtime-resume-lineage-v2.schema.json +182 -0
  26. package/dist/site/buildchain-contract.json +472 -361
  27. package/dist/site/buildchain-site.json +99 -99
  28. package/dist/site/capability-registry.json +3 -3
  29. package/dist/site/cli-registry.json +4 -4
  30. package/dist/site/controller-registry.json +94 -78
  31. package/dist/site/kfd-claims.json +224 -343
  32. package/dist/site/kfd-upstream-aggregate.json +1 -1
  33. package/dist/site/manual-registry.json +15 -15
  34. package/dist/site/node-api-registry.json +239 -354
  35. package/dist/site/page-registry.json +80 -80
  36. package/dist/site/public-surface-audit.json +203 -254
  37. package/dist/site/publication-authority-registry.json +1 -24
  38. package/dist/site/publication-registry.json +4 -4
  39. package/dist/site/release-provenance.json +1 -1
  40. package/dist/site/site-manifest.json +19 -19
  41. package/dist/site/workflow-registry.json +197 -357
  42. package/docs/MAP.md +3 -3
  43. package/docs/auditable-demo.md +4 -4
  44. package/docs/cli-reference.md +2 -2
  45. package/docs/code-organization.md +5 -4
  46. package/docs/controller-evidence.md +2 -2
  47. package/docs/dev-alpha-candidate-patrol.md +5 -5
  48. package/docs/dev-delivery-warrant.md +14 -24
  49. package/docs/engineering-housekeeper.md +3 -3
  50. package/docs/github-artifact-attestation.md +9 -12
  51. package/docs/github-governance-authority.md +4 -4
  52. package/docs/node-api-reference.md +153 -167
  53. package/docs/publication-artifacts.md +3 -4
  54. package/docs/publication-authority.md +2 -2
  55. package/docs/release-candidate.md +21 -27
  56. package/docs/release-governance.md +10 -10
  57. package/docs/release-promotion-request.md +11 -18
  58. package/docs/release-propagation.md +5 -6
  59. package/docs/release-tail-provider-plane.md +2 -3
  60. package/docs/reusable-build-surface.md +13 -7
  61. package/docs/runtime-entry.md +122 -0
  62. package/docs/runtime-train-validation.md +30 -41
  63. package/docs/v4-publication-rehearsal.md +5 -5
  64. package/docs/v4-stage-capsule.md +9 -10
  65. package/docs/web-surface-deployments.md +16 -20
  66. package/docs/workflow-catalog.md +0 -1
  67. package/package.json +6 -6
  68. package/packages/core/adoption/commands/init-repo.mjs +2 -2
  69. package/packages/core/adoption/cross-platform-adopter-qualification.js +2 -13
  70. package/packages/core/adoption/qualification/actions.js +3 -5
  71. package/packages/core/adoption/qualification/admission.js +2 -3
  72. package/packages/core/build/artifact/attestation.js +0 -1
  73. package/packages/core/build/artifact/contracts.js +1 -4
  74. package/packages/core/build/artifact/store.js +6 -5
  75. package/packages/core/build/artifact-signing.js +4 -10
  76. package/packages/core/build/binary/actions.js +15 -9
  77. package/packages/core/build/binary-publication-dispatch.js +2 -6
  78. package/packages/core/build/demo/action-context.js +2 -11
  79. package/packages/core/build/demo/binary-action.js +8 -10
  80. package/packages/core/build/gate/controller.js +0 -2
  81. package/packages/core/build/github-attestation/action.js +2 -7
  82. package/packages/core/build/github-attestation/admission.js +0 -8
  83. package/packages/core/build/github-attestation/transaction.js +1 -2
  84. package/packages/core/build/lifecycle/stage-action.js +2 -0
  85. package/packages/core/build/lifecycle/stage.js +6 -3
  86. package/packages/core/build/macos-credential-island/dmg-assembly.js +0 -2
  87. package/packages/core/build/macos-credential-island/lib.js +2 -6
  88. package/packages/core/build/plan/action-context.js +0 -7
  89. package/packages/core/build/plan/action.js +7 -11
  90. package/packages/core/build/plan/admission.js +6 -73
  91. package/packages/core/build/plan/configuration.js +1 -1
  92. package/packages/core/build/plan/environment.js +1 -1
  93. package/packages/core/build/plan/identity.js +1 -5
  94. package/packages/core/build/plan/resolve.js +7 -1
  95. package/packages/core/build/recovery/checkpoint.js +158 -0
  96. package/packages/core/build/recovery/transaction.js +51 -0
  97. package/packages/core/build/signing/authority-actions.js +2 -11
  98. package/packages/core/build/signing/control.js +0 -5
  99. package/packages/core/build/signing/controller-transaction.js +0 -1
  100. package/packages/core/build/signing/delegation.js +2 -4
  101. package/packages/core/build/signing/dispatch.js +10 -56
  102. package/packages/core/build/signing/intake.js +0 -4
  103. package/packages/core/build/signing/prepare.js +1 -1
  104. package/packages/core/build/signing/request.js +3 -8
  105. package/packages/core/build/source/checkout-action.js +6 -10
  106. package/packages/core/build/source/controller.js +1 -4
  107. package/packages/core/build/source/lifecycle.js +3 -13
  108. package/packages/core/build/stage-capsule/actions.js +3 -13
  109. package/packages/core/build/stage-capsule/campaign/resume.js +10 -5
  110. package/packages/core/build/stage-capsule/rehearsal/verify.js +3 -4
  111. package/packages/core/build/stage-capsule-resume-planner.js +0 -1
  112. package/packages/core/build/summary/aggregation.js +5 -5
  113. package/packages/core/build/summary/finalization.js +1 -1
  114. package/packages/core/build/verification/actions.js +3 -12
  115. package/packages/core/build/verification/runner.js +22 -20
  116. package/packages/core/consumer/floating-consumer-evidence.js +3 -21
  117. package/packages/core/consumer/floating-consumer-policy.js +2 -93
  118. package/packages/core/consumer/floating-consumer-release-passport.js +1 -34
  119. package/packages/core/consumer/policy-admission.js +1 -3
  120. package/packages/core/contracts/buildchain-contract.js +12 -146
  121. package/packages/core/contracts/workflow-yaml-contract.js +11 -1
  122. package/packages/core/dev-delivery/candidate/admission.js +3 -4
  123. package/packages/core/dev-delivery/candidate/coordinates.js +0 -8
  124. package/packages/core/dev-delivery/commands/dev-delivery-request.sh +5 -13
  125. package/packages/core/dev-delivery/commands/dev-delivery-source-proof-reuse.mjs +0 -3
  126. package/packages/core/dev-delivery/dev-delivery-contract-surface.js +4 -52
  127. package/packages/core/dev-delivery/dev-delivery-execution-transfer.js +2 -15
  128. package/packages/core/dev-delivery/native/action-context.js +4 -32
  129. package/packages/core/dev-delivery/native/provider-boundary.js +0 -2
  130. package/packages/core/dev-delivery/native/transactions.js +1 -2
  131. package/packages/core/dev-delivery/queue/reconciliation-action.js +6 -10
  132. package/packages/core/dev-delivery/source-proof/predicates.js +0 -6
  133. package/packages/core/dev-delivery/source-proof/reuse.js +0 -2
  134. package/packages/core/dev-delivery/source-proof/seal.js +0 -2
  135. package/packages/core/governance/alpha-candidate/action.js +1 -10
  136. package/packages/core/governance/audit/actions.js +1 -3
  137. package/packages/core/governance/audit/collection.js +1 -3
  138. package/packages/core/governance/audit/identity.js +3 -31
  139. package/packages/core/governance/audit/transactions.js +2 -4
  140. package/packages/core/governance/buildchain-publication-authority.js +0 -1
  141. package/packages/core/governance/commands/audit-github-governance.mjs +3 -2
  142. package/packages/core/governance/github-governance-authority.js +0 -5
  143. package/packages/core/governance/receipt-admission.js +0 -15
  144. package/packages/core/index.js +1 -1
  145. package/packages/core/observability/controller/identity.js +4 -5
  146. package/packages/core/observability/controller-evidence.js +5 -10
  147. package/packages/core/paper/commands/paper.mjs +6 -6
  148. package/packages/core/paper/operations/plans.js +2 -2
  149. package/packages/core/paper/operations/provisioning-validation.js +1 -1
  150. package/packages/core/paper/operations/scaffold-workflows.js +29 -12
  151. package/packages/core/paper/operations/scaffold.js +97 -69
  152. package/packages/core/paper/paper-agent-entry.js +0 -11
  153. package/packages/core/paper/paper-runtime-channels.js +21 -66
  154. package/packages/core/paper/paper-scaffold-content.js +1 -1
  155. package/packages/core/paper/paper.js +9 -5
  156. package/packages/core/paper/publication/candidate.js +0 -9
  157. package/packages/core/paper/publication-controller.js +2 -4
  158. package/packages/core/providers/github/artifact-attestation.js +1 -1
  159. package/packages/core/publication/authority/actions.js +9 -8
  160. package/packages/core/publication/authority/admission.js +0 -11
  161. package/packages/core/publication/authority/assembly.js +2 -7
  162. package/packages/core/publication/authority/control-plane.js +2 -2
  163. package/packages/core/publication/authority/governance.js +2 -9
  164. package/packages/core/publication/authority/request.js +7 -2
  165. package/packages/core/publication/authority/verification.js +3 -18
  166. package/packages/core/publication/binary/action.js +5 -7
  167. package/packages/core/publication/candidate/qualification-action.js +1 -6
  168. package/packages/core/publication/candidate/stage-capsules.js +1 -1
  169. package/packages/core/publication/npm/preview-action.js +9 -15
  170. package/packages/core/publication/oci/preview-actions.js +1 -10
  171. package/packages/core/publication/publication-artifact-candidate.js +2 -2
  172. package/packages/core/publication/publication-authority.js +2 -2
  173. package/packages/core/publication/publication-rehearsal-projection.js +3 -3
  174. package/packages/core/publication/settlement/actions.js +2 -11
  175. package/packages/core/release/binary/coordinates-action.js +0 -13
  176. package/packages/core/release/binary/coordinates.js +2 -19
  177. package/packages/core/release/commands/promotion-channel-router.mjs +12 -56
  178. package/packages/core/release/commands/resume-from-candidate-run.mjs +2 -3
  179. package/packages/core/release/dev-alpha-active-release-train.js +0 -5
  180. package/packages/core/release/line/action.js +2 -13
  181. package/packages/core/release/next-development/actions.js +2 -5
  182. package/packages/core/release/passport/consumer-validation.js +1 -28
  183. package/packages/core/release/promote-candidate/development-source.js +7 -8
  184. package/packages/core/release/promotion/actions.js +7 -38
  185. package/packages/core/release/promotion/admission.js +3 -50
  186. package/packages/core/release/promotion/candidate.js +2 -4
  187. package/packages/core/release/promotion/channel.js +13 -101
  188. package/packages/core/release/promotion/qualification-action.js +2 -13
  189. package/packages/core/release/promotion/routing.js +16 -81
  190. package/packages/core/release/promotion/selection.js +1 -9
  191. package/packages/core/release/promotion-request.js +1 -40
  192. package/packages/core/release/qualification/actions.js +0 -8
  193. package/packages/core/release/qualification/public-build.js +1 -1
  194. package/packages/core/release/recovery/candidate.js +15 -21
  195. package/packages/core/release/recovery/capsules.js +2 -10
  196. package/packages/core/release/recovery/lineage.js +305 -0
  197. package/packages/core/release/recovery/provenance.js +1 -2
  198. package/packages/core/release/recovery/publication.js +4 -14
  199. package/packages/core/release/recovery/readback.js +19 -32
  200. package/packages/core/release/recovery/runtime.js +4 -95
  201. package/packages/core/release/release-candidate-recovery.js +4 -9
  202. package/packages/core/release/release-candidate.js +2 -5
  203. package/packages/core/release/reseal/actions.js +3 -22
  204. package/packages/core/release/reseal/admission.js +0 -2
  205. package/packages/core/release/reseal/finalize-platform.js +0 -2
  206. package/packages/core/release/tail-reseal-github.js +1 -2
  207. package/packages/core/release/tail-reseal-receipt.js +0 -1
  208. package/packages/core/release/tail-reseal.js +2 -20
  209. package/packages/core/release/version-state.js +16 -13
  210. package/packages/core/runtime/buildchain-domain.wasm +0 -0
  211. package/packages/core/runtime/checkout-identity.js +0 -13
  212. package/packages/core/runtime/domain-wasm-artifact.js +2 -2
  213. package/packages/core/runtime/entry/actions.js +121 -0
  214. package/packages/core/runtime/entry/activation.js +86 -0
  215. package/packages/core/runtime/entry/github.js +55 -0
  216. package/packages/core/runtime/entry/recovery.js +37 -0
  217. package/packages/core/runtime/entry/selection.js +110 -0
  218. package/packages/core/web/controller.js +1 -1
  219. package/packages/core/web/publication-admission-action.js +1 -8
  220. package/packages/core/web/web-surface-publication-candidate.js +2 -5
  221. package/packages/core/workflow/admission/actions.js +6 -26
  222. package/packages/core/workflow/admission/recovery.js +3 -43
  223. package/packages/core/workflow/admission/transactions.js +8 -24
  224. package/packages/core/workflow/commands/buildchain-cli-help.mjs +1 -1
  225. package/packages/core/workflow/commands/universal-workflow-engine.mjs +2 -7
  226. package/packages/core/workflow/dogfood/actions.js +0 -17
  227. package/packages/core/workflow/engine/action.js +0 -5
  228. package/packages/core/workflow/engine/conformance.js +1 -1
  229. package/packages/core/workflow/engine/execution.js +2 -6
  230. package/packages/core/workflow/engine/identity.js +1 -6
  231. package/packages/core/workflow/engine/release-environment.js +11 -35
  232. package/packages/core/workflow/engine/release-promotion.js +2 -2
  233. package/packages/core/workflow/settlement/actions.js +0 -12
  234. package/packages/core/workflow/universal-self-dogfood.js +2 -16
  235. package/packages/core/workflow/universal-workflow-bootstrap.js +16 -241
  236. package/packages/core/workflow/workflow-taxonomy.mjs +2 -2
  237. package/scripts/check-dev-delivery-actions.mjs +17 -8
  238. package/scripts/check-floating-consumer-policy-contract.mjs +6 -6
  239. package/scripts/check-inventory.mjs +1 -10
  240. package/scripts/check-public-dogfood-contract.mjs +34 -21
  241. package/scripts/check-release-topology.mjs +2 -6
  242. package/scripts/check-runtime-entry.mjs +133 -0
  243. package/scripts/check-universal-workflow-bootstrap.mjs +45 -281
  244. package/scripts/check-workflows.sh +0 -7
  245. package/scripts/generate-bootstrap-admission.mjs +1 -1
  246. package/scripts/generate-channel-promotion-workflow.mjs +4 -4
  247. package/scripts/inventory/binary.mjs +4 -4
  248. package/scripts/inventory/promotion.mjs +5 -3
  249. package/scripts/runtime-source-boundaries.mjs +70 -0
  250. package/scripts/site-capability-metadata.mjs +2 -2
  251. package/scripts/workflow-action-graph.mjs +5 -18
  252. package/scripts/workflow-self-reference.mjs +20 -0
  253. package/templates/native-dev-delivery.yml +4 -4
  254. package/templates/universal-buildchain-bootstrap.yml +12 -2
  255. package/architecture/runtime-ref-resume-authority.json +0 -64
  256. package/architecture/universal-workflow-train-admission.json +0 -76
  257. package/contracts/v4-runtime-ref-resume-authority-v1.schema.json +0 -235
  258. package/docs/v4-runtime-ref-resume-authority.md +0 -69
  259. package/packages/core/consumer/channel-selection.js +0 -211
  260. package/packages/core/consumer/commands/buildchain-channel-router.mjs +0 -81
  261. package/packages/core/consumer/invocation-selector.js +0 -4
  262. package/packages/core/consumer/runtime-ref-resume-authority.js +0 -621
  263. package/packages/core/consumer/workflow-definition-authority.js +0 -64
  264. package/packages/core/contracts/runtime-contract-action.js +0 -48
  265. package/packages/core/release/commands/promotion-identity-resolver.mjs +0 -77
  266. package/packages/core/release/commands/verify-promotion-router-binding.sh +0 -36
  267. package/packages/core/release/promotion/identities.js +0 -56
  268. package/packages/core/release/runtime-override-authorization.js +0 -242
  269. package/packages/core/runtime/commands/runtime-ref-core.mjs +0 -171
  270. package/packages/core/runtime/provider-closure.js +0 -60
  271. package/packages/core/runtime/runtime-selection-action.js +0 -34
  272. package/packages/core/runtime/runtime-selection.js +0 -136
  273. package/packages/core/runtime/workflow-runtime.js +0 -11
  274. package/packages/core/workflow/admission/review.js +0 -188
  275. package/packages/core/workflow/commands/universal-workflow-backflow.mjs +0 -26
  276. package/packages/core/workflow/installation.js +0 -33
  277. package/packages/core/workflow/settlement/backflow.js +0 -187
  278. package/scripts/generate-bootstrap-recovery.mjs +0 -94
  279. package/templates/bootstrap-recovery/actions/build/source/admit/action.yml +0 -17
  280. package/templates/bootstrap-recovery/actions/build/source/admit/dist/index.js +0 -63
  281. package/templates/bootstrap-recovery/actions/runtime/dependencies/install/action.yml +0 -18
  282. package/templates/bootstrap-recovery/actions/runtime/dependencies/install/dist/index.js +0 -63
  283. package/templates/bootstrap-recovery/actions/runtime/environment/prepare/action.yml +0 -21
  284. package/templates/bootstrap-recovery/actions/workflow/admission/inspect/action.yml +0 -27
  285. package/templates/bootstrap-recovery/actions/workflow/admission/inspect/dist/index.js +0 -64
  286. package/templates/bootstrap-recovery/actions/workflow/admission/review/action.yml +0 -15
  287. package/templates/bootstrap-recovery/actions/workflow/admission/review/dist/index.js +0 -65
  288. package/templates/bootstrap-recovery/actions/workflow/recovery/admit/action.yml +0 -47
  289. package/templates/bootstrap-recovery/actions/workflow/recovery/execute/action.yml +0 -48
  290. package/templates/bootstrap-recovery/actions/workflow/recovery/settle/action.yml +0 -54
  291. package/templates/bootstrap-recovery/actions/workflow/settlement/seal/action.yml +0 -17
  292. package/templates/bootstrap-recovery/actions/workflow/settlement/seal/dist/index.js +0 -65
  293. package/templates/bootstrap-recovery/manifest.json +0 -68
  294. package/templates/bootstrap-recovery/package.json +0 -9
  295. package/templates/universal-buildchain-bootstrap-recovery.yml +0 -115
@@ -1,621 +0,0 @@
1
- import crypto from "node:crypto";
2
- import {
3
- RUNTIME_PERSISTENCE_SCAN_CONTRACT,
4
- scanRuntimeSelectorPersistence,
5
- } from "./runtime-selector-persistence.js";
6
-
7
- export { RUNTIME_PERSISTENCE_SCAN_CONTRACT, scanRuntimeSelectorPersistence };
8
-
9
- export const RUNTIME_AUTHORIZATION_CONTRACT =
10
- "kungfu-buildchain-v4-runtime-authorization/v1";
11
- export const RUNTIME_RESUME_LINEAGE_CONTRACT =
12
- "kungfu-buildchain-v4-runtime-resume-lineage/v1";
13
- const EXACT_SHA = /^[0-9a-f]{40}$/u;
14
- const SHA256_ROOT = /^sha256:[0-9a-f]{64}$/u;
15
- const OFFICIAL_DOMAIN_REF = /^v4(?:-alpha)?$/u;
16
- const TRAIN_DOMAIN_REF = /^train\/v4\/v4\.\d+\/[A-Za-z0-9._/-]+$/u;
17
- const AUTHORITY_DOMAIN_REF = /^authority\/v4\/v4\.\d+\/[A-Za-z0-9._/-]+$/u;
18
- const TOKEN = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/u;
19
-
20
- function stableJson(value) {
21
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
22
- if (value && typeof value === "object") {
23
- return `{${Object.keys(value)
24
- .sort()
25
- .map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
26
- .join(",")}}`;
27
- }
28
- return JSON.stringify(value);
29
- }
30
-
31
- export function runtimeResumeDocumentRoot(value) {
32
- return `sha256:${crypto.createHash("sha256").update(stableJson(value)).digest("hex")}`;
33
- }
34
-
35
- function exactSha(value, label) {
36
- const normalized = String(value || "")
37
- .trim()
38
- .toLowerCase();
39
- if (!EXACT_SHA.test(normalized)) {
40
- throw new Error(`${label} must be an exact 40-character Git SHA`);
41
- }
42
- return normalized;
43
- }
44
-
45
- function root(value, label) {
46
- const normalized = String(value || "")
47
- .trim()
48
- .toLowerCase();
49
- if (!SHA256_ROOT.test(normalized)) {
50
- throw new Error(`${label} must be a sha256 content root`);
51
- }
52
- return normalized;
53
- }
54
-
55
- function nonEmpty(value, label) {
56
- const normalized = String(value || "").trim();
57
- if (!normalized) throw new Error(`${label} must be non-empty`);
58
- return normalized;
59
- }
60
-
61
- function token(value, label) {
62
- const normalized = nonEmpty(value, label);
63
- if (!TOKEN.test(normalized)) {
64
- throw new Error(`${label} must be an ASCII identity token`);
65
- }
66
- return normalized;
67
- }
68
-
69
- function exactKeys(value, expected, label) {
70
- if (!value || typeof value !== "object" || Array.isArray(value)) {
71
- throw new Error(`${label} must be an object`);
72
- }
73
- const actual = Object.keys(value).sort();
74
- const canonical = [...expected].sort();
75
- if (
76
- actual.length !== canonical.length ||
77
- actual.some((key, index) => key !== canonical[index])
78
- ) {
79
- throw new Error(`${label} has an invalid field set`);
80
- }
81
- }
82
-
83
- function sortedUnique(values, label) {
84
- if (!Array.isArray(values) || values.length === 0) {
85
- throw new Error(`${label} must be a non-empty array`);
86
- }
87
- const normalized = values.map((value, index) =>
88
- token(value, `${label}[${index}]`),
89
- );
90
- const expected = [...new Set(normalized)].sort((left, right) =>
91
- Buffer.from(left).compare(Buffer.from(right)),
92
- );
93
- if (
94
- normalized.length !== expected.length ||
95
- normalized.some((value, index) => value !== expected[index])
96
- ) {
97
- throw new Error(`${label} must be byte-sorted and duplicate-free`);
98
- }
99
- return normalized;
100
- }
101
-
102
- function normalizeRef(value) {
103
- return String(value || "")
104
- .trim()
105
- .replace(/^refs\/(?:heads|tags)\//u, "");
106
- }
107
-
108
- function approvedRef(value) {
109
- const ref = normalizeRef(value);
110
- return (
111
- OFFICIAL_DOMAIN_REF.test(ref) ||
112
- TRAIN_DOMAIN_REF.test(ref) ||
113
- AUTHORITY_DOMAIN_REF.test(ref)
114
- );
115
- }
116
-
117
- function runtimeClass(value) {
118
- const ref = normalizeRef(value);
119
- if (EXACT_SHA.test(ref.toLowerCase())) return "exact-sha";
120
- if (OFFICIAL_DOMAIN_REF.test(ref)) return "floating";
121
- if (TRAIN_DOMAIN_REF.test(ref)) return "train";
122
- if (AUTHORITY_DOMAIN_REF.test(ref)) return "authority";
123
- return "rejected";
124
- }
125
-
126
- function normalizeReadbacks(values, runtimeSha) {
127
- if (!Array.isArray(values) || values.length === 0) {
128
- throw new Error(
129
- "approvedRefReadbacks must contain provider readback evidence",
130
- );
131
- }
132
- const normalized = values.map((value, index) => {
133
- exactKeys(
134
- value,
135
- ["ref", "sha", "containsRuntimeSha", "readbackRoot"],
136
- `approvedRefReadbacks[${index}]`,
137
- );
138
- const ref = normalizeRef(value.ref);
139
- if (!approvedRef(ref)) {
140
- throw new Error(
141
- `approvedRefReadbacks[${index}].ref is not an approved v4 ref`,
142
- );
143
- }
144
- return {
145
- ref,
146
- sha: exactSha(value.sha, `approvedRefReadbacks[${index}].sha`),
147
- containsRuntimeSha: value.containsRuntimeSha === true,
148
- readbackRoot: root(
149
- value.readbackRoot,
150
- `approvedRefReadbacks[${index}].readbackRoot`,
151
- ),
152
- };
153
- });
154
- normalized.sort((left, right) =>
155
- Buffer.from(left.ref).compare(Buffer.from(right.ref)),
156
- );
157
- if (
158
- new Set(normalized.map((entry) => entry.ref)).size !== normalized.length
159
- ) {
160
- throw new Error("approvedRefReadbacks refs must be unique");
161
- }
162
- if (!normalized.some((entry) => entry.containsRuntimeSha)) {
163
- throw new Error(
164
- `runtime ${runtimeSha} is not reachable from an approved v4 ref`,
165
- );
166
- }
167
- return normalized;
168
- }
169
-
170
- export function authorizeRuntimeSelection({
171
- repository,
172
- eventName,
173
- mode = "dispatch",
174
- actor,
175
- actorPermission,
176
- reason,
177
- authorizedAt,
178
- sourceSha,
179
- sourceTreeSha,
180
- requestedRef,
181
- resolvedRuntimeSha,
182
- approvedRefReadbacks,
183
- stableContractLockRoot,
184
- alphaContractLockRoot,
185
- consumerPolicyReceiptRoot,
186
- persistenceScan,
187
- } = {}) {
188
- const runtimeSha = exactSha(resolvedRuntimeSha, "resolvedRuntimeSha");
189
- const ref = normalizeRef(requestedRef);
190
- const refClass = runtimeClass(ref);
191
- if (refClass === "rejected")
192
- throw new Error(
193
- "requestedRef is outside the v4 runtime authority boundary",
194
- );
195
- if (!new Set(["dispatch", "resume", "rescue"]).has(mode)) {
196
- throw new Error("mode must be dispatch, resume, or rescue");
197
- }
198
- if (eventName !== "workflow_dispatch") {
199
- throw new Error(
200
- "v4 runtime escape is only allowed for trusted workflow_dispatch runs",
201
- );
202
- }
203
- if (!new Set(["write", "maintain", "admin"]).has(actorPermission)) {
204
- throw new Error(
205
- "v4 runtime escape requires write, maintain, or admin permission",
206
- );
207
- }
208
- if (
209
- !persistenceScan ||
210
- persistenceScan.contract !== RUNTIME_PERSISTENCE_SCAN_CONTRACT
211
- ) {
212
- throw new Error("a v4 runtime persistence scan is required");
213
- }
214
- if (persistenceScan.status !== "passed" || persistenceScan.failures?.length) {
215
- throw new Error(
216
- "runtime selector persistence scan must pass before authorization",
217
- );
218
- }
219
- const scanPayload = { ...persistenceScan };
220
- delete scanPayload.root;
221
- if (persistenceScan.root !== runtimeResumeDocumentRoot(scanPayload)) {
222
- throw new Error("runtime persistence scan root mismatch");
223
- }
224
- const readbacks = normalizeReadbacks(approvedRefReadbacks, runtimeSha);
225
- if (refClass !== "exact-sha") {
226
- const direct = readbacks.find((entry) => entry.ref === ref);
227
- if (!direct || direct.sha !== runtimeSha) {
228
- throw new Error(
229
- "named runtime ref readback does not match resolvedRuntimeSha",
230
- );
231
- }
232
- }
233
- const timestamp = nonEmpty(authorizedAt, "authorizedAt");
234
- if (Number.isNaN(Date.parse(timestamp)))
235
- throw new Error("authorizedAt must be an ISO-8601 timestamp");
236
- const receipt = {
237
- schemaVersion: 1,
238
- contract: RUNTIME_AUTHORIZATION_CONTRACT,
239
- status: "authorized",
240
- mode,
241
- eventName,
242
- repository: nonEmpty(repository, "repository"),
243
- actor: {
244
- login: nonEmpty(actor, "actor"),
245
- permission: actorPermission,
246
- },
247
- reason: nonEmpty(reason, "reason"),
248
- authorizedAt: timestamp,
249
- source: {
250
- sha: exactSha(sourceSha, "sourceSha"),
251
- treeSha: exactSha(sourceTreeSha, "sourceTreeSha"),
252
- },
253
- request: { ref, class: refClass },
254
- runtime: { sha: runtimeSha, reachableFrom: readbacks },
255
- contractLocks: {
256
- stableRoot: root(stableContractLockRoot, "stableContractLockRoot"),
257
- alphaRoot: root(alphaContractLockRoot, "alphaContractLockRoot"),
258
- },
259
- consumerPolicyReceiptRoot: root(
260
- consumerPolicyReceiptRoot,
261
- "consumerPolicyReceiptRoot",
262
- ),
263
- persistenceScanRoot: root(persistenceScan.root, "persistenceScan.root"),
264
- };
265
- return { receipt, receiptRoot: runtimeResumeDocumentRoot(receipt) };
266
- }
267
-
268
- export function verifyRuntimeAuthorizationReceipt({
269
- receipt,
270
- receiptRoot,
271
- repository = "",
272
- sourceSha = "",
273
- runtimeSha = "",
274
- consumerPolicyReceiptRoot = "",
275
- } = {}) {
276
- const failures = [];
277
- const check = (condition, code) => {
278
- if (!condition) failures.push(code);
279
- };
280
- check(
281
- receipt?.contract === RUNTIME_AUTHORIZATION_CONTRACT,
282
- "contract-invalid",
283
- );
284
- check(receipt?.status === "authorized", "status-not-authorized");
285
- check(
286
- SHA256_ROOT.test(String(receiptRoot || "")) &&
287
- receiptRoot === runtimeResumeDocumentRoot(receipt),
288
- "receipt-root-mismatch",
289
- );
290
- check(receipt?.eventName === "workflow_dispatch", "event-not-trusted");
291
- check(
292
- new Set(["write", "maintain", "admin"]).has(receipt?.actor?.permission),
293
- "permission-insufficient",
294
- );
295
- check(
296
- EXACT_SHA.test(String(receipt?.runtime?.sha || "")),
297
- "runtime-sha-invalid",
298
- );
299
- check(
300
- Array.isArray(receipt?.runtime?.reachableFrom) &&
301
- receipt.runtime.reachableFrom.some(
302
- (entry) => approvedRef(entry.ref) && entry.containsRuntimeSha === true,
303
- ),
304
- "runtime-unreachable",
305
- );
306
- if (repository)
307
- check(receipt?.repository === repository, "repository-mismatch");
308
- if (sourceSha)
309
- check(receipt?.source?.sha === sourceSha, "source-sha-mismatch");
310
- if (runtimeSha)
311
- check(receipt?.runtime?.sha === runtimeSha, "runtime-sha-mismatch");
312
- if (consumerPolicyReceiptRoot) {
313
- check(
314
- receipt?.consumerPolicyReceiptRoot === consumerPolicyReceiptRoot,
315
- "consumer-policy-root-mismatch",
316
- );
317
- }
318
- return { ok: failures.length === 0, failures };
319
- }
320
-
321
- export function createRuntimeResumeLineage({
322
- authorization,
323
- authorizationRoot,
324
- buildAttempt,
325
- resumeAttempt,
326
- source,
327
- consumerPolicyReceiptRoot,
328
- requiredPlatforms,
329
- stageCapsules,
330
- resumePlanRoot,
331
- finalPublicReadbackRoot,
332
- floatingRefBefore,
333
- floatingRefAfter,
334
- } = {}) {
335
- const verification = verifyRuntimeAuthorizationReceipt({
336
- receipt: authorization,
337
- receiptRoot: authorizationRoot,
338
- sourceSha: source?.sha,
339
- runtimeSha: resumeAttempt?.runtimeSha,
340
- consumerPolicyReceiptRoot,
341
- });
342
- if (!verification.ok) {
343
- throw new Error(
344
- `runtime authorization invalid: ${verification.failures.join(", ")}`,
345
- );
346
- }
347
- const buildId = token(buildAttempt?.id, "buildAttempt.id");
348
- const resumeId = token(resumeAttempt?.id, "resumeAttempt.id");
349
- if (buildId === resumeId) {
350
- throw new Error("resume must create a new governed attempt");
351
- }
352
- const buildRuntimeSha = exactSha(
353
- buildAttempt?.runtimeSha,
354
- "buildAttempt.runtimeSha",
355
- );
356
- const resumeRuntimeSha = exactSha(
357
- resumeAttempt?.runtimeSha,
358
- "resumeAttempt.runtimeSha",
359
- );
360
- if (buildRuntimeSha === resumeRuntimeSha) {
361
- throw new Error(
362
- "resume runtime must differ from the original build runtime",
363
- );
364
- }
365
- const sourceIdentity = {
366
- sha: exactSha(source?.sha, "source.sha"),
367
- treeSha: exactSha(source?.treeSha, "source.treeSha"),
368
- };
369
- const policyRoot = root(
370
- consumerPolicyReceiptRoot,
371
- "consumerPolicyReceiptRoot",
372
- );
373
- const platforms = sortedUnique(requiredPlatforms, "requiredPlatforms");
374
- if (!Array.isArray(stageCapsules))
375
- throw new Error("stageCapsules must be an array");
376
- const capsuleByPlatform = new Map();
377
- for (const [index, capsule] of stageCapsules.entries()) {
378
- exactKeys(
379
- capsule,
380
- [
381
- "platform",
382
- "capsuleRoot",
383
- "identityRoot",
384
- "artifactDigest",
385
- "sourceSha",
386
- "sourceTreeSha",
387
- "policyRoot",
388
- "buildRuntimeSha",
389
- "sealed",
390
- ],
391
- `stageCapsules[${index}]`,
392
- );
393
- const platform = token(
394
- capsule.platform,
395
- `stageCapsules[${index}].platform`,
396
- );
397
- if (!platforms.includes(platform)) {
398
- throw new Error(`stageCapsules[${index}] names an undeclared platform`);
399
- }
400
- if (capsuleByPlatform.has(platform)) {
401
- throw new Error(`duplicate Stage Capsule for ${platform}`);
402
- }
403
- if (capsule.sealed !== true)
404
- throw new Error(`Stage Capsule ${platform} is not sealed`);
405
- if (
406
- exactSha(capsule.sourceSha, `${platform}.sourceSha`) !==
407
- sourceIdentity.sha ||
408
- exactSha(capsule.sourceTreeSha, `${platform}.sourceTreeSha`) !==
409
- sourceIdentity.treeSha ||
410
- root(capsule.policyRoot, `${platform}.policyRoot`) !== policyRoot ||
411
- exactSha(capsule.buildRuntimeSha, `${platform}.buildRuntimeSha`) !==
412
- buildRuntimeSha
413
- ) {
414
- throw new Error(
415
- `Stage Capsule ${platform} identity is stale or ambiguous`,
416
- );
417
- }
418
- capsuleByPlatform.set(platform, {
419
- platform,
420
- capsuleRoot: root(capsule.capsuleRoot, `${platform}.capsuleRoot`),
421
- identityRoot: root(capsule.identityRoot, `${platform}.identityRoot`),
422
- artifactDigest: root(
423
- capsule.artifactDigest,
424
- `${platform}.artifactDigest`,
425
- ),
426
- });
427
- }
428
- const before = {
429
- ref: normalizeRef(floatingRefBefore?.ref),
430
- sha: exactSha(floatingRefBefore?.sha, "floatingRefBefore.sha"),
431
- };
432
- const after = {
433
- ref: normalizeRef(floatingRefAfter?.ref),
434
- sha: exactSha(floatingRefAfter?.sha, "floatingRefAfter.sha"),
435
- };
436
- if (!OFFICIAL_DOMAIN_REF.test(before.ref) || before.ref !== after.ref) {
437
- throw new Error(
438
- "floating ref movement must remain on one official v4 channel",
439
- );
440
- }
441
- if (before.sha === after.sha) {
442
- throw new Error(
443
- "floating ref movement must bind distinct before and after SHAs",
444
- );
445
- }
446
- const reusedCapsules = platforms
447
- .filter((platform) => capsuleByPlatform.has(platform))
448
- .map((platform) => capsuleByPlatform.get(platform));
449
- const rebuildPlatforms = platforms.filter(
450
- (platform) => !capsuleByPlatform.has(platform),
451
- );
452
- const lineage = {
453
- schemaVersion: 1,
454
- contract: RUNTIME_RESUME_LINEAGE_CONTRACT,
455
- status: "qualified",
456
- authorizationRoot: root(authorizationRoot, "authorizationRoot"),
457
- source: sourceIdentity,
458
- consumerPolicyReceiptRoot: policyRoot,
459
- attempts: {
460
- build: { id: buildId, runtimeSha: buildRuntimeSha },
461
- resume: { id: resumeId, runtimeSha: resumeRuntimeSha },
462
- },
463
- floatingRefMovement: { before, after },
464
- continuation: {
465
- mechanism: "new-governed-attempt",
466
- rerunFailedJobs: false,
467
- resumePlanRoot: root(resumePlanRoot, "resumePlanRoot"),
468
- },
469
- stageCapsules: {
470
- requiredPlatforms: platforms,
471
- reused: reusedCapsules,
472
- rebuildPlatforms,
473
- },
474
- finalPublicReadbackRoot: root(
475
- finalPublicReadbackRoot,
476
- "finalPublicReadbackRoot",
477
- ),
478
- };
479
- return { lineage, lineageRoot: runtimeResumeDocumentRoot(lineage) };
480
- }
481
-
482
- function validSourceIdentity(source) {
483
- return (
484
- EXACT_SHA.test(String(source?.sha || "")) &&
485
- EXACT_SHA.test(String(source?.treeSha || ""))
486
- );
487
- }
488
-
489
- function validRuntimeTransition(attempts) {
490
- return (
491
- EXACT_SHA.test(String(attempts?.build?.runtimeSha || "")) &&
492
- EXACT_SHA.test(String(attempts?.resume?.runtimeSha || "")) &&
493
- attempts.build.runtimeSha !== attempts.resume.runtimeSha
494
- );
495
- }
496
-
497
- function validFloatingRefMovement(movement) {
498
- return (
499
- OFFICIAL_DOMAIN_REF.test(String(movement?.before?.ref || "")) &&
500
- movement.before.ref === movement?.after?.ref &&
501
- EXACT_SHA.test(String(movement.before.sha || "")) &&
502
- EXACT_SHA.test(String(movement.after.sha || "")) &&
503
- movement.before.sha !== movement.after.sha
504
- );
505
- }
506
-
507
- function platformLineageFailures(stageCapsules) {
508
- const failures = [];
509
- const required = stageCapsules?.requiredPlatforms || [];
510
- const reused = stageCapsules?.reused || [];
511
- const rebuilt = stageCapsules?.rebuildPlatforms || [];
512
- const requiredCanonical = [...new Set(required)].sort();
513
- const reusedPlatforms = reused.map((entry) => entry.platform);
514
- const rebuiltCanonical = [...new Set(rebuilt)].sort();
515
- if (
516
- required.length === 0 ||
517
- required.length !== requiredCanonical.length ||
518
- !required.every((platform, index) => platform === requiredCanonical[index])
519
- ) {
520
- failures.push("required-platforms-invalid");
521
- }
522
- if (
523
- reusedPlatforms.length !== new Set(reusedPlatforms).size ||
524
- rebuilt.length !== rebuiltCanonical.length ||
525
- !reusedPlatforms.every((platform) => !rebuilt.includes(platform))
526
- ) {
527
- failures.push("platform-lineage-ambiguous");
528
- }
529
- if (
530
- [...reusedPlatforms, ...rebuilt].sort().join("\0") !==
531
- [...required].sort().join("\0")
532
- ) {
533
- failures.push("platform-lineage-incomplete");
534
- }
535
- if (
536
- !reused.every((entry) =>
537
- [entry.capsuleRoot, entry.identityRoot, entry.artifactDigest].every(
538
- (value) => SHA256_ROOT.test(String(value || "")),
539
- ),
540
- )
541
- ) {
542
- failures.push("capsule-root-invalid");
543
- }
544
- return failures;
545
- }
546
-
547
- export function verifyRuntimeResumeLineage({
548
- lineage,
549
- lineageRoot,
550
- repository = "",
551
- sourceSha = "",
552
- buildRuntimeSha = "",
553
- resumeRuntimeSha = "",
554
- consumerPolicyReceiptRoot = "",
555
- } = {}) {
556
- const failures = [];
557
- const check = (condition, code) => {
558
- if (!condition) failures.push(code);
559
- };
560
- check(
561
- lineage?.contract === RUNTIME_RESUME_LINEAGE_CONTRACT,
562
- "contract-invalid",
563
- );
564
- check(lineage?.status === "qualified", "status-not-qualified");
565
- check(
566
- SHA256_ROOT.test(String(lineageRoot || "")) &&
567
- lineageRoot === runtimeResumeDocumentRoot(lineage),
568
- "lineage-root-mismatch",
569
- );
570
- check(
571
- lineage?.attempts?.build?.id &&
572
- lineage?.attempts?.resume?.id &&
573
- lineage.attempts.build.id !== lineage.attempts.resume.id,
574
- "attempt-not-fresh",
575
- );
576
- check(validSourceIdentity(lineage?.source), "source-identity-invalid");
577
- check(validRuntimeTransition(lineage?.attempts), "runtime-lineage-invalid");
578
- check(
579
- validFloatingRefMovement(lineage?.floatingRefMovement),
580
- "floating-ref-movement-invalid",
581
- );
582
- check(
583
- lineage?.continuation?.mechanism === "new-governed-attempt" &&
584
- lineage?.continuation?.rerunFailedJobs === false,
585
- "continuation-mechanism-invalid",
586
- );
587
- failures.push(...platformLineageFailures(lineage?.stageCapsules));
588
- check(
589
- [
590
- lineage?.authorizationRoot,
591
- lineage?.consumerPolicyReceiptRoot,
592
- lineage?.continuation?.resumePlanRoot,
593
- lineage?.finalPublicReadbackRoot,
594
- ].every((value) => SHA256_ROOT.test(String(value || ""))),
595
- "evidence-root-invalid",
596
- );
597
- if (sourceSha)
598
- check(lineage?.source?.sha === sourceSha, "source-sha-mismatch");
599
- if (buildRuntimeSha) {
600
- check(
601
- lineage?.attempts?.build?.runtimeSha === buildRuntimeSha,
602
- "build-runtime-mismatch",
603
- );
604
- }
605
- if (resumeRuntimeSha) {
606
- check(
607
- lineage?.attempts?.resume?.runtimeSha === resumeRuntimeSha,
608
- "resume-runtime-mismatch",
609
- );
610
- }
611
- if (consumerPolicyReceiptRoot) {
612
- check(
613
- lineage?.consumerPolicyReceiptRoot === consumerPolicyReceiptRoot,
614
- "consumer-policy-root-mismatch",
615
- );
616
- }
617
- if (repository) {
618
- check(Boolean(lineage?.authorizationRoot), "authorization-root-missing");
619
- }
620
- return { ok: failures.length === 0, failures };
621
- }
@@ -1,64 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import crypto from "node:crypto";
5
-
6
- import { SOURCE_OWNED_PROMOTION } from "./invocation-selector.js";
7
- export { SOURCE_OWNED_PROMOTION };
8
-
9
- export function verifyWorkflowDefinition({
10
- root,
11
- repository,
12
- definitionRepository,
13
- definitionSha,
14
- workflowSha,
15
- files,
16
- }) {
17
- if (
18
- repository !== SOURCE_OWNED_PROMOTION.repository ||
19
- definitionRepository !== repository
20
- )
21
- throw new Error(
22
- "Source-owned invocation requires the defining Buildchain repository",
23
- );
24
- if (
25
- !/^[0-9a-f]{40}$/.test(definitionSha || "") ||
26
- definitionSha !== workflowSha
27
- )
28
- throw new Error(
29
- "Source-owned invocation must use the exact defining workflow commit",
30
- );
31
- const git = (...args) =>
32
- execFileSync("git", ["-C", root, ...args], {
33
- stdio: ["ignore", "pipe", "pipe"],
34
- });
35
- if (git("rev-parse", "HEAD").toString().trim() !== definitionSha)
36
- throw new Error(
37
- "Invocation checkout does not match its defining workflow commit",
38
- );
39
- for (const { path: relative } of files) {
40
- const absolute = path.resolve(root, relative);
41
- if (
42
- !absolute.startsWith(`${path.resolve(root)}${path.sep}`) ||
43
- fs.lstatSync(absolute).isSymbolicLink()
44
- )
45
- throw new Error(
46
- "Invocation source must be a repository-owned regular file",
47
- );
48
- let committed;
49
- try {
50
- committed = git("show", `${definitionSha}:${relative}`);
51
- } catch {
52
- throw new Error(`Invocation source is not committed: ${relative}`);
53
- }
54
- if (!committed.equals(fs.readFileSync(absolute)))
55
- throw new Error(
56
- `Invocation source differs from its defining commit: ${relative}`,
57
- );
58
- }
59
- return {
60
- repository,
61
- commitSha: definitionSha,
62
- filesRoot: `sha256:${crypto.createHash("sha256").update(JSON.stringify(files)).digest("hex")}`,
63
- };
64
- }