@xemahq/edition-tooling 0.0.0-stage → 0.1.1

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 (219) hide show
  1. package/LICENSE +175 -0
  2. package/dist/cli.d.ts +7 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +47 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/edition.d.ts +4 -0
  7. package/dist/commands/edition.d.ts.map +1 -0
  8. package/dist/commands/edition.js +58 -0
  9. package/dist/commands/edition.js.map +1 -0
  10. package/dist/edition/verify-image-evidence.bundle.mjs +72104 -0
  11. package/dist/edition/verify-image-evidence.bundle.provenance.json +4119 -0
  12. package/dist/lib/exit-codes.d.ts +6 -0
  13. package/dist/lib/exit-codes.d.ts.map +1 -0
  14. package/dist/lib/exit-codes.js +21 -0
  15. package/dist/lib/exit-codes.js.map +1 -0
  16. package/dist/runtime/edition/run-entry.d.ts +10 -0
  17. package/dist/runtime/edition/run-entry.d.ts.map +1 -0
  18. package/dist/runtime/edition/run-entry.js +49 -0
  19. package/dist/runtime/edition/run-entry.js.map +1 -0
  20. package/dist/runtime/edition/verb-map.d.ts +70 -0
  21. package/dist/runtime/edition/verb-map.d.ts.map +1 -0
  22. package/dist/runtime/edition/verb-map.js +166 -0
  23. package/dist/runtime/edition/verb-map.js.map +1 -0
  24. package/edition/README.md +291 -0
  25. package/edition/adapter-bundled-versions.mjs +128 -0
  26. package/edition/adapter-host-versions.mjs +76 -0
  27. package/edition/assemble-producer-input.mjs +1423 -0
  28. package/edition/assemble-third-party-input.mjs +348 -0
  29. package/edition/authored-source-check.mjs +141 -0
  30. package/edition/authority-binding.mjs +23 -0
  31. package/edition/authorization-base.mjs +357 -0
  32. package/edition/biome-content.mjs +327 -0
  33. package/edition/build-biome-index.mjs +180 -0
  34. package/edition/build-ledger-completeness.mjs +67 -0
  35. package/edition/build-ledger.mjs +107 -0
  36. package/edition/build-platform-service-index.mjs +292 -0
  37. package/edition/build-runtime-image-source-index.mjs +1197 -0
  38. package/edition/built-verifier.mjs +55 -0
  39. package/edition/candidate-edition.mjs +735 -0
  40. package/edition/canonical-evidence-json.mjs +82 -0
  41. package/edition/canonical-repository-url.mjs +61 -0
  42. package/edition/catalogue-edition.mjs +49 -0
  43. package/edition/catalogue-evidence.mjs +228 -0
  44. package/edition/catalogue-fetch.mjs +124 -0
  45. package/edition/catalogue-lock-field.mjs +41 -0
  46. package/edition/catalogue-pin.mjs +71 -0
  47. package/edition/catalogue.mjs +237 -0
  48. package/edition/chart-set-source-commit.mjs +97 -0
  49. package/edition/check-committed-image-evidence.mjs +412 -0
  50. package/edition/checkout-indexed-source-repositories.mjs +283 -0
  51. package/edition/collected-refusals.mjs +59 -0
  52. package/edition/comment-blanker.mjs +75 -0
  53. package/edition/connector-source-authority.mjs +60 -0
  54. package/edition/contract-authority.mjs +122 -0
  55. package/edition/cosign-certificate-constraints.mjs +50 -0
  56. package/edition/cross-repo-build-inputs.mjs +158 -0
  57. package/edition/customer-definition.mjs +72 -0
  58. package/edition/customer-edition-verbs.mjs +610 -0
  59. package/edition/customer-flags.mjs +15 -0
  60. package/edition/definition-checkout.mjs +96 -0
  61. package/edition/develop-build-record.mjs +122 -0
  62. package/edition/develop-catalogue-free-resolve.mjs +282 -0
  63. package/edition/develop-catalogue-free-vendor.mjs +118 -0
  64. package/edition/develop-channel-evidence.mjs +23 -0
  65. package/edition/develop-channel-record.mjs +49 -0
  66. package/edition/develop-channel-satellites.mjs +65 -0
  67. package/edition/develop-chart-roster.mjs +96 -0
  68. package/edition/develop-chart-set.mjs +147 -0
  69. package/edition/develop-current-resolution.mjs +29 -0
  70. package/edition/develop-edition-invariants.mjs +142 -0
  71. package/edition/develop-edition-resolve.mjs +250 -0
  72. package/edition/develop-edition.mjs +332 -0
  73. package/edition/develop-engine-image.mjs +97 -0
  74. package/edition/develop-freeze.mjs +304 -0
  75. package/edition/develop-git-bundles.mjs +296 -0
  76. package/edition/develop-graph.mjs +34 -0
  77. package/edition/develop-image-coordinate.mjs +85 -0
  78. package/edition/develop-image-probe.mjs +122 -0
  79. package/edition/develop-image-record.mjs +260 -0
  80. package/edition/develop-resolve-report.mjs +159 -0
  81. package/edition/develop-select-records.mjs +90 -0
  82. package/edition/develop-selection-line.mjs +29 -0
  83. package/edition/develop-selection-pins.mjs +36 -0
  84. package/edition/develop-snapshot-inputs.mjs +112 -0
  85. package/edition/develop-store-root.mjs +20 -0
  86. package/edition/dispatched-runtime-components.mjs +172 -0
  87. package/edition/dist-check.mjs +1174 -0
  88. package/edition/distribution-evidence-set.mjs +1064 -0
  89. package/edition/distribution-paths.mjs +191 -0
  90. package/edition/edition-artifact.mjs +529 -0
  91. package/edition/edition-biome-manifests.mjs +85 -0
  92. package/edition/edition-ceiling.mjs +182 -0
  93. package/edition/edition-channel.mjs +27 -0
  94. package/edition/edition-chart-set.mjs +104 -0
  95. package/edition/edition-config.mjs +374 -0
  96. package/edition/edition-constants.mjs +110 -0
  97. package/edition/edition-contract-parse.mjs +179 -0
  98. package/edition/edition-contract.json +100 -0
  99. package/edition/edition-image-seal.mjs +115 -0
  100. package/edition/edition-kind.mjs +10 -0
  101. package/edition/edition-list.mjs +35 -0
  102. package/edition/edition-locks.mjs +78 -0
  103. package/edition/edition-pin-verbs.mjs +183 -0
  104. package/edition/edition-pin.mjs +17 -0
  105. package/edition/edition-pinned-inputs.mjs +116 -0
  106. package/edition/edition-protocol.mjs +285 -0
  107. package/edition/edition-publisher.mjs +21 -0
  108. package/edition/edition-seal.mjs +257 -0
  109. package/edition/edition-source-pins.mjs +211 -0
  110. package/edition/edition-unpin.mjs +16 -0
  111. package/edition/edition-workspace.mjs +205 -0
  112. package/edition/evidence-checkout.mjs +439 -0
  113. package/edition/evidence-grammars.mjs +18 -0
  114. package/edition/evidence-input-mode.mjs +136 -0
  115. package/edition/evidence-trust-pin.mjs +392 -0
  116. package/edition/evidence-trust-policy.mjs +374 -0
  117. package/edition/external-source-walk.mjs +486 -0
  118. package/edition/first-party-source-ids.mjs +62 -0
  119. package/edition/fleet-coordinate-finality.mjs +363 -0
  120. package/edition/fulcio-certificate-extensions.mjs +278 -0
  121. package/edition/git-json.mjs +54 -0
  122. package/edition/git-source-auth.mjs +131 -0
  123. package/edition/image-delivery-entitlement-descriptor.mjs +106 -0
  124. package/edition/image-evidence-replay-state.mjs +242 -0
  125. package/edition/image-evidence.mjs +1328 -0
  126. package/edition/image-lock-service-rows.mjs +26 -0
  127. package/edition/image-subject-identity.mjs +19 -0
  128. package/edition/images-from-record.mjs +216 -0
  129. package/edition/indexes-at-pins.mjs +13 -0
  130. package/edition/infra-images-lock.mjs +105 -0
  131. package/edition/kernel-capability-domain.mjs +30 -0
  132. package/edition/kernel-contract-authority.mjs +526 -0
  133. package/edition/kernel-contract-source-path-set.mjs +96 -0
  134. package/edition/kernel-package.mjs +237 -0
  135. package/edition/kernel-pin-source.mjs +63 -0
  136. package/edition/kernel-protocol-choice.mjs +239 -0
  137. package/edition/kernel-signer-kind.mjs +23 -0
  138. package/edition/lib.mjs +3322 -0
  139. package/edition/locked-bundle-signer.mjs +297 -0
  140. package/edition/map-with-concurrency.mjs +19 -0
  141. package/edition/no-follow-file.mjs +94 -0
  142. package/edition/oci-evidence-verbs.mjs +421 -0
  143. package/edition/oci-image-source-evidence.mjs +377 -0
  144. package/edition/operation-grant-admission.mjs +285 -0
  145. package/edition/operation-requirements.mjs +282 -0
  146. package/edition/oras-process.mjs +35 -0
  147. package/edition/oras.mjs +664 -0
  148. package/edition/pin-source.mjs +242 -0
  149. package/edition/pinned-cosign.mjs +86 -0
  150. package/edition/plan-image-evidence-refresh.mjs +428 -0
  151. package/edition/platform-lock-readability.mjs +903 -0
  152. package/edition/platform-service-class.mjs +85 -0
  153. package/edition/platform-service-ownership.mjs +91 -0
  154. package/edition/platform-service-source-authority.mjs +105 -0
  155. package/edition/positive-closure.mjs +162 -0
  156. package/edition/previous-image-lock.mjs +25 -0
  157. package/edition/produce-distribution-completeness-evidence.mjs +317 -0
  158. package/edition/produce-edition-catalogue-evidence.mjs +252 -0
  159. package/edition/produce-image-delivery-entitlement-evidence.mjs +302 -0
  160. package/edition/produce-image-source-evidence.mjs +788 -0
  161. package/edition/producer-evidence-bundle.mjs +201 -0
  162. package/edition/producer-evidence-index-file.mjs +4 -0
  163. package/edition/record-refused.mjs +11 -0
  164. package/edition/registry-client-factory.mjs +21 -0
  165. package/edition/registry-credentials.mjs +225 -0
  166. package/edition/release-catalogue.mjs +48 -0
  167. package/edition/release-channel-guard.mjs +108 -0
  168. package/edition/release-edition-artifact.mjs +152 -0
  169. package/edition/release-edition.mjs +54 -0
  170. package/edition/remedy-command.mjs +89 -0
  171. package/edition/repin-evidence-trust.mjs +109 -0
  172. package/edition/required-resource-provider.mjs +58 -0
  173. package/edition/resolve-chart-set.mjs +376 -0
  174. package/edition/resolve-customer-edition.mjs +374 -0
  175. package/edition/resolve-distribution.mjs +2642 -0
  176. package/edition/resolve-edition.mjs +192 -0
  177. package/edition/resolve-images.mjs +2344 -0
  178. package/edition/resolve-infra-images.mjs +88 -0
  179. package/edition/seal-image-digests.mjs +954 -0
  180. package/edition/sealed-distribution-lock.mjs +22 -0
  181. package/edition/sealed-evidence-accept.mjs +82 -0
  182. package/edition/shallow-checkout.mjs +18 -0
  183. package/edition/sigstore-bundle-media-type.mjs +4 -0
  184. package/edition/source-authority.mjs +713 -0
  185. package/edition/source-closure.mjs +115 -0
  186. package/edition/store-publisher-trust.mjs +40 -0
  187. package/edition/strict-json.mjs +121 -0
  188. package/edition/string-order.mjs +5 -0
  189. package/edition/third-party-admission.mjs +118 -0
  190. package/edition/third-party-bundle-registry.mjs +349 -0
  191. package/edition/third-party-certificate-extensions.mjs +58 -0
  192. package/edition/third-party-image-pins.mjs +41 -0
  193. package/edition/third-party-pins-trusted.mjs +24 -0
  194. package/edition/third-party-producer-evidence.mjs +76 -0
  195. package/edition/third-party-source-check.mjs +28 -0
  196. package/edition/third-party-source-context.mjs +41 -0
  197. package/edition/third-party-source-evidence.mjs +326 -0
  198. package/edition/third-party-source-flags.mjs +148 -0
  199. package/edition/third-party-source-pins.mjs +270 -0
  200. package/edition/third-party-source-uniqueness.mjs +54 -0
  201. package/edition/third-party-source-walk.mjs +118 -0
  202. package/edition/third-party-source-writer.mjs +228 -0
  203. package/edition/third-party-subjects.mjs +128 -0
  204. package/edition/trust-policy-arming-verb.mjs +60 -0
  205. package/edition/trust-policy-arming.mjs +144 -0
  206. package/edition/trust-policy-image-ownership.mjs +85 -0
  207. package/edition/trusted-executable.mjs +166 -0
  208. package/edition/verifier-binding.mjs +45 -0
  209. package/edition/verifier-digest-pin.mjs +17 -0
  210. package/edition/verifier-files.mjs +8 -0
  211. package/edition/verifier-kernel-schemas.mjs +22 -0
  212. package/edition/verifier-provenance.mjs +64 -0
  213. package/edition/verify-catalogue-evidence.mjs +247 -0
  214. package/edition/verify-image-evidence.mjs +493 -0
  215. package/edition/verify-image-platforms.mjs +523 -0
  216. package/edition/why.mjs +56 -0
  217. package/edition/workspace-sections.mjs +287 -0
  218. package/package.json +83 -4
  219. package/README.md +0 -3
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+ // assemble-third-party-input — the producer input of ONE image of a THIRD-PARTY biome, assembled in the publisher's own
3
+ // GitHub-hosted workflow (XEMA-1611 design section 5).
4
+ //
5
+ // xema-edition evidence assemble-third-party-input \
6
+ // --bundle <oci repository>@sha256:<64-hex> \
7
+ // --contract-authority <file> \
8
+ // --image <image repository>@sha256:<64-hex> \
9
+ // --subject-id <component image name> \
10
+ // --provenance <absolute exported BuildKit SLSA v1 predicate> \
11
+ // --registry-credentials <file> \
12
+ // --output <path> \
13
+ // [--ttl-seconds <n>]
14
+ //
15
+ // It runs on PUBLIC inputs only: this CLI and the `@xemahq/contracts` it ships (both from public npm), the publisher's
16
+ // own checkout at GITHUB_WORKSPACE, its own registry credential declaration and the GITHUB_* context of the run. It
17
+ // takes no data root, no kernel checkout and nothing of the vendor's. The checks that need the vendor's trust policy
18
+ // (the producer TTL ceiling, the signer rows, image ownership) run at the vendor's admission, by the trusted writer
19
+ // (`index runtime-image-source --trusted-write --third-party`), never here.
20
+ //
21
+ // Emits the EXACT producer input `produce-image-source-evidence.mjs` accepts, through the first-party assembler's own
22
+ // document builder and gates (`assemble-producer-input.mjs`). Field derivation:
23
+ // * contractAuthority — the `--contract-authority` file (the kernel contract authority the vendor's evidence trust
24
+ // pin names), proved to be the kernel this CLI ships before anything else is read.
25
+ // * subject kind and id — the bundle's `xema-biome.json` (`--bundle`, read BY DIGEST): the one service or worker
26
+ // component with an `oci-image` artifact whose image name is `--subject-id` (a service by
27
+ // `protocol.serviceName`, a worker by its key), kind `runtime-component` for a
28
+ // `scheduler: runtime` component and `service` otherwise, as the index builder names it.
29
+ // * repo, repositoryUrl, sourceCommit, producerWorkflowSha, signer, builderId, runId, runAttempt
30
+ // — GITHUB_REPOSITORY, GITHUB_SHA, GITHUB_WORKFLOW_REF, GITHUB_RUN_ID, GITHUB_RUN_ATTEMPT.
31
+ // * sourceManifestHash — the kernel hash of the bundle's `xema-biome.json`.
32
+ // * path — `<repo>/<biome dir>`: the directory of the ONE `xema-biome.json` of the checkout at
33
+ // GITHUB_SHA whose hash is the bundle's (none, or more than one, is refused by name).
34
+ // * sourceTreeHash — `hashImageBuildInputs` over the publisher's checkout at GITHUB_SHA, the same algorithm
35
+ // first-party images are hashed with, with no cross-repo inputs. It is publisher-attested:
36
+ // the vendor never recomputes it.
37
+ // * builder digests — the exported BuildKit SLSA v1 predicate (`--provenance`), whose VCS revision must be
38
+ // GITHUB_SHA.
39
+ // * manifest media type, platforms, sbomDigest, provenanceDigest
40
+ // — the registry, read BY DIGEST at `--image` (no tag is read), with the publisher's credential.
41
+ // * issuedAt/expiresAt — now, plus `--ttl-seconds` or DEFAULT_PRODUCER_TTL_SECONDS (72h). The ceiling is the vendor
42
+ // trust policy's maxProducerTtlSeconds, enforced by the vendor's third-party pin writer
43
+ // (`index runtime-image-source --trusted-write --third-party`) and `verify-evidence`; this CLI holds
44
+ // no second one. A value that is not a positive integer, or whose expiresAt RFC 3339 cannot
45
+ // name, is refused by name.
46
+ //
47
+ // It refuses to run unless RUNNER_ENVIRONMENT is the declared runner environment (`--runner-environment self-hosted`, else
48
+ // `github-hosted`) and GITHUB_REF is `refs/heads/main`. Those variables are self-asserted by the run, so this is only a
49
+ // fail-fast mirror: the authority is the vendor's admission, which holds the Fulcio certificate's Runner Environment to
50
+ // the runner environment the vendor's pin declares for the source (github-hosted unless declared self-hosted) and its
51
+ // Source Repository Ref to the release channel.
52
+
53
+ import { isAbsolute, resolve } from 'node:path';
54
+
55
+ import {
56
+ ProducerTtlCeilingSource,
57
+ assertAssembledProducerInput,
58
+ assertCanonicalSubjectId,
59
+ assertWorkflowContext,
60
+ deriveBuilder,
61
+ producerInputDocument,
62
+ producerTtlSecondsFlag,
63
+ readAssemblerFlags,
64
+ resolveImageArtifactEvidenceByDigest,
65
+ resolveProducerTtlSeconds,
66
+ writeProducerInputFile,
67
+ } from './assemble-producer-input.mjs';
68
+ import { configureEditionWorkspaceFromArgv, isEntryModule, kernelPackageDir, stateKernelPackageDir } from './edition-workspace.mjs';
69
+ import { readPublicContractAuthority } from './evidence-input-mode.mjs';
70
+ import { RunnerEnvironment, parseDeclaredRunnerEnvironment } from './fulcio-certificate-extensions.mjs';
71
+ import { shippedKernelPackageDir } from './kernel-package.mjs';
72
+ import {
73
+ assertGitCommitExists,
74
+ biomeImageComponents,
75
+ claimBiomeImageName,
76
+ hashImageBuildInputs,
77
+ isRuntimeImageComponent,
78
+ imageInputSourceHashAlgorithm,
79
+ readGitJsonAtCommit,
80
+ } from './lib.mjs';
81
+ import { OrasRegistry } from './oras.mjs';
82
+ import { sandboxedGit } from './produce-image-source-evidence.mjs';
83
+ import { loadRegistryCredentialDeclarationsFile, resolveRegistryCredential, splitImageRef } from './registry-credentials.mjs';
84
+ import { subjectServicePath } from './resolve-images.mjs';
85
+ import { fetchBundleManifest } from './third-party-bundle-registry.mjs';
86
+ import { OCI_REPOSITORY_AT_DIGEST } from './third-party-source-flags.mjs';
87
+ import { PUBLISHER_ATTESTED_LABEL } from './third-party-subjects.mjs';
88
+
89
+ /** The runner a third-party producer input is assembled on when `--runner-environment` is not given. */
90
+ export const THIRD_PARTY_RUNNER_ENVIRONMENT = RunnerEnvironment.GithubHosted;
91
+ /** The only ref a third-party producer input is assembled from (the release channel). */
92
+ export const THIRD_PARTY_PRODUCER_REF = 'refs/heads/main';
93
+
94
+ const BIOME_MANIFEST_FILE = 'xema-biome.json';
95
+ const OCI_IMAGE_ARTIFACT = 'oci-image';
96
+
97
+ /** The flags of the verb; every one but `--ttl-seconds` is required. */
98
+ export const THIRD_PARTY_INPUT_FLAGS = Object.freeze({
99
+ bundle: 'bundle',
100
+ contractAuthority: 'contract-authority',
101
+ image: 'image',
102
+ subjectId: 'subject-id',
103
+ provenance: 'provenance',
104
+ registryCredentials: 'registry-credentials',
105
+ output: 'output',
106
+ ttlSeconds: 'ttl-seconds',
107
+ runnerEnvironment: 'runner-environment',
108
+ });
109
+
110
+ export const THIRD_PARTY_INPUT_USAGE =
111
+ 'usage: xema-edition evidence assemble-third-party-input --bundle <oci repository>@sha256:<64-hex> ' +
112
+ '--contract-authority <file> --image <image repository>@sha256:<64-hex> --subject-id <component image name> ' +
113
+ '--provenance <absolute exported SLSA v1 predicate> --registry-credentials <file> --output <path> [--ttl-seconds <n>] ' +
114
+ `[--runner-environment ${RunnerEnvironment.SelfHosted}, default ${RunnerEnvironment.GithubHosted}]`;
115
+
116
+ /**
117
+ * Refuse a run that is not a `runnerEnvironment` run of refs/heads/main, before anything else is read.
118
+ * RUNNER_ENVIRONMENT and GITHUB_REF are self-asserted, so this is a fail-fast mirror only; the authority is the vendor
119
+ * admission's check of the Fulcio certificate's Runner Environment (against the pin's declaration) and Source
120
+ * Repository Ref extensions.
121
+ */
122
+ export function assertThirdPartyRunner(env, runnerEnvironment = THIRD_PARTY_RUNNER_ENVIRONMENT) {
123
+ if (env.RUNNER_ENVIRONMENT !== runnerEnvironment) {
124
+ throw new Error(
125
+ `a third-party producer input is assembled only on a ${runnerEnvironment} runner ` +
126
+ `(RUNNER_ENVIRONMENT is ${JSON.stringify(env.RUNNER_ENVIRONMENT ?? null)}): the vendor admits only certificates whose ` +
127
+ `Fulcio Runner Environment extension is the one its pin declares (${RunnerEnvironment.GithubHosted} unless declared ` +
128
+ `${RunnerEnvironment.SelfHosted}); pass --${THIRD_PARTY_INPUT_FLAGS.runnerEnvironment} ${RunnerEnvironment.SelfHosted} only when it does`,
129
+ );
130
+ }
131
+ if (env.GITHUB_REF !== THIRD_PARTY_PRODUCER_REF) {
132
+ throw new Error(
133
+ `a third-party producer input is assembled only from ${THIRD_PARTY_PRODUCER_REF} ` +
134
+ `(GITHUB_REF is ${JSON.stringify(env.GITHUB_REF ?? null)}): the vendor admits only certificates whose Fulcio ` +
135
+ `Source Repository Ref extension is ${THIRD_PARTY_PRODUCER_REF}, the release channel`,
136
+ );
137
+ }
138
+ }
139
+
140
+ function ociReference(flag, value) {
141
+ if (!OCI_REPOSITORY_AT_DIGEST.test(value ?? '')) {
142
+ throw new Error(`--${flag} must be <repository>@sha256:<64-hex>; got ${JSON.stringify(value)}`);
143
+ }
144
+ const at = value.indexOf('@');
145
+ return { repository: value.slice(0, at), digest: value.slice(at + 1) };
146
+ }
147
+
148
+ /**
149
+ * The bundle component `subjectId` names, and its runtime image subject kind, by the index builder's own rule
150
+ * (`biomeImageComponents`, `claimBiomeImageName`, `isRuntimeImageComponent` in lib.mjs): every image component is
151
+ * named, so a component with no image name, a web biome with an image and a duplicate image name are refused as the
152
+ * index builder refuses them; the subject is the one with its own `oci-image` artifact named `subjectId`.
153
+ */
154
+ export function bundleImageSubject({ manifest, subjectId, enums, bundleRef }) {
155
+ const xema = manifest?.xema;
156
+ const components = Array.isArray(xema?.components) ? xema.components : [];
157
+ const rel = `bundle ${bundleRef} ${BIOME_MANIFEST_FILE}`;
158
+ const taken = new Set();
159
+ const images = biomeImageComponents(components)
160
+ .map((component) => ({ component, name: claimBiomeImageName(component, { rel, id: xema?.id, target: xema?.target, taken }) }))
161
+ .filter(({ component }) => component.artifact?.kind === OCI_IMAGE_ARTIFACT);
162
+ const match = images.find(({ name }) => name === subjectId);
163
+ if (match === undefined) {
164
+ throw new Error(
165
+ `bundle ${bundleRef} has no ${OCI_IMAGE_ARTIFACT} service or worker component named ${JSON.stringify(subjectId)} ` +
166
+ `(its images: ${images.map(({ name }) => name).join(', ') || 'none'}); --subject-id names exactly one`,
167
+ );
168
+ }
169
+ const kind = isRuntimeImageComponent(match.component)
170
+ ? enums.RuntimeImageSubjectKind.RuntimeComponent
171
+ : enums.RuntimeImageSubjectKind.Service;
172
+ return { kind, id: subjectId };
173
+ }
174
+
175
+ /**
176
+ * The biome directory of the publisher's checkout at `commit`: the directory of the ONE `xema-biome.json` whose kernel
177
+ * source manifest hash is `sourceManifestHash` (`''` for the repository root). None, or more than one, is refused.
178
+ */
179
+ export async function biomeDirectoryAtCommit({ checkout, commit, sourceManifestHash, schemas, bundleRef }) {
180
+ const paths = sandboxedGit(checkout, ['ls-tree', '-r', '-z', '--name-only', commit])
181
+ .split('\0')
182
+ .filter((path) => path === BIOME_MANIFEST_FILE || path.endsWith(`/${BIOME_MANIFEST_FILE}`));
183
+ const matching = [];
184
+ for (const path of paths) {
185
+ const hash = await schemas.computeBiomeSourceManifestHash(readGitJsonAtCommit(checkout, path, commit));
186
+ if (hash === sourceManifestHash) {matching.push(path);}
187
+ }
188
+ if (matching.length !== 1) {
189
+ throw new Error(
190
+ `the checkout at ${commit} has ${matching.length} ${BIOME_MANIFEST_FILE} files whose source manifest hash is ` +
191
+ `${sourceManifestHash}, the hash of bundle ${bundleRef} ` +
192
+ `(${matching.length === 0 ? `its ${BIOME_MANIFEST_FILE} files: ${paths.join(', ') || 'none'}` : matching.join(', ')}); exactly one is required: ` +
193
+ 'the bundle must be published from this commit',
194
+ );
195
+ }
196
+ const [path] = matching;
197
+ return path === BIOME_MANIFEST_FILE ? '' : path.slice(0, -`/${BIOME_MANIFEST_FILE}`.length);
198
+ }
199
+
200
+ /**
201
+ * Assemble the producer input of one third-party image and prove it satisfies the producer's exact contract.
202
+ *
203
+ * @param {object} request
204
+ * @param {string} request.bundle `<oci repository>@sha256:<hex>` of the published biome bundle
205
+ * @param {string} request.contractAuthorityPath the `--contract-authority` file
206
+ * @param {string} request.image `<image repository>@sha256:<hex>` of the published image
207
+ * @param {string} request.subjectId the bundle component's image name
208
+ * @param {string} request.provenancePath the exported BuildKit SLSA v1 predicate (absolute)
209
+ * @param {string} request.registryCredentialsPath the publisher's registry credential declaration file
210
+ * @param {number} [request.ttlSeconds]
211
+ * @param {'self-hosted'} [request.runnerEnvironment] the declared runner environment (`parseDeclaredRunnerEnvironment`; absent: github-hosted)
212
+ * @param {string} [request.kernelDir] the kernel package (default: the configured Kernel root)
213
+ * @param {object} [request.env]
214
+ * @param {Function} [request.registryFor] `({authority, credential}) => client`, for tests
215
+ * @param {number} [request.now]
216
+ */
217
+ export async function assembleThirdPartyInput({
218
+ bundle,
219
+ contractAuthorityPath,
220
+ image,
221
+ subjectId,
222
+ provenancePath,
223
+ registryCredentialsPath,
224
+ ttlSeconds,
225
+ runnerEnvironment,
226
+ kernelDir = kernelPackageDir(),
227
+ env = process.env,
228
+ fetchImpl = globalThis.fetch,
229
+ registryFor = null,
230
+ now = Date.now(),
231
+ }) {
232
+ assertThirdPartyRunner(
233
+ env,
234
+ parseDeclaredRunnerEnvironment(runnerEnvironment, `--${THIRD_PARTY_INPUT_FLAGS.runnerEnvironment}`) ?? THIRD_PARTY_RUNNER_ENVIRONMENT,
235
+ );
236
+ const context = assertWorkflowContext(env);
237
+ ociReference(THIRD_PARTY_INPUT_FLAGS.bundle, bundle);
238
+ const imageRef = ociReference(THIRD_PARTY_INPUT_FLAGS.image, image);
239
+ assertCanonicalSubjectId(subjectId);
240
+ const effectiveTtlSeconds = resolveProducerTtlSeconds({ ttlSeconds, now, ceiling: { source: ProducerTtlCeilingSource.VendorAdmission } });
241
+ const checkout = env.GITHUB_WORKSPACE;
242
+ if (typeof checkout !== 'string' || !isAbsolute(checkout)) {
243
+ throw new Error(
244
+ `GITHUB_WORKSPACE (${JSON.stringify(checkout ?? null)}) is not an absolute path: the publisher's checkout at GITHUB_SHA is hashed there`,
245
+ );
246
+ }
247
+ try {
248
+ assertGitCommitExists(checkout, context.sha);
249
+ } catch (error) {
250
+ throw new Error(`the publisher's checkout at GITHUB_WORKSPACE ${checkout} does not hold GITHUB_SHA ${context.sha}: ${error.message}`);
251
+ }
252
+ const { contractAuthority, schemas } = await readPublicContractAuthority({ path: contractAuthorityPath, kernelDir });
253
+ const builder = deriveBuilder({ provenancePath, sourceCommit: context.sha });
254
+
255
+ const declarations = loadRegistryCredentialDeclarationsFile(registryCredentialsPath);
256
+ const clients = new Map();
257
+ const clientFor = (authority) => {
258
+ if (!clients.has(authority)) {
259
+ const credential = resolveRegistryCredential({ authority, declarations, env });
260
+ // No vendor data root: the publisher's oras is held to its version, not to a vendor pin. Every byte read is
261
+ // content-addressed here and re-verified by the vendor's admission.
262
+ clients.set(authority, registryFor ? registryFor({ authority, credential }) : new OrasRegistry({ credential, trust: () => null, fetchImpl }));
263
+ }
264
+ return clients.get(authority);
265
+ };
266
+
267
+ const { manifest } = await fetchBundleManifest({ clientFor, ociRef: bundle });
268
+ const sourceManifestHash = await schemas.computeBiomeSourceManifestHash(manifest);
269
+ const subject = bundleImageSubject({ manifest, subjectId, enums: schemas, bundleRef: bundle });
270
+ const biomeDirectory = await biomeDirectoryAtCommit({ checkout, commit: context.sha, sourceManifestHash, schemas, bundleRef: bundle });
271
+ const servicePath = subjectServicePath(
272
+ { kind: subject.kind, id: subject.id, repo: context.repository, path: biomeDirectory === '' ? '.' : biomeDirectory },
273
+ context.sha,
274
+ () => ({ dir: checkout, base: checkout }),
275
+ );
276
+ const sourceTreeHash = hashImageBuildInputs(
277
+ { repoDir: checkout, service: subject.id, servicePath, commit: context.sha, crossRepoInputs: [], buildStepDirectories: [] },
278
+ imageInputSourceHashAlgorithm(),
279
+ );
280
+ const artifact = await resolveImageArtifactEvidenceByDigest({
281
+ client: clientFor(splitImageRef(imageRef.repository).authority),
282
+ imageRepository: imageRef.repository,
283
+ manifestDigest: imageRef.digest,
284
+ referenceProvenance: builder.predicate,
285
+ });
286
+ const input = producerInputDocument({
287
+ contractAuthority,
288
+ context,
289
+ builder,
290
+ now,
291
+ ttlSeconds: effectiveTtlSeconds,
292
+ subject: {
293
+ kind: subject.kind,
294
+ id: subject.id,
295
+ repositoryUrl: `https://github.com/${context.repository}`,
296
+ repo: context.repository,
297
+ path: biomeDirectory === '' ? context.repository : `${context.repository}/${biomeDirectory}`,
298
+ sourceCommit: context.sha,
299
+ sourceManifestHash,
300
+ sourceTreeHash,
301
+ imageRepository: imageRef.repository,
302
+ ...artifact,
303
+ },
304
+ });
305
+ assertAssembledProducerInput({ input, schemas, contractAuthority, context });
306
+ return input;
307
+ }
308
+
309
+ function requireFlag(flags, name) {
310
+ const value = flags.get(name);
311
+ if (value === undefined) {
312
+ throw new Error(`--${name} is required\n${THIRD_PARTY_INPUT_USAGE}`);
313
+ }
314
+ return value;
315
+ }
316
+
317
+ async function main() {
318
+ // No workspace root: the verb runs on public inputs, with the kernel this CLI ships.
319
+ const args = configureEditionWorkspaceFromArgv(process.argv.slice(2), [], { refuseOthers: true });
320
+ stateKernelPackageDir(shippedKernelPackageDir());
321
+ const flags = readAssemblerFlags(args, new Set(Object.values(THIRD_PARTY_INPUT_FLAGS)));
322
+ const output = resolve(requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.output));
323
+ const ttl = flags.get(THIRD_PARTY_INPUT_FLAGS.ttlSeconds);
324
+ const input = await assembleThirdPartyInput({
325
+ bundle: requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.bundle),
326
+ contractAuthorityPath: resolve(requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.contractAuthority)),
327
+ image: requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.image),
328
+ subjectId: requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.subjectId),
329
+ provenancePath: requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.provenance),
330
+ registryCredentialsPath: resolve(requireFlag(flags, THIRD_PARTY_INPUT_FLAGS.registryCredentials)),
331
+ ttlSeconds: producerTtlSecondsFlag(ttl),
332
+ runnerEnvironment: flags.get(THIRD_PARTY_INPUT_FLAGS.runnerEnvironment),
333
+ });
334
+ writeProducerInputFile(output, input);
335
+ const [subject] = input.subjects;
336
+ console.log(
337
+ `[assemble-third-party-input] wrote ${output} for subject ${subject.kind}:${subject.id} ` +
338
+ `(${subject.imageRepository}@${subject.manifestDigest}, ${subject.repo}@${subject.sourceCommit}, ` +
339
+ `sourceTreeHash (${PUBLISHER_ATTESTED_LABEL}) ${subject.sourceTreeHash})`,
340
+ );
341
+ }
342
+
343
+ if (isEntryModule(import.meta.url)) {
344
+ main().catch((error) => {
345
+ console.error(`[assemble-third-party-input] ${error.message}`);
346
+ process.exitCode = 1;
347
+ });
348
+ }
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ // authored-source-check — `xema-edition check-authored`: grades the AUTHORED
3
+ // distribution source, the develop lifecycle's gate. It is its own mode, not
4
+ // the release check with assertions turned off: `xema-edition check`
5
+ // (dist-check.mjs) stays the full release-path grade, and nothing here can
6
+ // make it skip anything.
7
+ //
8
+ // Authored and release state are separate lifecycles. What is authored is
9
+ // graded here, offline; what only a release carries is not read: no lock
10
+ // content (the committed distribution / images / authorization / chart-set
11
+ // locks; the trust-arming rule counts lock files by presence), and nothing that
12
+ // needs a source checkout at a committed `repositoryPins` commit. No edition is
13
+ // resolved here: the plane invariants over a resolved edition (the
14
+ // capability-domain reservation table and biome confinement) are asserted on
15
+ // develop by the develop resolve against the graph it froze
16
+ // (develop-edition-invariants.mjs), and on the release path by
17
+ // `xema-edition check` over the committed locks. One place per assertion.
18
+ //
19
+ // KEPT (each refuses):
20
+ // - the kernel speaks a supported edition protocol;
21
+ // - biome-index.json: immutable entries, deterministic order, no duplicate
22
+ // source identity, a serviceDependencies projection on every entry;
23
+ // - platform-service-index.json: parses, matches the declared
24
+ // platformServiceSources offline, service-dependency owner parity;
25
+ // - runtime-image-source-index.json: the offline `--check`;
26
+ // - the evidence trust policy, its data-root pin and its arming assessment
27
+ // (`--channel <develop|release>`, default release: release holds the built
28
+ // evidence verifier to the pin's digests, develop states a differing digest
29
+ // and checks everything else; an unknown channel is refused);
30
+ // - the data-root readers (store source repositories, publisher trust);
31
+ // - the fleet chart-set pin is a rostered chart-set lock;
32
+ // - the pinned kernel's authorization composer loads;
33
+ // - the ownership self-tests: the kernel's capability-domain reader refuses
34
+ // two owners of one domain and an invalid domain, and counts no unlisted
35
+ // domain as first-party (synthetic tables, no lock);
36
+ // - every edition definition is structurally valid: the kernel parses it
37
+ // (its `sourcePins` included) and it composes.
38
+ //
39
+ // NOT HERE:
40
+ // - lock currency, sealed hashes, the authorization lock, each edition's
41
+ // chart-set.lock.json copy, platform-service ownership at pinned commits,
42
+ // `--check-sources` (refused): release state, `xema-edition check`;
43
+ // - the definition resolve, reservations, confinement: the develop resolve.
44
+
45
+ import {
46
+ checkChannelFromArgv,
47
+ checkDataRootPlane,
48
+ discoverDistributionIds,
49
+ loadPlaneAuthorizationKernel,
50
+ ownershipSelfTestFailures,
51
+ readFleetChartSetPin,
52
+ } from './dist-check.mjs';
53
+ import { EditionChannel } from './edition-channel.mjs';
54
+ import { describeEditionHolds } from './edition-source-pins.mjs';
55
+ import { WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule } from './edition-workspace.mjs';
56
+ import { loadKernelDistributionSchemas } from './lib.mjs';
57
+ import { composeEdition } from './resolve-distribution.mjs';
58
+
59
+ /** The release-path flag this mode refuses rather than ignores. */
60
+ const CHECK_SOURCES_FLAG = '--check-sources';
61
+
62
+ /**
63
+ * Whether edition `id`'s definition is structurally valid: the kernel parses it
64
+ * and it composes. Nothing is resolved and no index or source is read.
65
+ *
66
+ * @returns {Promise<string | null>} the refusal, or null when valid
67
+ */
68
+ export async function definitionRefusal(id) {
69
+ try {
70
+ const schemas = await loadKernelDistributionSchemas();
71
+ composeEdition(schemas.parseDistribution, id);
72
+ return null;
73
+ } catch (err) {
74
+ return err.message;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * The ownership self-tests (dist-check.mjs): the kernel's capability-domain
80
+ * reader, against synthetic tables. Lock-free. Returns true when green.
81
+ */
82
+ async function checkOwnershipSelfTests(resolveFirstPartyCapabilityDomains) {
83
+ let failures;
84
+ try {
85
+ failures = ownershipSelfTestFailures(resolveFirstPartyCapabilityDomains);
86
+ } catch (err) {
87
+ failures = [`the kernel's capability-domain reader threw on a valid table: ${err.message}`];
88
+ }
89
+ for (const failure of failures) {console.error(`[dist:check-authored] ownership self-test FAILED: ${failure}`);}
90
+ if (failures.length === 0) {console.log('[dist:check-authored] ownership self-tests: green.');}
91
+ return failures.length === 0;
92
+ }
93
+
94
+ /**
95
+ * The authored check over the configured roots. Returns true when green.
96
+ * `resolveFirstPartyCapabilityDomains` is the kernel's reader unless a test
97
+ * stands in for it. `channel` (`--channel <develop|release>`, default release) is
98
+ * the lane checked; `locateVerifier` is a test seam for the built verifier.
99
+ */
100
+ export async function checkAuthoredSource({ resolveFirstPartyCapabilityDomains, channel = EditionChannel.Release, locateVerifier } = {}) {
101
+ let green = await checkDataRootPlane({ checkSources: false, channel, locateVerifier });
102
+ const reader = resolveFirstPartyCapabilityDomains ?? (await loadKernelDistributionSchemas()).resolveFirstPartyCapabilityDomains;
103
+ if (!(await checkOwnershipSelfTests(reader))) {green = false;}
104
+
105
+ const distributionIds = discoverDistributionIds();
106
+ for (const line of describeEditionHolds(distributionIds)) {console.log(line);}
107
+ for (const id of distributionIds) {
108
+ const refusal = await definitionRefusal(id);
109
+ if (refusal === null) {
110
+ console.log(`[dist:check-authored] ${id}: the definition is valid.`);
111
+ } else {
112
+ console.error(`[dist:check-authored] ${id}: the definition is invalid — ${refusal}`);
113
+ green = false;
114
+ }
115
+ }
116
+
117
+ const { green: authorizationKernelGreen } = await loadPlaneAuthorizationKernel();
118
+ if (!authorizationKernelGreen) {green = false;}
119
+ if (readFleetChartSetPin() === null) {green = false;}
120
+
121
+ return green;
122
+ }
123
+
124
+ async function main() {
125
+ if (process.argv.includes(CHECK_SOURCES_FLAG)) {
126
+ throw new Error(
127
+ `${CHECK_SOURCES_FLAG} audits the indexes against source checkouts, which is release state: ` +
128
+ `run \`xema-edition check ${CHECK_SOURCES_FLAG}\`.`,
129
+ );
130
+ }
131
+ if (!(await checkAuthoredSource({ channel: checkChannelFromArgv(process.argv.slice(2)) }))) {process.exit(1);}
132
+ console.log('[dist:check-authored] the authored distribution source is green.');
133
+ }
134
+
135
+ if (isEntryModule(import.meta.url)) {
136
+ configureEditionWorkspaceFromProcess([WorkspaceRoot.Data, WorkspaceRoot.Editions, WorkspaceRoot.Sources, WorkspaceRoot.Kernel]);
137
+ main().catch((err) => {
138
+ console.error(`[dist:check-authored] ${err.message}`);
139
+ process.exit(1);
140
+ });
141
+ }
@@ -0,0 +1,23 @@
1
+ // authority-binding — whether evidence must be stated under the current kernel contract authority. One predicate, used
2
+ // by every site that compares a statement's `contractAuthority` with the expected one.
3
+
4
+ import { EditionChannel } from './edition-channel.mjs';
5
+
6
+ /** Who owns the subject whose evidence is checked: a first-party subject is built from this platform's source. */
7
+ export const EvidenceSubjectKind = Object.freeze({ FirstParty: 'first-party', ThirdParty: 'third-party' });
8
+
9
+ /**
10
+ * Whether evidence of a `subjectKind` subject on `channel` is held to the current kernel contract authority. False only
11
+ * for a third-party subject on the develop channel: re-signing every third-party subject whenever the kernel moves is
12
+ * provenance machinery that a deployment we manage does not carry. Only the authority comparison is skipped; the
13
+ * signature, subject digest, policy row and sole image signer row are checked on every channel.
14
+ */
15
+ export function isAuthorityBound(channel, subjectKind) {
16
+ if (!Object.values(EditionChannel).includes(channel)) {
17
+ throw new Error(`unknown edition channel ${JSON.stringify(channel)}`);
18
+ }
19
+ if (!Object.values(EvidenceSubjectKind).includes(subjectKind)) {
20
+ throw new Error(`unknown evidence subject kind ${JSON.stringify(subjectKind)}`);
21
+ }
22
+ return !(channel === EditionChannel.Develop && subjectKind === EvidenceSubjectKind.ThirdParty);
23
+ }