@xemahq/xema 0.25.1 → 0.27.0
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.
- package/README.md +70 -37
- package/dist/assets/appliance-base/images.lock.json +126 -126
- package/dist/assets/appliance-lean/images.lock.json +123 -123
- package/dist/assets/cloud/images.lock.json +126 -126
- package/dist/assets/core/images.lock.json +120 -120
- package/dist/assets/distribution-assets.manifest.json +15 -16
- package/dist/assets/minimal/images.lock.json +108 -108
- package/dist/assets/oss/images.lock.json +126 -126
- package/dist/assets/starter/images.lock.json +123 -123
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/dev.js +1 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +7 -1
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/edition.d.ts +3 -0
- package/dist/commands/edition.d.ts.map +1 -0
- package/dist/commands/edition.js +46 -0
- package/dist/commands/edition.js.map +1 -0
- package/dist/commands/init.js +2 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/serve.js +1 -0
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/up.js +1 -0
- package/dist/commands/up.js.map +1 -1
- package/dist/dev/env-defaults.d.ts.map +1 -1
- package/dist/dev/env-defaults.js +6 -1
- package/dist/dev/env-defaults.js.map +1 -1
- package/dist/dev/infra-preflight.d.ts +3 -2
- package/dist/dev/infra-preflight.d.ts.map +1 -1
- package/dist/dev/infra-preflight.js +18 -8
- package/dist/dev/infra-preflight.js.map +1 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +54179 -0
- package/dist/edition/verify-image-evidence.bundle.provenance.json +2871 -0
- package/dist/runtime/backing-registration.d.ts.map +1 -1
- package/dist/runtime/backing-registration.js +7 -2
- package/dist/runtime/backing-registration.js.map +1 -1
- package/dist/runtime/config/schema.d.ts +1 -0
- package/dist/runtime/config/schema.d.ts.map +1 -1
- package/dist/runtime/custody/local-custody.d.ts +12 -0
- package/dist/runtime/custody/local-custody.d.ts.map +1 -0
- package/dist/runtime/custody/local-custody.js +22 -0
- package/dist/runtime/custody/local-custody.js.map +1 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.d.ts +18 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.js +50 -0
- package/dist/runtime/custody/openbao-custody-bootstrap.js.map +1 -0
- package/dist/runtime/custody/openbao-custody-store.d.ts +18 -0
- package/dist/runtime/custody/openbao-custody-store.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-custody-store.js +55 -0
- package/dist/runtime/custody/openbao-custody-store.js.map +1 -0
- package/dist/runtime/custody/openbao-http-client.d.ts +34 -0
- package/dist/runtime/custody/openbao-http-client.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-http-client.js +154 -0
- package/dist/runtime/custody/openbao-http-client.js.map +1 -0
- package/dist/runtime/custody/openbao-local-constants.d.ts +22 -0
- package/dist/runtime/custody/openbao-local-constants.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-local-constants.js +48 -0
- package/dist/runtime/custody/openbao-local-constants.js.map +1 -0
- package/dist/runtime/custody/openbao-local-runner.d.ts +16 -0
- package/dist/runtime/custody/openbao-local-runner.d.ts.map +1 -0
- package/dist/runtime/custody/openbao-local-runner.js +86 -0
- package/dist/runtime/custody/openbao-local-runner.js.map +1 -0
- package/dist/runtime/edition/run-entry.d.ts +9 -0
- package/dist/runtime/edition/run-entry.d.ts.map +1 -0
- package/dist/runtime/edition/run-entry.js +36 -0
- package/dist/runtime/edition/run-entry.js.map +1 -0
- package/dist/runtime/edition/verb-map.d.ts +60 -0
- package/dist/runtime/edition/verb-map.d.ts.map +1 -0
- package/dist/runtime/edition/verb-map.js +146 -0
- package/dist/runtime/edition/verb-map.js.map +1 -0
- package/dist/runtime/env-seeder.d.ts +3 -0
- package/dist/runtime/env-seeder.d.ts.map +1 -1
- package/dist/runtime/env-seeder.js +20 -3
- package/dist/runtime/env-seeder.js.map +1 -1
- package/dist/runtime/launch.d.ts +3 -1
- package/dist/runtime/launch.d.ts.map +1 -1
- package/dist/runtime/launch.js +37 -8
- package/dist/runtime/launch.js.map +1 -1
- package/dist/runtime/paths.d.ts +1 -2
- package/dist/runtime/paths.d.ts.map +1 -1
- package/dist/runtime/paths.js +0 -10
- package/dist/runtime/paths.js.map +1 -1
- package/dist/runtime/runner/chart-local-provenance.d.ts +20 -0
- package/dist/runtime/runner/chart-local-provenance.d.ts.map +1 -0
- package/dist/runtime/runner/chart-local-provenance.js +114 -0
- package/dist/runtime/runner/chart-local-provenance.js.map +1 -0
- package/dist/runtime/runner/chart-preflight.d.ts +8 -8
- package/dist/runtime/runner/chart-preflight.d.ts.map +1 -1
- package/dist/runtime/runner/chart-preflight.js +30 -69
- package/dist/runtime/runner/chart-preflight.js.map +1 -1
- package/dist/runtime/runner/chart-pull.d.ts +14 -0
- package/dist/runtime/runner/chart-pull.d.ts.map +1 -0
- package/dist/runtime/runner/chart-pull.js +96 -0
- package/dist/runtime/runner/chart-pull.js.map +1 -0
- package/dist/runtime/runner/chart-resolver.d.ts +4 -12
- package/dist/runtime/runner/chart-resolver.d.ts.map +1 -1
- package/dist/runtime/runner/chart-resolver.js +16 -62
- package/dist/runtime/runner/chart-resolver.js.map +1 -1
- package/dist/runtime/runner/chart-set-lock.d.ts +6 -20
- package/dist/runtime/runner/chart-set-lock.d.ts.map +1 -1
- package/dist/runtime/runner/chart-set-lock.js +55 -99
- package/dist/runtime/runner/chart-set-lock.js.map +1 -1
- package/dist/runtime/runner/helm-cli.d.ts +3 -1
- package/dist/runtime/runner/helm-cli.d.ts.map +1 -1
- package/dist/runtime/runner/helm-cli.js +9 -7
- package/dist/runtime/runner/helm-cli.js.map +1 -1
- package/dist/runtime/runner/helm-runner.d.ts +4 -4
- package/dist/runtime/runner/helm-runner.d.ts.map +1 -1
- package/dist/runtime/runner/helm-runner.js +18 -16
- package/dist/runtime/runner/helm-runner.js.map +1 -1
- package/dist/runtime/sources/kinds/git.d.ts +1 -2
- package/dist/runtime/sources/kinds/git.d.ts.map +1 -1
- package/dist/runtime/sources/kinds/git.js +8 -5
- package/dist/runtime/sources/kinds/git.js.map +1 -1
- package/dist/runtime/sources/manifest-walk.d.ts +3 -1
- package/dist/runtime/sources/manifest-walk.d.ts.map +1 -1
- package/dist/runtime/sources/manifest-walk.js +8 -3
- package/dist/runtime/sources/manifest-walk.js.map +1 -1
- package/dist/runtime/sources/schema.d.ts +3 -0
- package/dist/runtime/sources/schema.d.ts.map +1 -1
- package/dist/runtime/sources/schema.js +6 -1
- package/dist/runtime/sources/schema.js.map +1 -1
- package/dist/runtime/supervisor.d.ts +3 -0
- package/dist/runtime/supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor.js +2 -0
- package/dist/runtime/supervisor.js.map +1 -1
- package/edition/README.md +315 -0
- package/edition/assemble-producer-input.mjs +1251 -0
- package/edition/authorization-base.mjs +347 -0
- package/edition/biome-content.mjs +268 -0
- package/edition/build-biome-index.mjs +175 -0
- package/edition/build-platform-service-index.mjs +288 -0
- package/edition/build-runtime-image-source-index.mjs +821 -0
- package/edition/built-verifier.mjs +55 -0
- package/edition/catalogue-edition.mjs +49 -0
- package/edition/catalogue-fetch.mjs +64 -0
- package/edition/catalogue.mjs +135 -0
- package/edition/chart-set-source-commit.mjs +92 -0
- package/edition/check-committed-image-evidence.mjs +383 -0
- package/edition/checkout-indexed-source-repositories.mjs +105 -0
- package/edition/comment-blanker.mjs +75 -0
- package/edition/connector-source-authority.mjs +60 -0
- package/edition/customer-definition.mjs +183 -0
- package/edition/customer-flags.mjs +7 -0
- package/edition/develop-customer-edition.mjs +99 -0
- package/edition/develop-edition-resolve.mjs +65 -0
- package/edition/develop-edition.mjs +274 -0
- package/edition/develop-freeze.mjs +173 -0
- package/edition/develop-image-coordinate.mjs +54 -0
- package/edition/develop-seal.mjs +77 -0
- package/edition/develop-snapshot-inputs.mjs +104 -0
- package/edition/dist-check.mjs +986 -0
- package/edition/distribution-evidence-set.mjs +987 -0
- package/edition/distribution-paths.mjs +191 -0
- package/edition/edition-artifact.mjs +270 -0
- package/edition/edition-biome-manifests.mjs +85 -0
- package/edition/edition-channel.mjs +6 -0
- package/edition/edition-chart-set.mjs +93 -0
- package/edition/edition-config.mjs +207 -0
- package/edition/edition-constants.mjs +57 -0
- package/edition/edition-contract.json +23 -0
- package/edition/edition-list.mjs +35 -0
- package/edition/edition-locks.mjs +69 -0
- package/edition/edition-protocol.mjs +228 -0
- package/edition/edition-workspace.mjs +176 -0
- package/edition/evidence-checkout.mjs +418 -0
- package/edition/evidence-grammars.mjs +16 -0
- package/edition/evidence-trust-pin.mjs +304 -0
- package/edition/evidence-trust-policy.mjs +256 -0
- package/edition/external-source-walk.mjs +463 -0
- package/edition/first-party-source-ids.mjs +59 -0
- package/edition/fleet-coordinate-finality.mjs +350 -0
- package/edition/git-source-auth.mjs +52 -0
- package/edition/image-evidence-replay-state.mjs +242 -0
- package/edition/image-evidence.mjs +1225 -0
- package/edition/kernel-contract-authority.mjs +443 -0
- package/edition/lib.mjs +3018 -0
- package/edition/no-follow-file.mjs +94 -0
- package/edition/oci-evidence-verbs.mjs +392 -0
- package/edition/oci-image-source-evidence.mjs +239 -0
- package/edition/oras.mjs +648 -0
- package/edition/pin-source.mjs +227 -0
- package/edition/plan-image-evidence-refresh.mjs +408 -0
- package/edition/platform-lock-readability.mjs +892 -0
- package/edition/platform-service-class.mjs +18 -0
- package/edition/platform-service-source-authority.mjs +105 -0
- package/edition/positive-closure.mjs +162 -0
- package/edition/produce-distribution-completeness-evidence.mjs +311 -0
- package/edition/produce-image-delivery-entitlement-evidence.mjs +294 -0
- package/edition/produce-image-source-evidence.mjs +765 -0
- package/edition/producer-evidence-bundle.mjs +157 -0
- package/edition/registry-credentials.mjs +193 -0
- package/edition/release-catalogue.mjs +39 -0
- package/edition/release-channel-guard.mjs +99 -0
- package/edition/release-edition-artifact.mjs +158 -0
- package/edition/remedy-command.mjs +89 -0
- package/edition/repin-evidence-trust.mjs +101 -0
- package/edition/required-resource-provider.mjs +58 -0
- package/edition/resolve-chart-set.mjs +415 -0
- package/edition/resolve-customer-edition.mjs +288 -0
- package/edition/resolve-distribution.mjs +2318 -0
- package/edition/resolve-edition.mjs +186 -0
- package/edition/resolve-images.mjs +1606 -0
- package/edition/seal-image-digests.mjs +855 -0
- package/edition/sealed-evidence-accept.mjs +82 -0
- package/edition/source-authority.mjs +725 -0
- package/edition/source-closure.mjs +116 -0
- package/edition/strict-json.mjs +121 -0
- package/edition/string-order.mjs +5 -0
- package/edition/trust-policy-arming.mjs +120 -0
- package/edition/trusted-executable.mjs +166 -0
- package/edition/verifier-binding.mjs +45 -0
- package/edition/verifier-files.mjs +8 -0
- package/edition/verifier-kernel-schemas.mjs +22 -0
- package/edition/verifier-provenance.mjs +64 -0
- package/edition/verify-image-evidence.mjs +430 -0
- package/edition/verify-image-platforms.mjs +495 -0
- package/edition/why.mjs +56 -0
- package/edition/workspace-sections.mjs +195 -0
- package/examples/06-biome-sources.yaml +1 -0
- package/package.json +18 -7
- package/dist/commands/dist.d.ts +0 -16
- package/dist/commands/dist.d.ts.map +0 -1
- package/dist/commands/dist.js +0 -182
- package/dist/commands/dist.js.map +0 -1
- package/dist/runtime/distribution/available-biome.d.ts +0 -10
- package/dist/runtime/distribution/available-biome.d.ts.map +0 -1
- package/dist/runtime/distribution/available-biome.js +0 -154
- package/dist/runtime/distribution/available-biome.js.map +0 -1
- package/dist/runtime/distribution/compose.d.ts +0 -23
- package/dist/runtime/distribution/compose.d.ts.map +0 -1
- package/dist/runtime/distribution/compose.js +0 -48
- package/dist/runtime/distribution/compose.js.map +0 -1
- package/dist/runtime/runner/chart-trust.d.ts +0 -57
- package/dist/runtime/runner/chart-trust.d.ts.map +0 -1
- package/dist/runtime/runner/chart-trust.js +0 -270
- package/dist/runtime/runner/chart-trust.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A platform service's installation class, as the index carries it.
|
|
3
|
+
*
|
|
4
|
+
* The class is the service's OWN declaration (`core/xema-distribution.json`
|
|
5
|
+
* `platformServices[].installationClass`). There is no default and no list of
|
|
6
|
+
* names here: a service that declares none has none in the index, and the
|
|
7
|
+
* resolver refuses it by name rather than reading a guessed class.
|
|
8
|
+
*/
|
|
9
|
+
export function declaredInstallationClass(declaration) {
|
|
10
|
+
return declaration.installationClass === undefined
|
|
11
|
+
? {}
|
|
12
|
+
: { installationClass: declaration.installationClass };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The (name, tier, declared class) a declaration and an index entry must agree on. */
|
|
16
|
+
export function comparablePlatformService(entry) {
|
|
17
|
+
return { name: entry.name, tier: entry.tier, ...declaredInstallationClass(entry) };
|
|
18
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { workspaceSection } from './edition-config.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The ONE authority for what a declared platform service IS, source-wise.
|
|
5
|
+
*
|
|
6
|
+
* the data root's `edition-workspace.json#platformServiceSources` declares the
|
|
7
|
+
* platform services, their tier and installation class, and where their source
|
|
8
|
+
* lives. The library owns the SCHEMA of that section (workspace-sections.mjs) and
|
|
9
|
+
* this module reads it; for every declared platform service it names
|
|
10
|
+
*
|
|
11
|
+
* - `repo` + `manifestPath` — the exact source manifest whose `name`/`version`
|
|
12
|
+
* are the service's identity, read at the repository's published commit;
|
|
13
|
+
* - `manifestName` — the name that manifest MUST declare, which is what stops
|
|
14
|
+
* a name-only substitution from silently re-pointing a service;
|
|
15
|
+
* - `imageKey` — the join to the independent images.lock authority;
|
|
16
|
+
* - `runtimeBinding` — the actual runtime image subject that CARRIES this
|
|
17
|
+
* service, or `null` when the service is its own repository-root image.
|
|
18
|
+
*
|
|
19
|
+
* That last field is the partition the whole image plane turns on, and it is
|
|
20
|
+
* declared exactly once, here:
|
|
21
|
+
*
|
|
22
|
+
* - `runtimeBinding === null` — an INDEPENDENTLY SOURCED service. Its image is
|
|
23
|
+
* built from its repository root, and `resolve-images.mjs` mints a
|
|
24
|
+
* `platform`-kind subject for it whose id is `imageKey`. These are the five
|
|
25
|
+
* generic submodules.
|
|
26
|
+
* - `runtimeBinding !== null` — the service's image is a subject something ELSE
|
|
27
|
+
* produces: a supplemental subject declared by
|
|
28
|
+
* `build-runtime-image-source-index.mjs`, or a component of a selected biome.
|
|
29
|
+
* No `platform`-kind subject is minted; the binding names the real one.
|
|
30
|
+
*
|
|
31
|
+
* Both halves still carry an exact source identity, because
|
|
32
|
+
* `LockedPlatformService.source` is REQUIRED by the kernel distribution-lock
|
|
33
|
+
* schema at schemaVersion 2. Before this module existed, the index generator
|
|
34
|
+
* resolved every service to a hardcoded `submodules/<name>`, so it could only
|
|
35
|
+
* ever describe the five submodules, and every edition failed to resolve on
|
|
36
|
+
* `xema-kernel-server` — declared since bc8d5c6 and never resolvable.
|
|
37
|
+
*
|
|
38
|
+
* Adding a platform service is therefore ONE data entry in the data root and no
|
|
39
|
+
* code change: the platform service index is derived from these rows, so there
|
|
40
|
+
* is no second list to forget.
|
|
41
|
+
*
|
|
42
|
+
* A row is a hand-declared fact only where nothing can derive it. It cannot be
|
|
43
|
+
* derived: `docs-api` is sourced from a package named `xema-docs`,
|
|
44
|
+
* `xema-kernel-server` sits at a sub-path of a carved repository, and
|
|
45
|
+
* `opencode-image-prepuller` ships no package of its own at all — it is a
|
|
46
|
+
* DaemonSet whose sole purpose is to keep the `opencode-worker` image warm, so
|
|
47
|
+
* its identity is exactly the identity of the biome that declares that image.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
const declarations = () =>
|
|
51
|
+
workspaceSection('platformServiceSources', 'resolving a platform service to its source');
|
|
52
|
+
|
|
53
|
+
/** Every declared platform service, sorted by name. */
|
|
54
|
+
export const platformServiceSourceDeclarations = () =>
|
|
55
|
+
declarations()
|
|
56
|
+
.map((declaration) =>
|
|
57
|
+
Object.freeze({
|
|
58
|
+
...declaration,
|
|
59
|
+
/** Aggregate-workspace-relative manifest path, the lock's `source.path`. */
|
|
60
|
+
path: `${declaration.repo}/${declaration.manifestPath}`,
|
|
61
|
+
}),
|
|
62
|
+
)
|
|
63
|
+
.sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The declaration for one platform service. Fails fast and names the service —
|
|
67
|
+
* source resolution never falls back to a guessed location.
|
|
68
|
+
*/
|
|
69
|
+
export function platformServiceSourceDeclaration(name) {
|
|
70
|
+
const declaration = platformServiceSourceDeclarations().find((entry) => entry.name === name);
|
|
71
|
+
if (!declaration) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`platform service "${name}" has no source declaration in the "platformServiceSources" ` +
|
|
74
|
+
'section of edition-workspace.json; declare its repo, manifest path, ' +
|
|
75
|
+
'manifest name, image key and runtime binding there',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return declaration;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Every source repository a platform service is read from, deduplicated. */
|
|
82
|
+
export const platformServiceSourceRepositories = () => [
|
|
83
|
+
...new Set(platformServiceSourceDeclarations().map((declaration) => declaration.repo)),
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The logical-platform-service → actual-runtime-subject bindings, in the exact
|
|
88
|
+
* shape `runtime-image-source-index.json#platformServiceBindings` carries.
|
|
89
|
+
* DERIVED from the declarations above so the partition has one definition.
|
|
90
|
+
*/
|
|
91
|
+
export const platformServiceRuntimeBindings = () =>
|
|
92
|
+
platformServiceSourceDeclarations()
|
|
93
|
+
.filter((declaration) => declaration.runtimeBinding !== null)
|
|
94
|
+
.map((declaration) =>
|
|
95
|
+
Object.freeze({
|
|
96
|
+
platformService: declaration.name,
|
|
97
|
+
subjectKind: declaration.runtimeBinding.subjectKind,
|
|
98
|
+
subjectId: declaration.runtimeBinding.subjectId,
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/** True when the service's image is produced by some other subject. */
|
|
103
|
+
export function platformServiceHasRuntimeBinding(name) {
|
|
104
|
+
return platformServiceSourceDeclaration(name).runtimeBinding !== null;
|
|
105
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// positive-closure — the packaging plane's projection onto the kernel's ONE
|
|
2
|
+
// closure algorithm (`resolveBiomeClosure`, `@xemahq/contracts/distribution`).
|
|
3
|
+
//
|
|
4
|
+
// The KERNEL owns the rule; this module owns only the projection of THIS
|
|
5
|
+
// plane's facts onto it — biome-index entries and lock entries become
|
|
6
|
+
// `ClosureBiomeFacts`, and a lock's `resolution` section becomes a `why`
|
|
7
|
+
// answer. Two callers use the rule:
|
|
8
|
+
//
|
|
9
|
+
// * an EDITION (`resolve-distribution.mjs`, `selectByPositiveClosure`) — its
|
|
10
|
+
// declared roots against the biome catalogue, starting from nothing;
|
|
11
|
+
// * a RUNTIME INSTALLATION (`runtimeInstallationClosure` below) — one
|
|
12
|
+
// org-installed biome against what its base edition already PROVIDES (the
|
|
13
|
+
// sealed lock's roster) and PERMITS (the candidates the caller admits).
|
|
14
|
+
//
|
|
15
|
+
// Neither re-implements any part of the closure; a second closure here would be
|
|
16
|
+
// a rival answer to the question the kernel exists to answer once.
|
|
17
|
+
|
|
18
|
+
/** Project one biome-index entry onto the facts the closure reads. */
|
|
19
|
+
export function closureBiomeFacts(entry) {
|
|
20
|
+
return {
|
|
21
|
+
id: entry.id,
|
|
22
|
+
tier: entry.tier,
|
|
23
|
+
...(entry.installationClass === undefined
|
|
24
|
+
? {}
|
|
25
|
+
: { installationClass: entry.installationClass }),
|
|
26
|
+
...(entry.audience === undefined ? {} : { audience: entry.audience }),
|
|
27
|
+
services: Array.isArray(entry.services) ? entry.services : [],
|
|
28
|
+
...(entry.dependencyContract === undefined
|
|
29
|
+
? {}
|
|
30
|
+
: { dependencyContract: entry.dependencyContract }),
|
|
31
|
+
...(entry.serviceResources === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { serviceResources: entry.serviceResources }),
|
|
34
|
+
...(entry.providesResources === undefined
|
|
35
|
+
? {}
|
|
36
|
+
: { providesResources: entry.providesResources }),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The closure of ONE runtime-installed biome against a sealed base edition.
|
|
42
|
+
*
|
|
43
|
+
* - `baseLock` is the base edition's distribution lock (read with the kernel's
|
|
44
|
+
* tolerant reader by the caller). Its roster is what the base PROVIDES; each
|
|
45
|
+
* rostered biome's facts are taken from `index` by id, because a lock entry
|
|
46
|
+
* carries no dependency contract or resource provisions.
|
|
47
|
+
* - `permitted` is the catalogue the base edition PERMITS an installation to
|
|
48
|
+
* draw on — the caller decides it (store visibility, trust policy, sources);
|
|
49
|
+
* it defaults to every `index` entry the base does not already ship.
|
|
50
|
+
* - The installed biome is a `runtime-installation-root`. A biome the base
|
|
51
|
+
* already ships is never re-added and never traversed.
|
|
52
|
+
*
|
|
53
|
+
* Returns the kernel result unchanged: the entities the installation ADDS, with
|
|
54
|
+
* provenance, and every resource binding of an added biome against added ∪
|
|
55
|
+
* present. Refusals are the kernel's, prefixed with the installation subject.
|
|
56
|
+
*/
|
|
57
|
+
export function runtimeInstallationClosure({
|
|
58
|
+
schemas,
|
|
59
|
+
baseLock,
|
|
60
|
+
index,
|
|
61
|
+
biomeId,
|
|
62
|
+
permitted,
|
|
63
|
+
}) {
|
|
64
|
+
const indexById = new Map(index.map((entry) => [entry.id, entry]));
|
|
65
|
+
const presentIds = new Set(baseLock.biomes.map((biome) => biome.id));
|
|
66
|
+
const presentBiomes = baseLock.biomes.map((biome) => {
|
|
67
|
+
const entry = indexById.get(biome.id);
|
|
68
|
+
if (entry === undefined) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`runtime installation of "${biomeId}": base edition "${baseLock.distributionId}" ships ` +
|
|
71
|
+
`biome "${biome.id}", which the catalogue does not describe — the base's provisions ` +
|
|
72
|
+
'cannot be read, so its satisfaction of an edge cannot be decided',
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return closureBiomeFacts(entry);
|
|
76
|
+
});
|
|
77
|
+
const candidates = (permitted ?? index.filter((entry) => !presentIds.has(entry.id))).map(
|
|
78
|
+
closureBiomeFacts,
|
|
79
|
+
);
|
|
80
|
+
try {
|
|
81
|
+
return schemas.resolveBiomeClosure({
|
|
82
|
+
roots: [
|
|
83
|
+
{
|
|
84
|
+
entity: { kind: schemas.ClosureEntityKind.Biome, id: biomeId },
|
|
85
|
+
provenance: {
|
|
86
|
+
kind: schemas.ClosureProvenanceKind.RuntimeInstallationRoot,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
candidates: { biomes: candidates, platformServices: [] },
|
|
91
|
+
present: {
|
|
92
|
+
biomes: presentBiomes,
|
|
93
|
+
platformServices: baseLock.platformServices.map(({ name, tier }) => ({
|
|
94
|
+
name,
|
|
95
|
+
tier,
|
|
96
|
+
})),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error instanceof schemas.BiomeClosureError) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`runtime installation of "${biomeId}" into base edition "${baseLock.distributionId}": ${error.message}`,
|
|
103
|
+
{ cause: error },
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* `why <name>` — the deterministic explanation of one entity's presence in a
|
|
112
|
+
* lock, rendered from the lock's `resolution` section alone. Returns the lines
|
|
113
|
+
* to print. A biome id is looked up first, then a platform-service name; a
|
|
114
|
+
* selector-model lock records no provenance and says so rather than guessing.
|
|
115
|
+
*/
|
|
116
|
+
export function explainInclusion(schemas, lock, name) {
|
|
117
|
+
if (lock.resolution === undefined) {
|
|
118
|
+
return {
|
|
119
|
+
found: false,
|
|
120
|
+
lines: [
|
|
121
|
+
`${lock.distributionId}: this edition resolves by the selector model ` +
|
|
122
|
+
'(include/exclude); its lock records no provenance. Opt the edition into ' +
|
|
123
|
+
'`resolution: "positive-closure"` to make `why` answerable.',
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const entry =
|
|
128
|
+
lock.resolution.entries.find(
|
|
129
|
+
(candidate) =>
|
|
130
|
+
candidate.entity.kind === schemas.ClosureEntityKind.Biome &&
|
|
131
|
+
candidate.entity.id === name,
|
|
132
|
+
) ??
|
|
133
|
+
lock.resolution.entries.find(
|
|
134
|
+
(candidate) =>
|
|
135
|
+
candidate.entity.kind === schemas.ClosureEntityKind.PlatformService &&
|
|
136
|
+
candidate.entity.id === name,
|
|
137
|
+
);
|
|
138
|
+
if (entry === undefined) {
|
|
139
|
+
return {
|
|
140
|
+
found: false,
|
|
141
|
+
lines: [
|
|
142
|
+
`${lock.distributionId}: "${name}" is not in this edition — it is not a root, no ` +
|
|
143
|
+
'operation root names it, and no dependency or reached operation of anything in ' +
|
|
144
|
+
'the edition requires it.',
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const lines = [`${lock.distributionId}: ${entry.entity.kind} "${entry.entity.id}" is in because:`];
|
|
149
|
+
for (const provenance of entry.provenance) {
|
|
150
|
+
if (provenance.chain === undefined) {
|
|
151
|
+
lines.push(` - ${schemas.describeProvenance(provenance)}`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
lines.push(
|
|
155
|
+
` - ${provenance.kind}, from root ${provenance.root.kind} "${provenance.root.id}":`,
|
|
156
|
+
);
|
|
157
|
+
provenance.chain.forEach((hop, step) => {
|
|
158
|
+
lines.push(` ${step + 1}. ${schemas.describeHop(hop)}`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return { found: true, lines };
|
|
162
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The distribution completeness statement and the evidence set it seals.
|
|
3
|
+
//
|
|
4
|
+
// xema edition evidence statement completeness stage … (the bytes to sign)
|
|
5
|
+
// xema edition evidence assemble-set … (evidence-set.json)
|
|
6
|
+
//
|
|
7
|
+
// `stage` resolves the edition's producer evidence into a NEW staging
|
|
8
|
+
// directory, writes the canonical completeness statement there and prints the
|
|
9
|
+
// signing plan. The caller signs it (`cosign attest-blob`). `assemble-set`
|
|
10
|
+
// re-derives everything from the same flags, refuses staged bytes that differ,
|
|
11
|
+
// verifies the signed bundle with cosign against the declared signer, writes
|
|
12
|
+
// `evidence-set.json` and seals the evidence root (built from the re-derived
|
|
13
|
+
// files, never renamed from the staging).
|
|
14
|
+
// This module never signs and never requests an OIDC token.
|
|
15
|
+
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
acceptSignedEvidenceStatement,
|
|
21
|
+
assertPinnedCosignVersion,
|
|
22
|
+
buildDistributionImageEvidenceSet,
|
|
23
|
+
buildDistributionImagesStatement,
|
|
24
|
+
loadPinnedTrustPolicy,
|
|
25
|
+
parseEvidenceCliFlags,
|
|
26
|
+
resolveProducerEvidence,
|
|
27
|
+
stageEvidenceStatement,
|
|
28
|
+
stageProducerEvidence,
|
|
29
|
+
stagedProducerFiles,
|
|
30
|
+
} from './distribution-evidence-set.mjs';
|
|
31
|
+
import {
|
|
32
|
+
WorkspaceRoot,
|
|
33
|
+
configureEditionWorkspaceFromArgv,
|
|
34
|
+
dataRoot,
|
|
35
|
+
editionsRoot,
|
|
36
|
+
isEntryModule,
|
|
37
|
+
} from './edition-workspace.mjs';
|
|
38
|
+
import { canonicalJsonBytes } from './image-evidence.mjs';
|
|
39
|
+
import { serializeJson } from './lib.mjs';
|
|
40
|
+
import {
|
|
41
|
+
assertGitHubProducerContext,
|
|
42
|
+
createCosignVerifier,
|
|
43
|
+
loadProducerKernelSchemas,
|
|
44
|
+
} from './produce-image-source-evidence.mjs';
|
|
45
|
+
import { assertStagedExactly, discardStaging, reserveSealing, sealAccepted } from './sealed-evidence-accept.mjs';
|
|
46
|
+
|
|
47
|
+
const EVIDENCE_SET_FILE = 'evidence-set.json';
|
|
48
|
+
const COMPLETENESS_STATEMENT_PATH = 'completeness/statement.json';
|
|
49
|
+
const COMPLETENESS_BUNDLE_PATH = 'completeness/bundle.sigstore.json';
|
|
50
|
+
|
|
51
|
+
/** The keyless signer a GitHub workflow is: its certificate identity and issuer. */
|
|
52
|
+
export function githubWorkflowSigner({ repository, ref, path }) {
|
|
53
|
+
return {
|
|
54
|
+
certificateIdentity: `https://github.com/${repository}/${path}@${ref}`,
|
|
55
|
+
oidcIssuer: 'https://token.actions.githubusercontent.com',
|
|
56
|
+
githubWorkflow: { repository, ref, path },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The producer evidence and the completeness statement these inputs resolve to; deterministic. */
|
|
61
|
+
async function deriveCompleteness(options, { outputRoot }) {
|
|
62
|
+
const resolution = {
|
|
63
|
+
distributionId: options.distributionId,
|
|
64
|
+
dataRoot: options.dataRoot,
|
|
65
|
+
editionsRoot: options.editionsRoot,
|
|
66
|
+
schemas: options.schemas,
|
|
67
|
+
expectedContractAuthority: options.expectedContractAuthority,
|
|
68
|
+
registryClientFactory: options.registryClientFactory,
|
|
69
|
+
};
|
|
70
|
+
const staged = outputRoot === null
|
|
71
|
+
? await resolveProducerEvidence(resolution)
|
|
72
|
+
: await stageProducerEvidence({ ...resolution, outputRoot });
|
|
73
|
+
const completenessStatement = buildDistributionImagesStatement({
|
|
74
|
+
distributionLock: staged.distributionLock,
|
|
75
|
+
imageLock: staged.imageLock,
|
|
76
|
+
images: staged.images,
|
|
77
|
+
releaseSequence: options.releaseSequence,
|
|
78
|
+
releaseSourceCommit: options.releaseSourceCommit,
|
|
79
|
+
runId: options.runId,
|
|
80
|
+
runAttempt: options.runAttempt,
|
|
81
|
+
issuedAt: options.issuedAt,
|
|
82
|
+
expiresAt: options.expiresAt,
|
|
83
|
+
maxTtlSeconds: options.policy.maxCompletenessTtlSeconds,
|
|
84
|
+
schemas: options.schemas,
|
|
85
|
+
});
|
|
86
|
+
return { staged, completenessStatement };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function signingArguments(options, statement) {
|
|
90
|
+
return {
|
|
91
|
+
statement,
|
|
92
|
+
root: options.stagingDirectory,
|
|
93
|
+
statementPath: COMPLETENESS_STATEMENT_PATH,
|
|
94
|
+
bundlePath: COMPLETENESS_BUNDLE_PATH,
|
|
95
|
+
signer: options.signer,
|
|
96
|
+
certificateWorkflowSha: options.releaseSourceCommit,
|
|
97
|
+
predicateType: options.schemas.DISTRIBUTION_IMAGES_PREDICATE_TYPE,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Stage the producer evidence of one edition and its completeness statement
|
|
103
|
+
* for signing. Returns the signing plan and the statement.
|
|
104
|
+
*/
|
|
105
|
+
export async function stageDistributionCompletenessEvidence(options) {
|
|
106
|
+
const { stagingDirectory } = options;
|
|
107
|
+
if (typeof stagingDirectory !== 'string' || !isAbsolute(stagingDirectory)) {
|
|
108
|
+
throw new Error('the staging directory must be an absolute path');
|
|
109
|
+
}
|
|
110
|
+
if (existsSync(stagingDirectory)) {
|
|
111
|
+
throw new Error(`evidence staging directory already exists: ${stagingDirectory}`);
|
|
112
|
+
}
|
|
113
|
+
assertGitHubProducerContext(
|
|
114
|
+
{ signer: options.signer, producerWorkflowSha: options.releaseSourceCommit },
|
|
115
|
+
options.githubContext,
|
|
116
|
+
);
|
|
117
|
+
mkdirSync(stagingDirectory, { recursive: true, mode: 0o700 });
|
|
118
|
+
try {
|
|
119
|
+
const { completenessStatement } = await deriveCompleteness(options, { outputRoot: options.stagingDirectory });
|
|
120
|
+
const plan = stageEvidenceStatement(signingArguments(options, completenessStatement));
|
|
121
|
+
return {
|
|
122
|
+
plan: { ...plan, kind: 'completeness', distributionId: options.distributionId },
|
|
123
|
+
completenessStatement,
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
rmSync(stagingDirectory, { recursive: true, force: true });
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Accept the signed completeness bundle of a staged directory, write
|
|
133
|
+
* `evidence-set.json` and seal `evidenceRoot`.
|
|
134
|
+
*
|
|
135
|
+
* `evidenceRoot` is BUILT, never renamed from the staging: the producer
|
|
136
|
+
* evidence is re-resolved from the registry and must be byte-identical to what
|
|
137
|
+
* was staged, the completeness statement is re-derived and the signed bundle
|
|
138
|
+
* canonicalized and verified, and only those derived files are written. The
|
|
139
|
+
* staging must hold exactly what stage recorded and does not outlive the
|
|
140
|
+
* accept (see `sealed-evidence-accept.mjs`).
|
|
141
|
+
*/
|
|
142
|
+
export async function assembleDistributionEvidenceSet(options) {
|
|
143
|
+
const { evidenceRoot, stagingDirectory, readCosignVersion, runCosign } = options;
|
|
144
|
+
if (
|
|
145
|
+
typeof evidenceRoot !== 'string' ||
|
|
146
|
+
!isAbsolute(evidenceRoot) ||
|
|
147
|
+
typeof stagingDirectory !== 'string' ||
|
|
148
|
+
!isAbsolute(stagingDirectory)
|
|
149
|
+
) {
|
|
150
|
+
throw new Error('the evidence root and the staging directory must be absolute paths');
|
|
151
|
+
}
|
|
152
|
+
if (typeof readCosignVersion !== 'function' || typeof runCosign !== 'function') {
|
|
153
|
+
throw new Error('cosign seams are required');
|
|
154
|
+
}
|
|
155
|
+
const sealing = reserveSealing(evidenceRoot, 'evidence');
|
|
156
|
+
let derived;
|
|
157
|
+
try {
|
|
158
|
+
assertGitHubProducerContext(
|
|
159
|
+
{ signer: options.signer, producerWorkflowSha: options.releaseSourceCommit },
|
|
160
|
+
options.githubContext,
|
|
161
|
+
);
|
|
162
|
+
assertPinnedCosignVersion(readCosignVersion(), options.policy?.cosignVersion);
|
|
163
|
+
derived = await deriveCompleteness(options, { outputRoot: null });
|
|
164
|
+
} catch (error) {
|
|
165
|
+
discardStaging(stagingDirectory);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
const { staged, completenessStatement } = derived;
|
|
169
|
+
return sealAccepted({
|
|
170
|
+
stagingDirectory,
|
|
171
|
+
sealing,
|
|
172
|
+
outputDirectory: evidenceRoot,
|
|
173
|
+
build: () => {
|
|
174
|
+
assertStagedExactly(stagingDirectory, [
|
|
175
|
+
...staged.producers.flatMap((producer) => stagedProducerFiles(producer).map(([path]) => path)),
|
|
176
|
+
COMPLETENESS_STATEMENT_PATH,
|
|
177
|
+
COMPLETENESS_BUNDLE_PATH,
|
|
178
|
+
]);
|
|
179
|
+
for (const producer of staged.producers) {
|
|
180
|
+
for (const [path, bytes] of stagedProducerFiles(producer)) {
|
|
181
|
+
if (!readFileSync(join(stagingDirectory, path)).equals(bytes)) {
|
|
182
|
+
throw new Error(`staged ${path} is not the producer evidence this edition resolves to`);
|
|
183
|
+
}
|
|
184
|
+
mkdirSync(dirname(join(sealing, path)), { recursive: true, mode: 0o755 });
|
|
185
|
+
writeFileSync(join(sealing, path), bytes, { flag: 'wx', mode: 0o644 });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const completeness = acceptSignedEvidenceStatement({
|
|
189
|
+
...signingArguments(options, completenessStatement),
|
|
190
|
+
sealingRoot: sealing,
|
|
191
|
+
runCosign,
|
|
192
|
+
});
|
|
193
|
+
const evidenceSet = buildDistributionImageEvidenceSet({
|
|
194
|
+
distributionId: options.distributionId,
|
|
195
|
+
producerEvidence: staged.producerEvidence,
|
|
196
|
+
completeness,
|
|
197
|
+
schemas: options.schemas,
|
|
198
|
+
});
|
|
199
|
+
writeFileSync(join(sealing, EVIDENCE_SET_FILE), canonicalJsonBytes(evidenceSet), {
|
|
200
|
+
flag: 'wx',
|
|
201
|
+
mode: 0o644,
|
|
202
|
+
});
|
|
203
|
+
return {
|
|
204
|
+
evidenceRoot,
|
|
205
|
+
evidenceSet,
|
|
206
|
+
completeness,
|
|
207
|
+
completenessStatement,
|
|
208
|
+
completenessStatementDigest: completeness.statementDigest,
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function requireInteger(raw, label, { positive }) {
|
|
215
|
+
const value = Number(raw);
|
|
216
|
+
if (!/^\d+$/.test(String(raw)) || !Number.isSafeInteger(value) || (positive && value <= 0)) {
|
|
217
|
+
throw new Error(`--${label} must be a ${positive ? 'positive' : 'non-negative'} integer`);
|
|
218
|
+
}
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const SHARED_FLAGS = Object.freeze([
|
|
223
|
+
'distribution',
|
|
224
|
+
'staging',
|
|
225
|
+
'release-sequence',
|
|
226
|
+
'release-source-commit',
|
|
227
|
+
'run-id',
|
|
228
|
+
'run-attempt',
|
|
229
|
+
'issued-at',
|
|
230
|
+
'expires-at',
|
|
231
|
+
'signer-repository',
|
|
232
|
+
'signer-ref',
|
|
233
|
+
'signer-workflow-path',
|
|
234
|
+
'github-repository',
|
|
235
|
+
'github-ref',
|
|
236
|
+
'github-sha',
|
|
237
|
+
'github-workflow-ref',
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
async function main() {
|
|
241
|
+
const [phase, ...args] = configureEditionWorkspaceFromArgv(process.argv.slice(2), [
|
|
242
|
+
WorkspaceRoot.Data,
|
|
243
|
+
WorkspaceRoot.Editions,
|
|
244
|
+
WorkspaceRoot.Kernel,
|
|
245
|
+
]);
|
|
246
|
+
if (phase !== 'stage' && phase !== 'accept') {
|
|
247
|
+
throw new Error(
|
|
248
|
+
'usage: xema edition evidence statement completeness stage … | xema edition evidence assemble-set …',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const flags = parseEvidenceCliFlags(args, {
|
|
252
|
+
single: phase === 'stage' ? SHARED_FLAGS : [...SHARED_FLAGS, 'evidence-root', 'cosign'],
|
|
253
|
+
});
|
|
254
|
+
const { schemas, contractAuthority } = await loadProducerKernelSchemas({ dataRoot: dataRoot() });
|
|
255
|
+
const options = {
|
|
256
|
+
distributionId: flags.require('distribution'),
|
|
257
|
+
dataRoot: dataRoot(),
|
|
258
|
+
editionsRoot: editionsRoot(),
|
|
259
|
+
stagingDirectory: resolve(flags.require('staging')),
|
|
260
|
+
schemas,
|
|
261
|
+
expectedContractAuthority: contractAuthority,
|
|
262
|
+
policy: loadPinnedTrustPolicy({ dataRoot: dataRoot(), schemas }),
|
|
263
|
+
signer: githubWorkflowSigner({
|
|
264
|
+
repository: flags.require('signer-repository'),
|
|
265
|
+
ref: flags.require('signer-ref'),
|
|
266
|
+
path: flags.require('signer-workflow-path'),
|
|
267
|
+
}),
|
|
268
|
+
githubContext: {
|
|
269
|
+
repository: flags.require('github-repository'),
|
|
270
|
+
ref: flags.require('github-ref'),
|
|
271
|
+
sha: flags.require('github-sha'),
|
|
272
|
+
workflowRef: flags.require('github-workflow-ref'),
|
|
273
|
+
},
|
|
274
|
+
releaseSequence: requireInteger(flags.require('release-sequence'), 'release-sequence', {
|
|
275
|
+
positive: false,
|
|
276
|
+
}),
|
|
277
|
+
releaseSourceCommit: flags.require('release-source-commit'),
|
|
278
|
+
runId: flags.require('run-id'),
|
|
279
|
+
runAttempt: requireInteger(flags.require('run-attempt'), 'run-attempt', { positive: false }),
|
|
280
|
+
issuedAt: flags.require('issued-at'),
|
|
281
|
+
expiresAt: flags.require('expires-at'),
|
|
282
|
+
};
|
|
283
|
+
if (phase === 'stage') {
|
|
284
|
+
const { plan } = await stageDistributionCompletenessEvidence(options);
|
|
285
|
+
process.stdout.write(serializeJson(plan));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const result = await assembleDistributionEvidenceSet({
|
|
289
|
+
...options,
|
|
290
|
+
evidenceRoot: resolve(flags.require('evidence-root')),
|
|
291
|
+
...createCosignVerifier(resolve(flags.require('cosign'))),
|
|
292
|
+
});
|
|
293
|
+
process.stdout.write(
|
|
294
|
+
serializeJson({
|
|
295
|
+
schemaVersion: 1,
|
|
296
|
+
kind: 'evidence-set',
|
|
297
|
+
distributionId: options.distributionId,
|
|
298
|
+
evidenceRoot: result.evidenceRoot,
|
|
299
|
+
evidenceSetPath: EVIDENCE_SET_FILE,
|
|
300
|
+
producerBundles: result.evidenceSet.producerEvidence.length,
|
|
301
|
+
completenessStatementDigest: result.completenessStatementDigest,
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (isEntryModule(import.meta.url)) {
|
|
307
|
+
main().catch((error) => {
|
|
308
|
+
console.error(`[distribution-completeness-evidence] ${error.message}`);
|
|
309
|
+
process.exitCode = 1;
|
|
310
|
+
});
|
|
311
|
+
}
|