@xemahq/xema 0.59.1 → 0.60.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.
- package/README.md +1 -2
- package/dist/appliance-owner.d.ts.map +1 -1
- package/dist/appliance-owner.js +6 -6
- package/dist/appliance-owner.js.map +1 -1
- package/dist/assets/appliance-base/authorization.lock.json +5186 -0
- package/dist/assets/appliance-base/distribution.lock.json +760 -279
- package/dist/assets/appliance-base/images.lock.json +576 -513
- package/dist/assets/appliance-lean/authorization.lock.json +4648 -0
- package/dist/assets/appliance-lean/distribution.lock.json +641 -186
- package/dist/assets/appliance-lean/images.lock.json +574 -512
- package/dist/assets/boot-graph.json +33 -14
- package/dist/assets/distribution-assets.manifest.json +23 -20
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +7 -9
- package/dist/cli.js.map +1 -1
- package/dist/commands/biome-dev.d.ts.map +1 -1
- package/dist/commands/biome-dev.js +41 -137
- package/dist/commands/biome-dev.js.map +1 -1
- package/dist/commands/biome-lifecycle.d.ts +24 -0
- package/dist/commands/biome-lifecycle.d.ts.map +1 -0
- package/dist/commands/biome-lifecycle.js +253 -0
- package/dist/commands/biome-lifecycle.js.map +1 -0
- package/dist/commands/biome.d.ts +2 -4
- package/dist/commands/biome.d.ts.map +1 -1
- package/dist/commands/biome.js +61 -46
- package/dist/commands/biome.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +96 -8
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +13 -20
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +14 -21
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +6 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/installation-tenancy.d.ts +11 -0
- package/dist/commands/installation-tenancy.d.ts.map +1 -0
- package/dist/commands/installation-tenancy.js +308 -0
- package/dist/commands/installation-tenancy.js.map +1 -0
- package/dist/commands/installation.d.ts.map +1 -1
- package/dist/commands/installation.js +19 -5
- package/dist/commands/installation.js.map +1 -1
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +41 -8
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/{dev.d.ts → mirror.d.ts} +1 -1
- package/dist/commands/mirror.d.ts.map +1 -0
- package/dist/commands/mirror.js +132 -0
- package/dist/commands/mirror.js.map +1 -0
- package/dist/commands/source.d.ts.map +1 -1
- package/dist/commands/source.js +3 -2
- package/dist/commands/source.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +40 -21
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +36 -4
- package/dist/commands/up.js.map +1 -1
- package/dist/dev/infra-preflight.d.ts.map +1 -1
- package/dist/dev/infra-preflight.js +0 -4
- package/dist/dev/infra-preflight.js.map +1 -1
- package/dist/lib/confirm.d.ts +3 -0
- package/dist/lib/confirm.d.ts.map +1 -0
- package/dist/lib/confirm.js +19 -0
- package/dist/lib/confirm.js.map +1 -0
- package/dist/lib/output-format.d.ts +6 -0
- package/dist/lib/output-format.d.ts.map +1 -0
- package/dist/lib/output-format.js +20 -0
- package/dist/lib/output-format.js.map +1 -0
- package/dist/mirror/config/mirror-config-document.d.ts +18 -0
- package/dist/mirror/config/mirror-config-document.d.ts.map +1 -0
- package/dist/mirror/config/mirror-config-document.js +57 -0
- package/dist/mirror/config/mirror-config-document.js.map +1 -0
- package/dist/mirror/config/mirror-config-schema.d.ts +54 -0
- package/dist/mirror/config/mirror-config-schema.d.ts.map +1 -0
- package/dist/mirror/config/mirror-config-schema.js +80 -0
- package/dist/mirror/config/mirror-config-schema.js.map +1 -0
- package/dist/mirror/kit/delta.d.ts +4 -0
- package/dist/mirror/kit/delta.d.ts.map +1 -0
- package/dist/mirror/kit/delta.js +41 -0
- package/dist/mirror/kit/delta.js.map +1 -0
- package/dist/mirror/kit/export-kit.d.ts +16 -0
- package/dist/mirror/kit/export-kit.d.ts.map +1 -0
- package/dist/mirror/kit/export-kit.js +112 -0
- package/dist/mirror/kit/export-kit.js.map +1 -0
- package/dist/mirror/kit/import-kit.d.ts +9 -0
- package/dist/mirror/kit/import-kit.d.ts.map +1 -0
- package/dist/mirror/kit/import-kit.js +166 -0
- package/dist/mirror/kit/import-kit.js.map +1 -0
- package/dist/mirror/kit/kit-archive.d.ts +4 -0
- package/dist/mirror/kit/kit-archive.d.ts.map +1 -0
- package/dist/mirror/kit/kit-archive.js +38 -0
- package/dist/mirror/kit/kit-archive.js.map +1 -0
- package/dist/mirror/kit/kit-layout.d.ts +10 -0
- package/dist/mirror/kit/kit-layout.d.ts.map +1 -0
- package/dist/mirror/kit/kit-layout.js +19 -0
- package/dist/mirror/kit/kit-layout.js.map +1 -0
- package/dist/mirror/kit/kit-sums.d.ts +5 -0
- package/dist/mirror/kit/kit-sums.d.ts.map +1 -0
- package/dist/mirror/kit/kit-sums.js +63 -0
- package/dist/mirror/kit/kit-sums.js.map +1 -0
- package/dist/mirror/kit/layout-sync.d.ts +8 -0
- package/dist/mirror/kit/layout-sync.d.ts.map +1 -0
- package/dist/mirror/kit/layout-sync.js +58 -0
- package/dist/mirror/kit/layout-sync.js.map +1 -0
- package/dist/mirror/plan/bundle-content.d.ts +10 -0
- package/dist/mirror/plan/bundle-content.d.ts.map +1 -0
- package/dist/mirror/plan/bundle-content.js +61 -0
- package/dist/mirror/plan/bundle-content.js.map +1 -0
- package/dist/mirror/plan/declaration-file.d.ts +9 -0
- package/dist/mirror/plan/declaration-file.d.ts.map +1 -0
- package/dist/mirror/plan/declaration-file.js +27 -0
- package/dist/mirror/plan/declaration-file.js.map +1 -0
- package/dist/mirror/plan/edition-inputs.d.ts +57 -0
- package/dist/mirror/plan/edition-inputs.d.ts.map +1 -0
- package/dist/mirror/plan/edition-inputs.js +44 -0
- package/dist/mirror/plan/edition-inputs.js.map +1 -0
- package/dist/mirror/plan/edition-reader.d.ts +9 -0
- package/dist/mirror/plan/edition-reader.d.ts.map +1 -0
- package/dist/mirror/plan/edition-reader.js +47 -0
- package/dist/mirror/plan/edition-reader.js.map +1 -0
- package/dist/mirror/plan/image-content.d.ts +30 -0
- package/dist/mirror/plan/image-content.d.ts.map +1 -0
- package/dist/mirror/plan/image-content.js +120 -0
- package/dist/mirror/plan/image-content.js.map +1 -0
- package/dist/mirror/plan/npm-lockfile.d.ts +12 -0
- package/dist/mirror/plan/npm-lockfile.d.ts.map +1 -0
- package/dist/mirror/plan/npm-lockfile.js +106 -0
- package/dist/mirror/plan/npm-lockfile.js.map +1 -0
- package/dist/mirror/plan/oci-reads.d.ts +22 -0
- package/dist/mirror/plan/oci-reads.d.ts.map +1 -0
- package/dist/mirror/plan/oci-reads.js +43 -0
- package/dist/mirror/plan/oci-reads.js.map +1 -0
- package/dist/mirror/plan/plan-command.d.ts +22 -0
- package/dist/mirror/plan/plan-command.d.ts.map +1 -0
- package/dist/mirror/plan/plan-command.js +81 -0
- package/dist/mirror/plan/plan-command.js.map +1 -0
- package/dist/mirror/plan/plan-file.d.ts +13 -0
- package/dist/mirror/plan/plan-file.d.ts.map +1 -0
- package/dist/mirror/plan/plan-file.js +63 -0
- package/dist/mirror/plan/plan-file.js.map +1 -0
- package/dist/mirror/plan/resolve-content-set.d.ts +16 -0
- package/dist/mirror/plan/resolve-content-set.d.ts.map +1 -0
- package/dist/mirror/plan/resolve-content-set.js +90 -0
- package/dist/mirror/plan/resolve-content-set.js.map +1 -0
- package/dist/mirror/preflight/glob.d.ts +2 -0
- package/dist/mirror/preflight/glob.d.ts.map +1 -0
- package/dist/mirror/preflight/glob.js +59 -0
- package/dist/mirror/preflight/glob.js.map +1 -0
- package/dist/mirror/preflight/harbor-api.d.ts +27 -0
- package/dist/mirror/preflight/harbor-api.d.ts.map +1 -0
- package/dist/mirror/preflight/harbor-api.js +39 -0
- package/dist/mirror/preflight/harbor-api.js.map +1 -0
- package/dist/mirror/preflight/preflight.d.ts +18 -0
- package/dist/mirror/preflight/preflight.d.ts.map +1 -0
- package/dist/mirror/preflight/preflight.js +41 -0
- package/dist/mirror/preflight/preflight.js.map +1 -0
- package/dist/mirror/preflight/tag-moves.d.ts +16 -0
- package/dist/mirror/preflight/tag-moves.d.ts.map +1 -0
- package/dist/mirror/preflight/tag-moves.js +28 -0
- package/dist/mirror/preflight/tag-moves.js.map +1 -0
- package/dist/mirror/preflight/write-probe.d.ts +10 -0
- package/dist/mirror/preflight/write-probe.d.ts.map +1 -0
- package/dist/mirror/preflight/write-probe.js +36 -0
- package/dist/mirror/preflight/write-probe.js.map +1 -0
- package/dist/mirror/target/extras.d.ts +16 -0
- package/dist/mirror/target/extras.d.ts.map +1 -0
- package/dist/mirror/target/extras.js +47 -0
- package/dist/mirror/target/extras.js.map +1 -0
- package/dist/mirror/target/npm-registry.d.ts +20 -0
- package/dist/mirror/target/npm-registry.d.ts.map +1 -0
- package/dist/mirror/target/npm-registry.js +67 -0
- package/dist/mirror/target/npm-registry.js.map +1 -0
- package/dist/mirror/target/npm-sync.d.ts +15 -0
- package/dist/mirror/target/npm-sync.d.ts.map +1 -0
- package/dist/mirror/target/npm-sync.js +44 -0
- package/dist/mirror/target/npm-sync.js.map +1 -0
- package/dist/mirror/target/oci-items.d.ts +27 -0
- package/dist/mirror/target/oci-items.d.ts.map +1 -0
- package/dist/mirror/target/oci-items.js +79 -0
- package/dist/mirror/target/oci-items.js.map +1 -0
- package/dist/mirror/target/oci-sync.d.ts +20 -0
- package/dist/mirror/target/oci-sync.d.ts.map +1 -0
- package/dist/mirror/target/oci-sync.js +93 -0
- package/dist/mirror/target/oci-sync.js.map +1 -0
- package/dist/mirror/target/receipt-store.d.ts +13 -0
- package/dist/mirror/target/receipt-store.d.ts.map +1 -0
- package/dist/mirror/target/receipt-store.js +61 -0
- package/dist/mirror/target/receipt-store.js.map +1 -0
- package/dist/mirror/target/receipt.d.ts +13 -0
- package/dist/mirror/target/receipt.d.ts.map +1 -0
- package/dist/mirror/target/receipt.js +25 -0
- package/dist/mirror/target/receipt.js.map +1 -0
- package/dist/mirror/target/seed-push.d.ts +15 -0
- package/dist/mirror/target/seed-push.d.ts.map +1 -0
- package/dist/mirror/target/seed-push.js +20 -0
- package/dist/mirror/target/seed-push.js.map +1 -0
- package/dist/mirror/target/target-access.d.ts +7 -0
- package/dist/mirror/target/target-access.d.ts.map +1 -0
- package/dist/mirror/target/target-access.js +17 -0
- package/dist/mirror/target/target-access.js.map +1 -0
- package/dist/mirror/target/target-commands.d.ts +21 -0
- package/dist/mirror/target/target-commands.d.ts.map +1 -0
- package/dist/mirror/target/target-commands.js +134 -0
- package/dist/mirror/target/target-commands.js.map +1 -0
- package/dist/mirror/target/target-context.d.ts +11 -0
- package/dist/mirror/target/target-context.d.ts.map +1 -0
- package/dist/mirror/target/target-context.js +31 -0
- package/dist/mirror/target/target-context.js.map +1 -0
- package/dist/mirror/target/target-ports.d.ts +18 -0
- package/dist/mirror/target/target-ports.d.ts.map +1 -0
- package/dist/mirror/target/target-ports.js +49 -0
- package/dist/mirror/target/target-ports.js.map +1 -0
- package/dist/mirror/target/target-state.d.ts +22 -0
- package/dist/mirror/target/target-state.d.ts.map +1 -0
- package/dist/mirror/target/target-state.js +41 -0
- package/dist/mirror/target/target-state.js.map +1 -0
- package/dist/runtime/biome-dev.d.ts +0 -16
- package/dist/runtime/biome-dev.d.ts.map +1 -1
- package/dist/runtime/biome-dev.js +0 -53
- package/dist/runtime/biome-dev.js.map +1 -1
- package/dist/runtime/biome-host/biome-directory-packer.d.ts +3 -0
- package/dist/runtime/biome-host/biome-directory-packer.d.ts.map +1 -0
- package/dist/runtime/biome-host/biome-directory-packer.js +12 -0
- package/dist/runtime/biome-host/biome-directory-packer.js.map +1 -0
- package/dist/runtime/biome-host/bundle-packer.d.ts +8 -0
- package/dist/runtime/biome-host/bundle-packer.d.ts.map +1 -0
- package/dist/runtime/biome-host/bundle-packer.js +3 -0
- package/dist/runtime/biome-host/bundle-packer.js.map +1 -0
- package/dist/runtime/biome-host/from-spec.d.ts +33 -0
- package/dist/runtime/biome-host/from-spec.d.ts.map +1 -0
- package/dist/runtime/biome-host/from-spec.js +70 -0
- package/dist/runtime/biome-host/from-spec.js.map +1 -0
- package/dist/runtime/biome-host/lifecycle-client.d.ts +20 -0
- package/dist/runtime/biome-host/lifecycle-client.d.ts.map +1 -0
- package/dist/runtime/biome-host/lifecycle-client.js +197 -0
- package/dist/runtime/biome-host/lifecycle-client.js.map +1 -0
- package/dist/runtime/biome-host/lifecycle-contract.d.ts +95 -0
- package/dist/runtime/biome-host/lifecycle-contract.d.ts.map +1 -0
- package/dist/runtime/biome-host/lifecycle-contract.js +61 -0
- package/dist/runtime/biome-host/lifecycle-contract.js.map +1 -0
- package/dist/runtime/biome-host/lifecycle-flow.d.ts +29 -0
- package/dist/runtime/biome-host/lifecycle-flow.d.ts.map +1 -0
- package/dist/runtime/biome-host/lifecycle-flow.js +106 -0
- package/dist/runtime/biome-host/lifecycle-flow.js.map +1 -0
- package/dist/runtime/biome-host/plan-sources.d.ts +4 -0
- package/dist/runtime/biome-host/plan-sources.d.ts.map +1 -0
- package/dist/runtime/biome-host/plan-sources.js +26 -0
- package/dist/runtime/biome-host/plan-sources.js.map +1 -0
- package/dist/runtime/biome-host/relay/bundle-fetch.d.ts +12 -0
- package/dist/runtime/biome-host/relay/bundle-fetch.d.ts.map +1 -0
- package/dist/runtime/biome-host/relay/bundle-fetch.js +76 -0
- package/dist/runtime/biome-host/relay/bundle-fetch.js.map +1 -0
- package/dist/runtime/biome-host/relay/declared-scheme.d.ts +3 -0
- package/dist/runtime/biome-host/relay/declared-scheme.d.ts.map +1 -0
- package/dist/runtime/biome-host/relay/declared-scheme.js +14 -0
- package/dist/runtime/biome-host/relay/declared-scheme.js.map +1 -0
- package/dist/runtime/biome-host/relay/npm-token.d.ts +4 -0
- package/dist/runtime/biome-host/relay/npm-token.d.ts.map +1 -0
- package/dist/runtime/biome-host/relay/npm-token.js +16 -0
- package/dist/runtime/biome-host/relay/npm-token.js.map +1 -0
- package/dist/runtime/biome-host/relay/relay-flow.d.ts +16 -0
- package/dist/runtime/biome-host/relay/relay-flow.d.ts.map +1 -0
- package/dist/runtime/biome-host/relay/relay-flow.js +48 -0
- package/dist/runtime/biome-host/relay/relay-flow.js.map +1 -0
- package/dist/runtime/biome-host/source-staging.d.ts +17 -0
- package/dist/runtime/biome-host/source-staging.d.ts.map +1 -0
- package/dist/runtime/biome-host/source-staging.js +80 -0
- package/dist/runtime/biome-host/source-staging.js.map +1 -0
- package/dist/runtime/biome-host/sse.d.ts +2 -0
- package/dist/runtime/biome-host/sse.d.ts.map +1 -0
- package/dist/runtime/biome-host/sse.js +33 -0
- package/dist/runtime/biome-host/sse.js.map +1 -0
- package/dist/runtime/biome-local-install.js +1 -1
- package/dist/runtime/biome-local-install.js.map +1 -1
- package/dist/runtime/browser-origins.d.ts +9 -0
- package/dist/runtime/browser-origins.d.ts.map +1 -1
- package/dist/runtime/browser-origins.js +10 -0
- package/dist/runtime/browser-origins.js.map +1 -1
- package/dist/runtime/config/config-document.d.ts +10 -0
- package/dist/runtime/config/config-document.d.ts.map +1 -0
- package/dist/runtime/config/config-document.js +56 -0
- package/dist/runtime/config/config-document.js.map +1 -0
- package/dist/runtime/config/config-migrations.d.ts +21 -0
- package/dist/runtime/config/config-migrations.d.ts.map +1 -0
- package/dist/runtime/config/config-migrations.js +102 -0
- package/dist/runtime/config/config-migrations.js.map +1 -0
- package/dist/runtime/config/config-paths.d.ts +3 -0
- package/dist/runtime/config/config-paths.d.ts.map +1 -0
- package/dist/runtime/config/config-paths.js +9 -0
- package/dist/runtime/config/config-paths.js.map +1 -0
- package/dist/runtime/config/dev-mode.d.ts +0 -1
- package/dist/runtime/config/dev-mode.d.ts.map +1 -1
- package/dist/runtime/config/dev-mode.js +11 -27
- package/dist/runtime/config/dev-mode.js.map +1 -1
- package/dist/runtime/config/init-presets.js +5 -9
- package/dist/runtime/config/init-presets.js.map +1 -1
- package/dist/runtime/config/launch-config-help.d.ts +4 -0
- package/dist/runtime/config/launch-config-help.d.ts.map +1 -0
- package/dist/runtime/config/launch-config-help.js +42 -0
- package/dist/runtime/config/launch-config-help.js.map +1 -0
- package/dist/runtime/config/launch-config.d.ts +60 -0
- package/dist/runtime/config/launch-config.d.ts.map +1 -0
- package/dist/runtime/config/launch-config.js +123 -0
- package/dist/runtime/config/launch-config.js.map +1 -0
- package/dist/runtime/config/launch-schema.d.ts +18 -0
- package/dist/runtime/config/launch-schema.d.ts.map +1 -0
- package/dist/runtime/config/launch-schema.js +31 -0
- package/dist/runtime/config/launch-schema.js.map +1 -0
- package/dist/runtime/config/loader.d.ts.map +1 -1
- package/dist/runtime/config/loader.js +4 -14
- package/dist/runtime/config/loader.js.map +1 -1
- package/dist/runtime/config/schema.d.ts +43 -38
- package/dist/runtime/config/schema.d.ts.map +1 -1
- package/dist/runtime/config/schema.js +17 -46
- package/dist/runtime/config/schema.js.map +1 -1
- package/dist/runtime/config/sources-config.d.ts +0 -2
- package/dist/runtime/config/sources-config.d.ts.map +1 -1
- package/dist/runtime/config/sources-config.js +10 -29
- package/dist/runtime/config/sources-config.js.map +1 -1
- package/dist/runtime/config/versioned-document.d.ts +26 -0
- package/dist/runtime/config/versioned-document.d.ts.map +1 -0
- package/dist/runtime/config/versioned-document.js +87 -0
- package/dist/runtime/config/versioned-document.js.map +1 -0
- package/dist/runtime/directory-changes.d.ts +12 -0
- package/dist/runtime/directory-changes.d.ts.map +1 -0
- package/dist/runtime/directory-changes.js +62 -0
- package/dist/runtime/directory-changes.js.map +1 -0
- package/dist/runtime/foreground.d.ts +2 -0
- package/dist/runtime/foreground.d.ts.map +1 -1
- package/dist/runtime/foreground.js +5 -20
- package/dist/runtime/foreground.js.map +1 -1
- package/dist/runtime/infra/infra-runner.js +5 -5
- package/dist/runtime/infra/infra-runner.js.map +1 -1
- package/dist/runtime/infra/keycloak-realm.d.ts +3 -19
- package/dist/runtime/infra/keycloak-realm.d.ts.map +1 -1
- package/dist/runtime/infra/keycloak-realm.js +12 -133
- package/dist/runtime/infra/keycloak-realm.js.map +1 -1
- package/dist/runtime/installation/current-launcher-renderer.js.map +1 -1
- package/dist/runtime/installation/from-current-config.d.ts.map +1 -1
- package/dist/runtime/installation/from-current-config.js +24 -47
- package/dist/runtime/installation/from-current-config.js.map +1 -1
- package/dist/runtime/installation/index.d.ts +1 -0
- package/dist/runtime/installation/index.d.ts.map +1 -1
- package/dist/runtime/installation/index.js +1 -0
- package/dist/runtime/installation/index.js.map +1 -1
- package/dist/runtime/installation/installation-file.d.ts +3 -0
- package/dist/runtime/installation/installation-file.d.ts.map +1 -0
- package/dist/runtime/installation/installation-file.js +9 -0
- package/dist/runtime/installation/installation-file.js.map +1 -0
- package/dist/runtime/installation/readiness.d.ts +6 -7
- package/dist/runtime/installation/readiness.d.ts.map +1 -1
- package/dist/runtime/installation/readiness.js +82 -132
- package/dist/runtime/installation/readiness.js.map +1 -1
- package/dist/runtime/installation/state-store.d.ts +4 -0
- package/dist/runtime/installation/state-store.d.ts.map +1 -1
- package/dist/runtime/installation/state-store.js +22 -1
- package/dist/runtime/installation/state-store.js.map +1 -1
- package/dist/runtime/installation/to-current-config.d.ts +4 -1
- package/dist/runtime/installation/to-current-config.d.ts.map +1 -1
- package/dist/runtime/installation/to-current-config.js +10 -36
- package/dist/runtime/installation/to-current-config.js.map +1 -1
- package/dist/runtime/launch-plan-format.d.ts.map +1 -1
- package/dist/runtime/launch-plan-format.js +31 -6
- package/dist/runtime/launch-plan-format.js.map +1 -1
- package/dist/runtime/launch-result.d.ts +15 -0
- package/dist/runtime/launch-result.d.ts.map +1 -0
- package/dist/runtime/launch-result.js +3 -0
- package/dist/runtime/launch-result.js.map +1 -0
- package/dist/runtime/launch.d.ts +16 -5
- package/dist/runtime/launch.d.ts.map +1 -1
- package/dist/runtime/launch.js +151 -315
- package/dist/runtime/launch.js.map +1 -1
- package/dist/runtime/license/helm-pull-secret.d.ts +1 -1
- package/dist/runtime/license/helm-pull-secret.d.ts.map +1 -1
- package/dist/runtime/license/helm-pull-secret.js +2 -2
- package/dist/runtime/license/helm-pull-secret.js.map +1 -1
- package/dist/runtime/lifecycle-progress/index.d.ts +9 -0
- package/dist/runtime/lifecycle-progress/index.d.ts.map +1 -0
- package/dist/runtime/lifecycle-progress/index.js +30 -0
- package/dist/runtime/lifecycle-progress/index.js.map +1 -0
- package/dist/runtime/lifecycle-progress/json-renderer.d.ts +3 -0
- package/dist/runtime/lifecycle-progress/json-renderer.d.ts.map +1 -0
- package/dist/runtime/lifecycle-progress/json-renderer.js +27 -0
- package/dist/runtime/lifecycle-progress/json-renderer.js.map +1 -0
- package/dist/runtime/lifecycle-progress/text-renderer.d.ts +5 -0
- package/dist/runtime/lifecycle-progress/text-renderer.d.ts.map +1 -0
- package/dist/runtime/lifecycle-progress/text-renderer.js +83 -0
- package/dist/runtime/lifecycle-progress/text-renderer.js.map +1 -0
- package/dist/runtime/lifecycle-progress/types.d.ts +79 -0
- package/dist/runtime/lifecycle-progress/types.d.ts.map +1 -0
- package/dist/runtime/lifecycle-progress/types.js +32 -0
- package/dist/runtime/lifecycle-progress/types.js.map +1 -0
- package/dist/runtime/lock-loader.d.ts +13 -3
- package/dist/runtime/lock-loader.d.ts.map +1 -1
- package/dist/runtime/lock-loader.js +28 -8
- package/dist/runtime/lock-loader.js.map +1 -1
- package/dist/runtime/paths.d.ts +2 -0
- package/dist/runtime/paths.d.ts.map +1 -1
- package/dist/runtime/paths.js +17 -4
- package/dist/runtime/paths.js.map +1 -1
- package/dist/runtime/roster.d.ts +6 -5
- package/dist/runtime/roster.d.ts.map +1 -1
- package/dist/runtime/roster.js +0 -21
- package/dist/runtime/roster.js.map +1 -1
- package/dist/runtime/runner/container-preflight.d.ts +3 -2
- package/dist/runtime/runner/container-preflight.d.ts.map +1 -1
- package/dist/runtime/runner/container-preflight.js +5 -5
- package/dist/runtime/runner/container-preflight.js.map +1 -1
- package/dist/runtime/runner/container-runner.d.ts +0 -22
- package/dist/runtime/runner/container-runner.d.ts.map +1 -1
- package/dist/runtime/runner/container-runner.js +0 -112
- package/dist/runtime/runner/container-runner.js.map +1 -1
- package/dist/runtime/runner/docker-cli.d.ts +1 -0
- package/dist/runtime/runner/docker-cli.d.ts.map +1 -1
- package/dist/runtime/runner/docker-cli.js +19 -0
- package/dist/runtime/runner/docker-cli.js.map +1 -1
- package/dist/runtime/runner/helm-cli.d.ts +1 -0
- package/dist/runtime/runner/helm-cli.d.ts.map +1 -1
- package/dist/runtime/runner/helm-cli.js +5 -1
- package/dist/runtime/runner/helm-cli.js.map +1 -1
- package/dist/runtime/runner/helm-pre-install.d.ts +3 -2
- package/dist/runtime/runner/helm-pre-install.d.ts.map +1 -1
- package/dist/runtime/runner/helm-pre-install.js +3 -3
- package/dist/runtime/runner/helm-pre-install.js.map +1 -1
- package/dist/runtime/runner/helm-runner.d.ts +29 -4
- package/dist/runtime/runner/helm-runner.d.ts.map +1 -1
- package/dist/runtime/runner/helm-runner.js +44 -14
- package/dist/runtime/runner/helm-runner.js.map +1 -1
- package/dist/runtime/runner/kube-cli.d.ts +1 -0
- package/dist/runtime/runner/kube-cli.d.ts.map +1 -1
- package/dist/runtime/runner/kube-cli.js +24 -2
- package/dist/runtime/runner/kube-cli.js.map +1 -1
- package/dist/runtime/secrets/managed-local-key-provider.d.ts +2 -0
- package/dist/runtime/secrets/managed-local-key-provider.d.ts.map +1 -1
- package/dist/runtime/secrets/managed-local-key-provider.js +17 -8
- package/dist/runtime/secrets/managed-local-key-provider.js.map +1 -1
- package/dist/runtime/seed/closure-container.d.ts +8 -0
- package/dist/runtime/seed/closure-container.d.ts.map +1 -0
- package/dist/runtime/seed/closure-container.js +23 -0
- package/dist/runtime/seed/closure-container.js.map +1 -0
- package/dist/runtime/seed/closure-helm.d.ts +9 -0
- package/dist/runtime/seed/closure-helm.d.ts.map +1 -0
- package/dist/runtime/seed/closure-helm.js +16 -0
- package/dist/runtime/seed/closure-helm.js.map +1 -0
- package/dist/runtime/seed/closure-names.d.ts +4 -0
- package/dist/runtime/seed/closure-names.d.ts.map +1 -0
- package/dist/runtime/seed/closure-names.js +29 -0
- package/dist/runtime/seed/closure-names.js.map +1 -0
- package/dist/runtime/seed/closure-native.d.ts +3 -0
- package/dist/runtime/seed/closure-native.d.ts.map +1 -0
- package/dist/runtime/seed/closure-native.js +18 -0
- package/dist/runtime/seed/closure-native.js.map +1 -0
- package/dist/runtime/seed/installation-tar.d.ts +5 -0
- package/dist/runtime/seed/installation-tar.d.ts.map +1 -0
- package/dist/runtime/seed/installation-tar.js +30 -0
- package/dist/runtime/seed/installation-tar.js.map +1 -0
- package/dist/runtime/seed/launch-env.d.ts +42 -0
- package/dist/runtime/seed/launch-env.d.ts.map +1 -0
- package/dist/runtime/seed/launch-env.js +150 -0
- package/dist/runtime/seed/launch-env.js.map +1 -0
- package/dist/runtime/seed/seed-container.d.ts +13 -0
- package/dist/runtime/seed/seed-container.d.ts.map +1 -0
- package/dist/runtime/seed/seed-container.js +175 -0
- package/dist/runtime/seed/seed-container.js.map +1 -0
- package/dist/runtime/seed/seed-delivery-env.d.ts +39 -0
- package/dist/runtime/seed/seed-delivery-env.d.ts.map +1 -0
- package/dist/runtime/seed/seed-delivery-env.js +47 -0
- package/dist/runtime/seed/seed-delivery-env.js.map +1 -0
- package/dist/runtime/seed/seed-down.d.ts +2 -0
- package/dist/runtime/seed/seed-down.d.ts.map +1 -0
- package/dist/runtime/seed/seed-down.js +52 -0
- package/dist/runtime/seed/seed-down.js.map +1 -0
- package/dist/runtime/seed/seed-env-file.d.ts +3 -0
- package/dist/runtime/seed/seed-env-file.d.ts.map +1 -0
- package/dist/runtime/seed/seed-env-file.js +24 -0
- package/dist/runtime/seed/seed-env-file.js.map +1 -0
- package/dist/runtime/seed/seed-env-secret.d.ts +18 -0
- package/dist/runtime/seed/seed-env-secret.d.ts.map +1 -0
- package/dist/runtime/seed/seed-env-secret.js +62 -0
- package/dist/runtime/seed/seed-env-secret.js.map +1 -0
- package/dist/runtime/seed/seed-environment.d.ts +12 -0
- package/dist/runtime/seed/seed-environment.d.ts.map +1 -0
- package/dist/runtime/seed/seed-environment.js +66 -0
- package/dist/runtime/seed/seed-environment.js.map +1 -0
- package/dist/runtime/seed/seed-facts.d.ts +22 -0
- package/dist/runtime/seed/seed-facts.d.ts.map +1 -0
- package/dist/runtime/seed/seed-facts.js +40 -0
- package/dist/runtime/seed/seed-facts.js.map +1 -0
- package/dist/runtime/seed/seed-helm.d.ts +10 -0
- package/dist/runtime/seed/seed-helm.d.ts.map +1 -0
- package/dist/runtime/seed/seed-helm.js +74 -0
- package/dist/runtime/seed/seed-helm.js.map +1 -0
- package/dist/runtime/seed/seed-image-user.d.ts +6 -0
- package/dist/runtime/seed/seed-image-user.d.ts.map +1 -0
- package/dist/runtime/seed/seed-image-user.js +14 -0
- package/dist/runtime/seed/seed-image-user.js.map +1 -0
- package/dist/runtime/seed/seed-installation-files.d.ts +23 -0
- package/dist/runtime/seed/seed-installation-files.d.ts.map +1 -0
- package/dist/runtime/seed/seed-installation-files.js +51 -0
- package/dist/runtime/seed/seed-installation-files.js.map +1 -0
- package/dist/runtime/seed/seed-installation.d.ts +20 -0
- package/dist/runtime/seed/seed-installation.d.ts.map +1 -0
- package/dist/runtime/seed/seed-installation.js +36 -0
- package/dist/runtime/seed/seed-installation.js.map +1 -0
- package/dist/runtime/seed/seed-launcher.d.ts +14 -0
- package/dist/runtime/seed/seed-launcher.d.ts.map +1 -0
- package/dist/runtime/seed/seed-launcher.js +155 -0
- package/dist/runtime/seed/seed-launcher.js.map +1 -0
- package/dist/runtime/seed/seed-options.d.ts +22 -0
- package/dist/runtime/seed/seed-options.d.ts.map +1 -0
- package/dist/runtime/seed/seed-options.js +57 -0
- package/dist/runtime/seed/seed-options.js.map +1 -0
- package/dist/runtime/seed/seed-platform-endpoints.d.ts +8 -0
- package/dist/runtime/seed/seed-platform-endpoints.d.ts.map +1 -0
- package/dist/runtime/seed/seed-platform-endpoints.js +55 -0
- package/dist/runtime/seed/seed-platform-endpoints.js.map +1 -0
- package/dist/runtime/seed/seed-process.d.ts +22 -0
- package/dist/runtime/seed/seed-process.d.ts.map +1 -0
- package/dist/runtime/seed/seed-process.js +79 -0
- package/dist/runtime/seed/seed-process.js.map +1 -0
- package/dist/runtime/seed/seed-record.d.ts +101 -0
- package/dist/runtime/seed/seed-record.d.ts.map +1 -0
- package/dist/runtime/seed/seed-record.js +141 -0
- package/dist/runtime/seed/seed-record.js.map +1 -0
- package/dist/runtime/seed/seed-resolved-deployment.d.ts +14 -0
- package/dist/runtime/seed/seed-resolved-deployment.d.ts.map +1 -0
- package/dist/runtime/seed/seed-resolved-deployment.js +52 -0
- package/dist/runtime/seed/seed-resolved-deployment.js.map +1 -0
- package/dist/runtime/seed/substrate-facts.d.ts +52 -0
- package/dist/runtime/seed/substrate-facts.d.ts.map +1 -0
- package/dist/runtime/seed/substrate-facts.js +92 -0
- package/dist/runtime/seed/substrate-facts.js.map +1 -0
- package/dist/runtime/seed/supervisor-contract.d.ts +9 -0
- package/dist/runtime/seed/supervisor-contract.d.ts.map +1 -0
- package/dist/runtime/seed/supervisor-contract.js +68 -0
- package/dist/runtime/seed/supervisor-contract.js.map +1 -0
- package/dist/runtime/seed/workload-identity-facts.d.ts +17 -0
- package/dist/runtime/seed/workload-identity-facts.d.ts.map +1 -0
- package/dist/runtime/seed/workload-identity-facts.js +53 -0
- package/dist/runtime/seed/workload-identity-facts.js.map +1 -0
- package/dist/runtime/sources/schema.d.ts +2 -0
- package/dist/runtime/sources/schema.d.ts.map +1 -1
- package/dist/runtime/sources/schema.js +10 -9
- package/dist/runtime/sources/schema.js.map +1 -1
- package/dist/runtime/store-client.d.ts +0 -10
- package/dist/runtime/store-client.d.ts.map +1 -1
- package/dist/runtime/store-client.js +0 -5
- package/dist/runtime/store-client.js.map +1 -1
- package/dist/runtime/tenancy/identity-installer.d.ts +11 -0
- package/dist/runtime/tenancy/identity-installer.d.ts.map +1 -0
- package/dist/runtime/tenancy/identity-installer.js +39 -0
- package/dist/runtime/tenancy/identity-installer.js.map +1 -0
- package/dist/runtime/tenancy/import-cache.d.ts +5 -0
- package/dist/runtime/tenancy/import-cache.d.ts.map +1 -0
- package/dist/runtime/tenancy/import-cache.js +27 -0
- package/dist/runtime/tenancy/import-cache.js.map +1 -0
- package/dist/runtime/tenancy/openfga.d.ts +4 -0
- package/dist/runtime/tenancy/openfga.d.ts.map +1 -0
- package/dist/runtime/tenancy/openfga.js +29 -0
- package/dist/runtime/tenancy/openfga.js.map +1 -0
- package/dist/runtime/tenancy/servers.d.ts +18 -0
- package/dist/runtime/tenancy/servers.d.ts.map +1 -0
- package/dist/runtime/tenancy/servers.js +102 -0
- package/dist/runtime/tenancy/servers.js.map +1 -0
- package/dist/runtime/tenancy/session.d.ts +19 -0
- package/dist/runtime/tenancy/session.d.ts.map +1 -0
- package/dist/runtime/tenancy/session.js +79 -0
- package/dist/runtime/tenancy/session.js.map +1 -0
- package/dist/runtime/tenancy/specs.d.ts +27 -0
- package/dist/runtime/tenancy/specs.d.ts.map +1 -0
- package/dist/runtime/tenancy/specs.js +129 -0
- package/dist/runtime/tenancy/specs.js.map +1 -0
- package/examples/01-local-native.yaml +1 -3
- package/examples/02-local-container-full.yaml +0 -2
- package/examples/03-byo-infra.yaml +1 -5
- package/examples/06-biome-sources.yaml +1 -3
- package/package.json +32 -37
- package/xema.config.example.yaml +1 -10
- package/dist/assets/keycloak/realm-xema.json +0 -397
- package/dist/commands/dev.d.ts.map +0 -1
- package/dist/commands/dev.js +0 -196
- package/dist/commands/dev.js.map +0 -1
- package/dist/commands/edition.d.ts +0 -3
- package/dist/commands/edition.d.ts.map +0 -1
- package/dist/commands/edition.js +0 -54
- package/dist/commands/edition.js.map +0 -1
- package/dist/commands/serve.d.ts +0 -3
- package/dist/commands/serve.d.ts.map +0 -1
- package/dist/commands/serve.js +0 -114
- package/dist/commands/serve.js.map +0 -1
- package/dist/dev/carved-installs.d.ts +0 -3
- package/dist/dev/carved-installs.d.ts.map +0 -1
- package/dist/dev/carved-installs.js +0 -63
- package/dist/dev/carved-installs.js.map +0 -1
- package/dist/dev/env-defaults.d.ts +0 -15
- package/dist/dev/env-defaults.d.ts.map +0 -1
- package/dist/dev/env-defaults.js +0 -178
- package/dist/dev/env-defaults.js.map +0 -1
- package/dist/dev/forked-worker-bootstrap-protocol.d.ts +0 -17
- package/dist/dev/forked-worker-bootstrap-protocol.d.ts.map +0 -1
- package/dist/dev/forked-worker-bootstrap-protocol.js +0 -35
- package/dist/dev/forked-worker-bootstrap-protocol.js.map +0 -1
- package/dist/dev/forked-worker-bootstrap.d.ts +0 -18
- package/dist/dev/forked-worker-bootstrap.d.ts.map +0 -1
- package/dist/dev/forked-worker-bootstrap.js +0 -84
- package/dist/dev/forked-worker-bootstrap.js.map +0 -1
- package/dist/dev/forked-worker.d.ts +0 -43
- package/dist/dev/forked-worker.d.ts.map +0 -1
- package/dist/dev/forked-worker.js +0 -358
- package/dist/dev/forked-worker.js.map +0 -1
- package/dist/edition/verify-image-evidence.bundle.mjs +0 -70456
- package/dist/edition/verify-image-evidence.bundle.provenance.json +0 -3957
- package/dist/runtime/backing-registration.d.ts +0 -11
- package/dist/runtime/backing-registration.d.ts.map +0 -1
- package/dist/runtime/backing-registration.js +0 -94
- package/dist/runtime/backing-registration.js.map +0 -1
- package/dist/runtime/edition/run-entry.d.ts +0 -9
- package/dist/runtime/edition/run-entry.d.ts.map +0 -1
- package/dist/runtime/edition/run-entry.js +0 -36
- package/dist/runtime/edition/run-entry.js.map +0 -1
- package/dist/runtime/edition/verb-map.d.ts +0 -70
- package/dist/runtime/edition/verb-map.d.ts.map +0 -1
- package/dist/runtime/edition/verb-map.js +0 -166
- package/dist/runtime/edition/verb-map.js.map +0 -1
- package/dist/runtime/env-seeder.d.ts +0 -26
- package/dist/runtime/env-seeder.d.ts.map +0 -1
- package/dist/runtime/env-seeder.js +0 -340
- package/dist/runtime/env-seeder.js.map +0 -1
- package/dist/runtime/port-map.d.ts +0 -3
- package/dist/runtime/port-map.d.ts.map +0 -1
- package/dist/runtime/port-map.js +0 -25
- package/dist/runtime/port-map.js.map +0 -1
- package/dist/runtime/run-manifest.d.ts +0 -52
- package/dist/runtime/run-manifest.d.ts.map +0 -1
- package/dist/runtime/run-manifest.js +0 -585
- package/dist/runtime/run-manifest.js.map +0 -1
- package/dist/runtime/runner/native-runner.d.ts +0 -13
- package/dist/runtime/runner/native-runner.d.ts.map +0 -1
- package/dist/runtime/runner/native-runner.js +0 -94
- package/dist/runtime/runner/native-runner.js.map +0 -1
- package/dist/runtime/runner/service-runner.d.ts +0 -28
- package/dist/runtime/runner/service-runner.d.ts.map +0 -1
- package/dist/runtime/runner/service-runner.js +0 -10
- package/dist/runtime/runner/service-runner.js.map +0 -1
- package/dist/runtime/secrets/generated-platform-secrets.d.ts +0 -16
- package/dist/runtime/secrets/generated-platform-secrets.d.ts.map +0 -1
- package/dist/runtime/secrets/generated-platform-secrets.js +0 -115
- package/dist/runtime/secrets/generated-platform-secrets.js.map +0 -1
- package/dist/runtime/secrets/resolver.d.ts +0 -23
- package/dist/runtime/secrets/resolver.d.ts.map +0 -1
- package/dist/runtime/secrets/resolver.js +0 -266
- package/dist/runtime/secrets/resolver.js.map +0 -1
- package/dist/runtime/supervisor.d.ts +0 -67
- package/dist/runtime/supervisor.d.ts.map +0 -1
- package/dist/runtime/supervisor.js +0 -227
- package/dist/runtime/supervisor.js.map +0 -1
- package/edition/README.md +0 -293
- package/edition/adapter-bundled-versions.mjs +0 -108
- package/edition/adapter-host-versions.mjs +0 -41
- package/edition/assemble-producer-input.mjs +0 -1423
- package/edition/assemble-third-party-input.mjs +0 -336
- package/edition/authored-source-check.mjs +0 -135
- package/edition/authorization-base.mjs +0 -357
- package/edition/biome-content.mjs +0 -327
- package/edition/build-biome-index.mjs +0 -180
- package/edition/build-ledger-completeness.mjs +0 -67
- package/edition/build-ledger.mjs +0 -107
- package/edition/build-platform-service-index.mjs +0 -292
- package/edition/build-runtime-image-source-index.mjs +0 -1187
- package/edition/built-verifier.mjs +0 -55
- package/edition/candidate-edition.mjs +0 -697
- package/edition/canonical-evidence-json.mjs +0 -82
- package/edition/canonical-repository-url.mjs +0 -47
- package/edition/catalogue-edition.mjs +0 -49
- package/edition/catalogue-evidence.mjs +0 -228
- package/edition/catalogue-fetch.mjs +0 -124
- package/edition/catalogue-lock-field.mjs +0 -41
- package/edition/catalogue-pin.mjs +0 -71
- package/edition/catalogue.mjs +0 -223
- package/edition/chart-set-source-commit.mjs +0 -97
- package/edition/check-committed-image-evidence.mjs +0 -412
- package/edition/checkout-indexed-source-repositories.mjs +0 -283
- package/edition/collected-refusals.mjs +0 -59
- package/edition/comment-blanker.mjs +0 -75
- package/edition/connector-source-authority.mjs +0 -60
- package/edition/contract-authority.mjs +0 -122
- package/edition/cosign-certificate-constraints.mjs +0 -50
- package/edition/cross-repo-build-inputs.mjs +0 -158
- package/edition/customer-definition.mjs +0 -174
- package/edition/customer-edition-verbs.mjs +0 -617
- package/edition/customer-flags.mjs +0 -15
- package/edition/develop-catalogue-free-resolve.mjs +0 -280
- package/edition/develop-chart-roster.mjs +0 -96
- package/edition/develop-chart-set.mjs +0 -147
- package/edition/develop-coverage-readers.mjs +0 -217
- package/edition/develop-coverage-run.mjs +0 -50
- package/edition/develop-coverage-verdict.mjs +0 -153
- package/edition/develop-coverage.mjs +0 -131
- package/edition/develop-current-resolution.mjs +0 -29
- package/edition/develop-edition-invariants.mjs +0 -142
- package/edition/develop-edition-resolve.mjs +0 -211
- package/edition/develop-edition.mjs +0 -379
- package/edition/develop-engine-image.mjs +0 -95
- package/edition/develop-follow.mjs +0 -491
- package/edition/develop-freeze.mjs +0 -289
- package/edition/develop-git-bundles.mjs +0 -255
- package/edition/develop-graph.mjs +0 -28
- package/edition/develop-image-coordinate.mjs +0 -54
- package/edition/develop-image-probe.mjs +0 -120
- package/edition/develop-image-record.mjs +0 -194
- package/edition/develop-record-ancestry.mjs +0 -144
- package/edition/develop-record-verify.mjs +0 -216
- package/edition/develop-resolve-report.mjs +0 -159
- package/edition/develop-select-records.mjs +0 -172
- package/edition/develop-selection-line.mjs +0 -30
- package/edition/develop-selection-pins.mjs +0 -36
- package/edition/develop-snapshot-inputs.mjs +0 -93
- package/edition/develop-verify-image-record.mjs +0 -109
- package/edition/dispatched-runtime-components.mjs +0 -172
- package/edition/dist-check.mjs +0 -1137
- package/edition/distribution-evidence-set.mjs +0 -1064
- package/edition/distribution-paths.mjs +0 -191
- package/edition/edition-artifact.mjs +0 -529
- package/edition/edition-biome-manifests.mjs +0 -85
- package/edition/edition-ceiling.mjs +0 -180
- package/edition/edition-channel.mjs +0 -20
- package/edition/edition-chart-set.mjs +0 -104
- package/edition/edition-config.mjs +0 -374
- package/edition/edition-constants.mjs +0 -110
- package/edition/edition-contract-parse.mjs +0 -179
- package/edition/edition-contract.json +0 -100
- package/edition/edition-image-seal.mjs +0 -110
- package/edition/edition-kind.mjs +0 -10
- package/edition/edition-list.mjs +0 -35
- package/edition/edition-locks.mjs +0 -79
- package/edition/edition-pin-verbs.mjs +0 -183
- package/edition/edition-pin.mjs +0 -17
- package/edition/edition-pinned-inputs.mjs +0 -116
- package/edition/edition-protocol.mjs +0 -277
- package/edition/edition-publisher.mjs +0 -21
- package/edition/edition-seal.mjs +0 -257
- package/edition/edition-source-pins.mjs +0 -211
- package/edition/edition-unpin.mjs +0 -16
- package/edition/edition-workspace.mjs +0 -204
- package/edition/evidence-checkout.mjs +0 -439
- package/edition/evidence-grammars.mjs +0 -18
- package/edition/evidence-input-mode.mjs +0 -136
- package/edition/evidence-trust-pin.mjs +0 -380
- package/edition/evidence-trust-policy.mjs +0 -374
- package/edition/external-source-walk.mjs +0 -485
- package/edition/first-party-source-ids.mjs +0 -62
- package/edition/fleet-coordinate-finality.mjs +0 -363
- package/edition/fulcio-certificate-extensions.mjs +0 -231
- package/edition/git-json.mjs +0 -54
- package/edition/git-source-auth.mjs +0 -131
- package/edition/image-delivery-entitlement-descriptor.mjs +0 -106
- package/edition/image-evidence-replay-state.mjs +0 -242
- package/edition/image-evidence.mjs +0 -1404
- package/edition/image-lock-service-rows.mjs +0 -26
- package/edition/image-subject-identity.mjs +0 -24
- package/edition/images-from-record.mjs +0 -298
- package/edition/indexes-at-pins.mjs +0 -13
- package/edition/kernel-capability-domain.mjs +0 -30
- package/edition/kernel-contract-authority.mjs +0 -517
- package/edition/kernel-contract-source-path-set.mjs +0 -94
- package/edition/kernel-package.mjs +0 -206
- package/edition/kernel-protocol-choice.mjs +0 -239
- package/edition/kernel-signer-kind.mjs +0 -23
- package/edition/lib.mjs +0 -3303
- package/edition/locked-bundle-signer.mjs +0 -297
- package/edition/map-with-concurrency.mjs +0 -19
- package/edition/no-follow-file.mjs +0 -94
- package/edition/oci-evidence-verbs.mjs +0 -419
- package/edition/oci-image-source-evidence.mjs +0 -364
- package/edition/operation-grant-admission.mjs +0 -285
- package/edition/operation-requirements.mjs +0 -282
- package/edition/oras-process.mjs +0 -35
- package/edition/oras.mjs +0 -742
- package/edition/pin-source.mjs +0 -242
- package/edition/pinned-cosign.mjs +0 -70
- package/edition/plan-image-evidence-refresh.mjs +0 -427
- package/edition/platform-lock-readability.mjs +0 -901
- package/edition/platform-service-class.mjs +0 -85
- package/edition/platform-service-ownership.mjs +0 -88
- package/edition/platform-service-source-authority.mjs +0 -105
- package/edition/positive-closure.mjs +0 -162
- package/edition/previous-seal-registry.mjs +0 -149
- package/edition/produce-distribution-completeness-evidence.mjs +0 -318
- package/edition/produce-edition-catalogue-evidence.mjs +0 -252
- package/edition/produce-image-delivery-entitlement-evidence.mjs +0 -304
- package/edition/produce-image-source-evidence.mjs +0 -788
- package/edition/producer-evidence-bundle.mjs +0 -201
- package/edition/producer-evidence-index-file.mjs +0 -4
- package/edition/record-refused.mjs +0 -12
- package/edition/registry-credentials.mjs +0 -225
- package/edition/release-catalogue.mjs +0 -47
- package/edition/release-channel-guard.mjs +0 -108
- package/edition/release-edition-artifact.mjs +0 -152
- package/edition/release-edition.mjs +0 -54
- package/edition/remedy-command.mjs +0 -89
- package/edition/repin-evidence-trust.mjs +0 -109
- package/edition/required-resource-provider.mjs +0 -58
- package/edition/resolve-chart-set.mjs +0 -376
- package/edition/resolve-customer-edition.mjs +0 -374
- package/edition/resolve-distribution.mjs +0 -2639
- package/edition/resolve-edition.mjs +0 -192
- package/edition/resolve-images.mjs +0 -2351
- package/edition/seal-image-digests.mjs +0 -954
- package/edition/sealed-distribution-lock.mjs +0 -22
- package/edition/sealed-evidence-accept.mjs +0 -82
- package/edition/shallow-checkout.mjs +0 -18
- package/edition/sigstore-bundle-media-type.mjs +0 -4
- package/edition/source-authority.mjs +0 -713
- package/edition/source-closure.mjs +0 -115
- package/edition/store-publisher-trust.mjs +0 -40
- package/edition/strict-json.mjs +0 -121
- package/edition/string-order.mjs +0 -5
- package/edition/third-party-admission.mjs +0 -91
- package/edition/third-party-bundle-registry.mjs +0 -242
- package/edition/third-party-certificate-extensions.mjs +0 -21
- package/edition/third-party-image-pins.mjs +0 -41
- package/edition/third-party-pins-trusted.mjs +0 -24
- package/edition/third-party-producer-evidence.mjs +0 -76
- package/edition/third-party-source-check.mjs +0 -28
- package/edition/third-party-source-context.mjs +0 -41
- package/edition/third-party-source-evidence.mjs +0 -287
- package/edition/third-party-source-flags.mjs +0 -134
- package/edition/third-party-source-pins.mjs +0 -259
- package/edition/third-party-source-uniqueness.mjs +0 -54
- package/edition/third-party-source-walk.mjs +0 -115
- package/edition/third-party-source-writer.mjs +0 -213
- package/edition/third-party-subjects.mjs +0 -128
- package/edition/trust-policy-arming-verb.mjs +0 -60
- package/edition/trust-policy-arming.mjs +0 -144
- package/edition/trust-policy-image-ownership.mjs +0 -85
- package/edition/trusted-executable.mjs +0 -166
- package/edition/verifier-binding.mjs +0 -45
- package/edition/verifier-files.mjs +0 -8
- package/edition/verifier-kernel-schemas.mjs +0 -22
- package/edition/verifier-provenance.mjs +0 -64
- package/edition/verify-catalogue-evidence.mjs +0 -247
- package/edition/verify-image-evidence.mjs +0 -475
- package/edition/verify-image-platforms.mjs +0 -523
- package/edition/why.mjs +0 -56
- package/edition/workspace-sections.mjs +0 -276
|
@@ -1,1404 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { X509Certificate, createPublicKey } from 'node:crypto';
|
|
3
|
-
import {
|
|
4
|
-
mkdtempSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
realpathSync,
|
|
7
|
-
rmSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
} from 'node:fs';
|
|
10
|
-
import { tmpdir } from 'node:os';
|
|
11
|
-
import {
|
|
12
|
-
isAbsolute,
|
|
13
|
-
join,
|
|
14
|
-
relative,
|
|
15
|
-
resolve,
|
|
16
|
-
sep,
|
|
17
|
-
} from 'node:path';
|
|
18
|
-
|
|
19
|
-
import { canonicalJson, canonicalJsonBytes, decodeExactUtf8, parseCanonicalJsonBytes, sha256Digest } from './canonical-evidence-json.mjs';
|
|
20
|
-
import { certificateConstraintArgs } from './cosign-certificate-constraints.mjs';
|
|
21
|
-
import { assertCanonicalDistributionId } from './distribution-paths.mjs';
|
|
22
|
-
import { effectiveRepositoryPins, manifestSourcePins } from './edition-source-pins.mjs';
|
|
23
|
-
import { SHA256_DIGEST_PATTERN } from './evidence-grammars.mjs';
|
|
24
|
-
import { evidenceSubjectKey } from './image-subject-identity.mjs';
|
|
25
|
-
import { assertKernelContractAuthorityEqual } from './kernel-contract-authority.mjs';
|
|
26
|
-
import {
|
|
27
|
-
compareStrings,
|
|
28
|
-
validateImmutableImageLock,
|
|
29
|
-
} from './lib.mjs';
|
|
30
|
-
import { readBoundedNoFollowFile } from './no-follow-file.mjs';
|
|
31
|
-
import {
|
|
32
|
-
assertThirdPartySubjectAdmission,
|
|
33
|
-
thirdPartySourceRepositoryPins,
|
|
34
|
-
thirdPartySourcesByRepository,
|
|
35
|
-
} from './third-party-admission.mjs';
|
|
36
|
-
import { loadThirdPartySourcePins } from './third-party-source-pins.mjs';
|
|
37
|
-
import {
|
|
38
|
-
assertPreparedExecutable,
|
|
39
|
-
exactExecutableOutput,
|
|
40
|
-
prepareDigestPinnedExecutable,
|
|
41
|
-
} from './trusted-executable.mjs';
|
|
42
|
-
|
|
43
|
-
const MAX_CANONICAL_JSON_BYTES = 2 * 1024 * 1024;
|
|
44
|
-
const MAX_STATEMENT_BYTES = 2 * 1024 * 1024;
|
|
45
|
-
const MAX_SIGSTORE_BUNDLE_BYTES = 16 * 1024 * 1024;
|
|
46
|
-
const MAX_TRUSTED_ROOT_BYTES = 8 * 1024 * 1024;
|
|
47
|
-
/** The kernel's `SignerKind.OidcCertificate`: the one signer kind this verifier runs (cosign keyless). */
|
|
48
|
-
const OIDC_CERTIFICATE_SIGNER = 'oidc-certificate';
|
|
49
|
-
export { canonicalJson, canonicalJsonBytes, decodeExactUtf8, parseCanonicalJsonBytes, sha256Digest };
|
|
50
|
-
export const COSIGN_ENV = Object.freeze({
|
|
51
|
-
LANG: 'C',
|
|
52
|
-
LC_ALL: 'C',
|
|
53
|
-
PATH: '/usr/bin:/bin',
|
|
54
|
-
TZ: 'UTC',
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
export function resolveContainedFile(root, candidate, label) {
|
|
59
|
-
if (
|
|
60
|
-
typeof candidate !== 'string' ||
|
|
61
|
-
candidate.length === 0 ||
|
|
62
|
-
isAbsolute(candidate)
|
|
63
|
-
) {
|
|
64
|
-
throw new Error(`${label} must be a repository-relative path`);
|
|
65
|
-
}
|
|
66
|
-
const rootReal = realpathSync(root);
|
|
67
|
-
const path = resolve(rootReal, candidate);
|
|
68
|
-
const pathReal = realpathSync(path);
|
|
69
|
-
const rel = relative(rootReal, pathReal);
|
|
70
|
-
if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
71
|
-
throw new Error(`${label} escapes its evidence root`);
|
|
72
|
-
}
|
|
73
|
-
return pathReal;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function readExactDescriptorFile(root, path, expectedDigest, label, maxBytes) {
|
|
77
|
-
if (!SHA256_DIGEST_PATTERN.test(expectedDigest)) {
|
|
78
|
-
throw new Error(`${label} has an invalid expected sha256 digest`);
|
|
79
|
-
}
|
|
80
|
-
const { path: resolved, bytes } = readBoundedNoFollowFile(
|
|
81
|
-
root,
|
|
82
|
-
path,
|
|
83
|
-
`${label} path`,
|
|
84
|
-
maxBytes,
|
|
85
|
-
);
|
|
86
|
-
const actual = sha256Digest(bytes);
|
|
87
|
-
if (actual !== expectedDigest) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`${label} digest mismatch: expected ${expectedDigest}, got ${actual}`,
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return { path: resolved, bytes };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function readCanonicalJsonFromRoot(
|
|
96
|
-
root,
|
|
97
|
-
path,
|
|
98
|
-
label,
|
|
99
|
-
maxBytes = MAX_CANONICAL_JSON_BYTES,
|
|
100
|
-
) {
|
|
101
|
-
return parseCanonicalJsonBytes(
|
|
102
|
-
readBoundedNoFollowFile(root, path, label, maxBytes).bytes,
|
|
103
|
-
label,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function validatePinnedTrustedRoot(policyRoot, policy) {
|
|
108
|
-
const trustedRootFile = readBoundedNoFollowFile(
|
|
109
|
-
policyRoot,
|
|
110
|
-
policy.trustedRootPath,
|
|
111
|
-
'trusted root',
|
|
112
|
-
MAX_TRUSTED_ROOT_BYTES,
|
|
113
|
-
);
|
|
114
|
-
const { path: trustedRoot, bytes: rootBytes } = trustedRootFile;
|
|
115
|
-
if (sha256Digest(rootBytes) !== policy.trustedRootDigest) {
|
|
116
|
-
throw new Error('pinned Sigstore trusted-root digest mismatch');
|
|
117
|
-
}
|
|
118
|
-
const rootDocument = JSON.parse(
|
|
119
|
-
decodeExactUtf8(rootBytes, 'Sigstore trusted root'),
|
|
120
|
-
);
|
|
121
|
-
if (
|
|
122
|
-
rootDocument?.mediaType !==
|
|
123
|
-
'application/vnd.dev.sigstore.trustedroot+json;version=0.1'
|
|
124
|
-
) {
|
|
125
|
-
throw new Error('Sigstore trusted root has an unsupported mediaType');
|
|
126
|
-
}
|
|
127
|
-
return { path: trustedRoot, bytes: rootBytes };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** The one transparency log a CLASSIC (`.sig` / `.att`) cosign signature is entered in: Rekor v1 of the public good instance. */
|
|
131
|
-
const CLASSIC_REKOR_BASE_URL = 'https://rekor.sigstore.dev';
|
|
132
|
-
const STRICT_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;
|
|
133
|
-
|
|
134
|
-
const pemOf = (label, der) => `-----BEGIN ${label}-----\n${der.toString('base64').match(/.{1,64}/gu).join('\n')}\n-----END ${label}-----\n`;
|
|
135
|
-
const isOpen = (entry) => entry?.validFor?.end === undefined;
|
|
136
|
-
|
|
137
|
-
function derOf(rawBytes, what) {
|
|
138
|
-
if (typeof rawBytes !== 'string' || rawBytes.length === 0 || !STRICT_BASE64.test(rawBytes)) {
|
|
139
|
-
throw new Error(`Sigstore trusted root: ${what} rawBytes is not base64 DER`);
|
|
140
|
-
}
|
|
141
|
-
return Buffer.from(rawBytes, 'base64');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function publicKeyPem(entry, what) {
|
|
145
|
-
const der = derOf(entry?.publicKey?.rawBytes, what);
|
|
146
|
-
try {
|
|
147
|
-
createPublicKey({ key: der, format: 'der', type: 'spki' });
|
|
148
|
-
} catch {
|
|
149
|
-
throw new Error(`Sigstore trusted root: ${what} is not a DER SubjectPublicKeyInfo`);
|
|
150
|
-
}
|
|
151
|
-
return pemOf('PUBLIC KEY', der);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* The classic cosign trust material of the pinned Sigstore trusted root, derived deterministically from its bytes (the
|
|
156
|
-
* output of `validatePinnedTrustedRoot`): what cosign's classic verification reads from `SIGSTORE_ROOT_FILE`,
|
|
157
|
-
* `SIGSTORE_REKOR_PUBLIC_KEY` and `SIGSTORE_CT_LOG_PUBLIC_KEY_FILE` when it cannot take `--trusted-root` (cosign 2.6
|
|
158
|
-
* `verify-attestation` accepts `--trusted-root` only with `--new-bundle-format`, which classic `.att` attestations are
|
|
159
|
-
* not). Only material still open is kept (no `validFor.end`): the Fulcio certificate chains of every open certificate
|
|
160
|
-
* authority (at least one), exactly one open Rekor v1 log (`https://rekor.sigstore.dev`; later logs carry new-format
|
|
161
|
-
* entries only) and exactly one open CT log. Any other count is refused.
|
|
162
|
-
*
|
|
163
|
-
* @returns {{fulcioPem: string, rekorPem: string, ctLogPem: string}}
|
|
164
|
-
*/
|
|
165
|
-
export function classicCosignTrustMaterial(rootBytes) {
|
|
166
|
-
const root = JSON.parse(decodeExactUtf8(rootBytes, 'Sigstore trusted root'));
|
|
167
|
-
const listOf = (key) => (Array.isArray(root?.[key]) ? root[key] : []);
|
|
168
|
-
const authorities = listOf('certificateAuthorities').filter(isOpen);
|
|
169
|
-
if (authorities.length === 0) {
|
|
170
|
-
throw new Error('Sigstore trusted root: no open Fulcio certificate authority (certificateAuthorities without validFor.end)');
|
|
171
|
-
}
|
|
172
|
-
const fulcioPem = authorities
|
|
173
|
-
.flatMap((authority, index) => {
|
|
174
|
-
const certificates = authority?.certChain?.certificates;
|
|
175
|
-
if (!Array.isArray(certificates) || certificates.length === 0) {
|
|
176
|
-
throw new Error(`Sigstore trusted root: open certificate authority ${index} has no certificate chain`);
|
|
177
|
-
}
|
|
178
|
-
return certificates.map((certificate) => {
|
|
179
|
-
const der = derOf(certificate?.rawBytes, `certificate authority ${index} certificate`);
|
|
180
|
-
try {
|
|
181
|
-
new X509Certificate(der);
|
|
182
|
-
} catch {
|
|
183
|
-
throw new Error(`Sigstore trusted root: certificate authority ${index} holds a certificate that is not DER X.509`);
|
|
184
|
-
}
|
|
185
|
-
return pemOf('CERTIFICATE', der);
|
|
186
|
-
});
|
|
187
|
-
})
|
|
188
|
-
.join('');
|
|
189
|
-
const rekor = listOf('tlogs').filter((log) => log?.baseUrl === CLASSIC_REKOR_BASE_URL && isOpen(log?.publicKey));
|
|
190
|
-
if (rekor.length !== 1) {
|
|
191
|
-
throw new Error(`Sigstore trusted root: ${rekor.length} open Rekor v1 logs (${CLASSIC_REKOR_BASE_URL}); exactly one is required`);
|
|
192
|
-
}
|
|
193
|
-
const ctLogs = listOf('ctlogs').filter((log) => isOpen(log?.publicKey));
|
|
194
|
-
if (ctLogs.length !== 1) {
|
|
195
|
-
throw new Error(`Sigstore trusted root: ${ctLogs.length} open CT logs (ctlogs without validFor.end); exactly one is required`);
|
|
196
|
-
}
|
|
197
|
-
return { fulcioPem, rekorPem: publicKeyPem(rekor[0], 'the Rekor v1 log key'), ctLogPem: publicKeyPem(ctLogs[0], 'the CT log key') };
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function assertTimeWindow({ issuedAt, expiresAt, now, maxTtlSeconds, label }) {
|
|
201
|
-
const issued = Date.parse(issuedAt);
|
|
202
|
-
const expires = Date.parse(expiresAt);
|
|
203
|
-
const instant = now instanceof Date ? now.getTime() : Date.parse(now);
|
|
204
|
-
if (![issued, expires, instant].every(Number.isFinite)) {
|
|
205
|
-
throw new Error(`${label} has an invalid time`);
|
|
206
|
-
}
|
|
207
|
-
if (issued > instant) {
|
|
208
|
-
throw new Error(`${label} is not yet valid`);
|
|
209
|
-
}
|
|
210
|
-
if (expires <= instant) {
|
|
211
|
-
throw new Error(`${label} is expired`);
|
|
212
|
-
}
|
|
213
|
-
assertProducerTtlCeiling({ issuedAt, expiresAt, maxTtlSeconds, label });
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* The declared lifetime (`expiresAt - issuedAt`) is at most the trust policy's ceiling `maxTtlSeconds`. Held by
|
|
218
|
-
* `verify-evidence` (through the time window above) and by the third-party pin writer, which has no `now` to hold.
|
|
219
|
-
*/
|
|
220
|
-
export function assertProducerTtlCeiling({ issuedAt, expiresAt, maxTtlSeconds, label }) {
|
|
221
|
-
if (!Number.isSafeInteger(maxTtlSeconds) || maxTtlSeconds < 1) {
|
|
222
|
-
throw new Error(`${label}: the trust policy's TTL ceiling ${String(maxTtlSeconds)} is not a positive integer`);
|
|
223
|
-
}
|
|
224
|
-
const issued = Date.parse(issuedAt);
|
|
225
|
-
const expires = Date.parse(expiresAt);
|
|
226
|
-
if (![issued, expires].every(Number.isFinite)) {
|
|
227
|
-
throw new Error(`${label} has an invalid time`);
|
|
228
|
-
}
|
|
229
|
-
if (expires <= issued) {
|
|
230
|
-
throw new Error(`${label} expiresAt must be after issuedAt`);
|
|
231
|
-
}
|
|
232
|
-
if (expires - issued > maxTtlSeconds * 1000) {
|
|
233
|
-
throw new Error(
|
|
234
|
-
`${label} exceeds the trusted maximum TTL (${(expires - issued) / 1000}s declared, maxProducerTtlSeconds ${maxTtlSeconds})`,
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export function sourceIdentity(subject) {
|
|
240
|
-
return evidenceSubjectKey(subject);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/** Root of the committed evidence tree, in every producing repository. */
|
|
244
|
-
export const EVIDENCE_TREE_ROOT = 'evidence';
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* The repository-relative directory a producer commits ONE subject digest's
|
|
248
|
-
* evidence bundle to. ONE owner: the producing workflows, the reuse check and
|
|
249
|
-
* (later) the delivery-side loader all read the name from here rather than
|
|
250
|
-
* interpolating a digest into a path themselves.
|
|
251
|
-
*
|
|
252
|
-
* `sha256-<hex>`, NOT `sha256:<hex>`. `:` is illegal in an NTFS path component,
|
|
253
|
-
* so a `sha256:`-named directory cannot be checked out on Windows at all — the
|
|
254
|
-
* clone fails, which would make the evidence plane silently undeliverable to
|
|
255
|
-
* any Windows operator. `sha256-<hex>` is cosign's own on-registry convention
|
|
256
|
-
* for exactly this reason (`sha256-<digest>.att`), so the plane gains no new
|
|
257
|
-
* vocabulary.
|
|
258
|
-
*/
|
|
259
|
-
export function evidenceDirectoryForDigest(manifestDigest) {
|
|
260
|
-
if (!/^sha256:[a-f0-9]{64}$/.test(manifestDigest ?? '')) {
|
|
261
|
-
throw new Error(
|
|
262
|
-
`evidence directory requires an exact sha256:<64-hex> manifest digest, got "${String(manifestDigest)}"`,
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
return `${EVIDENCE_TREE_ROOT}/${manifestDigest.replace(':', '-')}`;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function comparePlatform(left, right) {
|
|
269
|
-
return compareStrings(
|
|
270
|
-
`${left.os}/${left.architecture}/${left.variant ?? ''}`,
|
|
271
|
-
`${right.os}/${right.architecture}/${right.variant ?? ''}`,
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function platformIdentity(platform) {
|
|
276
|
-
return `${platform.os}/${platform.architecture}/${platform.variant ?? ''}`;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function assertPlatforms(platforms, requiredPlatforms, label) {
|
|
280
|
-
if (platforms.length === 0) {
|
|
281
|
-
throw new Error(`${label} omits its complete platform set`);
|
|
282
|
-
}
|
|
283
|
-
const sorted = [...platforms].sort(comparePlatform);
|
|
284
|
-
if (canonicalJson(platforms) !== canonicalJson(sorted)) {
|
|
285
|
-
throw new Error(`${label} platforms are not canonically ordered`);
|
|
286
|
-
}
|
|
287
|
-
const identities = new Set();
|
|
288
|
-
const digests = new Set();
|
|
289
|
-
for (const platform of platforms) {
|
|
290
|
-
const identity = platformIdentity(platform);
|
|
291
|
-
if (identities.has(identity)) {
|
|
292
|
-
throw new Error(`${label} has duplicate platform "${identity}"`);
|
|
293
|
-
}
|
|
294
|
-
if (digests.has(platform.manifestDigest)) {
|
|
295
|
-
throw new Error(
|
|
296
|
-
`${label} reuses platform digest "${platform.manifestDigest}"`,
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
identities.add(identity);
|
|
300
|
-
digests.add(platform.manifestDigest);
|
|
301
|
-
}
|
|
302
|
-
// COVERAGE, not equality. `requiredPlatforms` is what the edition DEMANDS
|
|
303
|
-
// the published coordinate serve — `verify-image-platforms.mjs` reads it the
|
|
304
|
-
// same way at delivery (`missing = required.filter(not present)`), and this
|
|
305
|
-
// gate must not contradict it.
|
|
306
|
-
//
|
|
307
|
-
// Equality here was a latent, total break of the whole plane. The canonical
|
|
308
|
-
// `<imageRef>:<contentHash>` coordinate becomes a MULTI-ARCH INDEX once the
|
|
309
|
-
// arm64 lane merges, so a statement produced against the delivered digest
|
|
310
|
-
// necessarily proves BOTH platforms — while every non-appliance edition
|
|
311
|
-
// declares `[linux/amd64]` and both appliance editions declare
|
|
312
|
-
// `[linux/arm64]`. Under equality NO single statement could ever satisfy any
|
|
313
|
-
// of them, and the same subject would need contradictory evidence per
|
|
314
|
-
// edition. The producer signs ONE statement about ONE published artifact;
|
|
315
|
-
// the edition's requirement is a subset of what that artifact serves.
|
|
316
|
-
//
|
|
317
|
-
// Nothing is weakened: every entry in `platforms` was proven at assembly
|
|
318
|
-
// time through its own hash-verified image config
|
|
319
|
-
// (`resolvePublishedPlatforms`), the ordering/uniqueness invariants above
|
|
320
|
-
// still hold exactly, and a REQUIRED platform that the statement does not
|
|
321
|
-
// attest is still a hard failure — which is the property that matters.
|
|
322
|
-
const actualIdentities = new Set(platforms.map(platformIdentity));
|
|
323
|
-
const missing = [
|
|
324
|
-
...new Set(requiredPlatforms.map(platformIdentity)),
|
|
325
|
-
]
|
|
326
|
-
.filter((identity) => !actualIdentities.has(identity))
|
|
327
|
-
.sort();
|
|
328
|
-
if (missing.length > 0) {
|
|
329
|
-
throw new Error(
|
|
330
|
-
`${label} attests platforms ${[...actualIdentities].sort().join(', ')}, ` +
|
|
331
|
-
`which omit ${missing.join(', ')} required by images.lock.json`,
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
export function normalizedImageLockDigest(imageLock) {
|
|
337
|
-
return sha256Digest(canonicalJsonBytes(imageLock));
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Membership in an OPTIONAL policy allowlist.
|
|
342
|
-
*
|
|
343
|
-
* `imageProducer.buildDefinitionDigests` / `.parametersDigests` are optional in
|
|
344
|
-
* `SigstoreIdentityPolicySchema`. Both are derived from the BuildKit SLSA v1
|
|
345
|
-
* `buildDefinition`, which carries the build's VCS revision and resolved
|
|
346
|
-
* dependencies — they change on essentially every build, so an armed policy
|
|
347
|
-
* cannot enumerate them ahead of time. OMITTED therefore means "this dimension
|
|
348
|
-
* is not pinned", and the build stays bound by builder id, build type, OCI
|
|
349
|
-
* media type and the Sigstore certificate identity / workflow binding.
|
|
350
|
-
*
|
|
351
|
-
* PRESENT means a hermetic deployment pinned an exact build, and membership is
|
|
352
|
-
* then STRICT — there is no "empty list means anything" escape: the kernel
|
|
353
|
-
* schema rejects an empty list, and one reaching here anyway admits nothing.
|
|
354
|
-
*/
|
|
355
|
-
function pinnedAllowlistAdmits(allowlist, value) {
|
|
356
|
-
return allowlist === undefined || allowlist.includes(value);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
export function validateProducerStatementBinding({
|
|
360
|
-
statement,
|
|
361
|
-
subject,
|
|
362
|
-
sourceHashAlgorithm,
|
|
363
|
-
expectedContractAuthority,
|
|
364
|
-
now,
|
|
365
|
-
maxTtlSeconds,
|
|
366
|
-
producerPolicy,
|
|
367
|
-
repositoryPin,
|
|
368
|
-
consumedNonces = new Set(),
|
|
369
|
-
}) {
|
|
370
|
-
if (
|
|
371
|
-
typeof sourceHashAlgorithm !== 'string' ||
|
|
372
|
-
sourceHashAlgorithm.length === 0
|
|
373
|
-
) {
|
|
374
|
-
throw new Error(
|
|
375
|
-
"producer evidence validation requires the image lock's declared source hash algorithm",
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
const { predicate } = statement;
|
|
379
|
-
const label = `producer evidence for ${subject.kind}:${subject.id}`;
|
|
380
|
-
assertKernelContractAuthorityEqual(
|
|
381
|
-
predicate.contractAuthority,
|
|
382
|
-
expectedContractAuthority,
|
|
383
|
-
label,
|
|
384
|
-
);
|
|
385
|
-
assertTimeWindow({
|
|
386
|
-
issuedAt: predicate.producerRun.issuedAt,
|
|
387
|
-
expiresAt: predicate.producerRun.expiresAt,
|
|
388
|
-
now,
|
|
389
|
-
maxTtlSeconds,
|
|
390
|
-
label,
|
|
391
|
-
});
|
|
392
|
-
if (consumedNonces.has(predicate.producerRun.nonce)) {
|
|
393
|
-
throw new Error(`${label} replays an already-consumed nonce`);
|
|
394
|
-
}
|
|
395
|
-
if (
|
|
396
|
-
repositoryPin?.repo !== subject.repo ||
|
|
397
|
-
repositoryPin.sourceCommit !== subject.sourceCommit
|
|
398
|
-
) {
|
|
399
|
-
throw new Error(`${label} does not match the committed repository pin`);
|
|
400
|
-
}
|
|
401
|
-
const expectedSource = {
|
|
402
|
-
kind: subject.kind,
|
|
403
|
-
id: subject.id,
|
|
404
|
-
repositoryUrl: repositoryPin.repositoryUrl,
|
|
405
|
-
repo: subject.repo,
|
|
406
|
-
path: subject.path,
|
|
407
|
-
sourceCommit: subject.sourceCommit,
|
|
408
|
-
sourceManifestHash: subject.sourceManifestHash,
|
|
409
|
-
// Read from the lock, never a literal: the lock's declared algorithm IS the
|
|
410
|
-
// coordinate's identity, and a hardcoded copy here would silently keep
|
|
411
|
-
// validating against a retired algorithm after the coordinate widened.
|
|
412
|
-
sourceHashAlgorithm,
|
|
413
|
-
sourceTreeHash: subject.contentHash,
|
|
414
|
-
};
|
|
415
|
-
if (canonicalJson(predicate.source) !== canonicalJson(expectedSource)) {
|
|
416
|
-
throw new Error(`${label} source identity does not match images.lock.json`);
|
|
417
|
-
}
|
|
418
|
-
if (
|
|
419
|
-
statement.subject[0].name !== subject.imageRef ||
|
|
420
|
-
`sha256:${statement.subject[0].digest.sha256}` !==
|
|
421
|
-
predicate.artifact.manifestDigest ||
|
|
422
|
-
predicate.artifact.imageRepository !== subject.imageRef
|
|
423
|
-
) {
|
|
424
|
-
throw new Error(`${label} has an OCI subject/digest substitution`);
|
|
425
|
-
}
|
|
426
|
-
// THE SEALED ARTIFACT. `imageDigest` names the exact manifest that SHIPS
|
|
427
|
-
// (`seal-image-digests.mjs` writes it from the delivery-time platform
|
|
428
|
-
// verifier; the chart plane installs by it). The producer statement attests
|
|
429
|
-
// one manifest digest. If the two differ, the evidence proves an image OTHER
|
|
430
|
-
// than the one the lock installs — every check above still passes, because
|
|
431
|
-
// they bind the SOURCE coordinate and the statement's internal consistency,
|
|
432
|
-
// not the shipped bytes. That is the one substitution a self-consistent
|
|
433
|
-
// evidence set can carry, and it is the one a customer verifying its own
|
|
434
|
-
// edition most needs refused. A subject with NO `imageDigest` gives the
|
|
435
|
-
// statement nothing to be bound to, so it is refused, never read as "binds
|
|
436
|
-
// nothing": evidence is verified against a sealed lock only.
|
|
437
|
-
if (typeof subject.imageDigest !== 'string') {
|
|
438
|
-
throw new Error(
|
|
439
|
-
`${label}: images.lock.json seals no imageDigest for ${subject.kind}:${subject.id}, so the ` +
|
|
440
|
-
'attested manifest is bound to nothing. Run `xema edition resolve-digests` first.',
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
if (subject.imageDigest !== predicate.artifact.manifestDigest) {
|
|
444
|
-
throw new Error(
|
|
445
|
-
`${label} attests manifest ${predicate.artifact.manifestDigest} but ` +
|
|
446
|
-
`images.lock.json seals ${subject.imageDigest} for ${subject.imageRef}`,
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
assertPlatforms(predicate.build.platforms, subject.requiredPlatforms, label);
|
|
450
|
-
// The always-required anchors are checked unconditionally; the two per-build
|
|
451
|
-
// digests only narrow the identity when the policy actually pinned them (see
|
|
452
|
-
// `pinnedAllowlistAdmits`). Reading them unconditionally would throw a
|
|
453
|
-
// TypeError on `undefined.includes` for every ordinary policy, and simply
|
|
454
|
-
// ignoring a PRESENT list would be a gate that cannot fail.
|
|
455
|
-
const authorizedBuildIdentity =
|
|
456
|
-
Boolean(producerPolicy) &&
|
|
457
|
-
producerPolicy.builderIds.includes(predicate.build.builderId) &&
|
|
458
|
-
producerPolicy.buildTypes.includes(predicate.build.buildType) &&
|
|
459
|
-
producerPolicy.manifestMediaTypes.includes(
|
|
460
|
-
predicate.artifact.manifestMediaType,
|
|
461
|
-
) &&
|
|
462
|
-
pinnedAllowlistAdmits(
|
|
463
|
-
producerPolicy.buildDefinitionDigests,
|
|
464
|
-
predicate.build.buildDefinitionDigest,
|
|
465
|
-
) &&
|
|
466
|
-
pinnedAllowlistAdmits(
|
|
467
|
-
producerPolicy.parametersDigests,
|
|
468
|
-
predicate.build.parametersDigest,
|
|
469
|
-
);
|
|
470
|
-
if (!authorizedBuildIdentity) {
|
|
471
|
-
throw new Error(
|
|
472
|
-
`${label} build identity, definition, parameters, or OCI media type is not authorized`,
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
const protectedDigests = new Set([
|
|
476
|
-
predicate.artifact.manifestDigest,
|
|
477
|
-
predicate.artifact.sbom.digest,
|
|
478
|
-
predicate.artifact.provenance.digest,
|
|
479
|
-
]);
|
|
480
|
-
if (protectedDigests.size !== 3) {
|
|
481
|
-
throw new Error(
|
|
482
|
-
`${label} substitutes an image, SBOM, or provenance digest`,
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
return statement;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* The canonical order of a completeness `images[]` entry.
|
|
490
|
-
*
|
|
491
|
-
* Exported because the PRODUCER must emit exactly the order the verifier
|
|
492
|
-
* demands: `validateCompletenessStatementBinding` REFUSES a statement whose
|
|
493
|
-
* `images[]` is not in this order. Two copies of a comparator that a signature
|
|
494
|
-
* is bound to is a signed artifact waiting to become unverifiable.
|
|
495
|
-
*/
|
|
496
|
-
export function compareCompletenessImages(left, right) {
|
|
497
|
-
return (
|
|
498
|
-
compareStrings(left.subjectKind, right.subjectKind) ||
|
|
499
|
-
compareStrings(left.subjectId, right.subjectId) ||
|
|
500
|
-
// The generations of one composed artifact share kind and id: they order by image, then manifest.
|
|
501
|
-
compareStrings(left.imageRepository, right.imageRepository) ||
|
|
502
|
-
compareStrings(left.manifestDigest, right.manifestDigest)
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function normalizedCompletenessImages(images) {
|
|
507
|
-
return [...images].sort(compareCompletenessImages);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
export function validateCompletenessStatementBinding({
|
|
511
|
-
statement,
|
|
512
|
-
distributionLock,
|
|
513
|
-
imageLock,
|
|
514
|
-
verifiedProducers,
|
|
515
|
-
evidenceDescriptors,
|
|
516
|
-
now,
|
|
517
|
-
maxTtlSeconds,
|
|
518
|
-
expectedReleaseSourceCommit,
|
|
519
|
-
evidenceNonces = new Set(),
|
|
520
|
-
}) {
|
|
521
|
-
const { predicate } = statement;
|
|
522
|
-
const label = `distribution completeness for ${imageLock.distributionId}`;
|
|
523
|
-
assertTimeWindow({
|
|
524
|
-
issuedAt: predicate.issuedAt,
|
|
525
|
-
expiresAt: predicate.expiresAt,
|
|
526
|
-
now,
|
|
527
|
-
maxTtlSeconds,
|
|
528
|
-
label,
|
|
529
|
-
});
|
|
530
|
-
if (evidenceNonces.has(predicate.nonce)) {
|
|
531
|
-
throw new Error(`${label} reuses a producer nonce`);
|
|
532
|
-
}
|
|
533
|
-
if (predicate.releaseSourceCommit !== expectedReleaseSourceCommit) {
|
|
534
|
-
throw new Error(
|
|
535
|
-
`${label} does not bind the exact editions release source commit`,
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
const lockDigest = normalizedImageLockDigest(imageLock);
|
|
539
|
-
if (
|
|
540
|
-
predicate.distributionId !== distributionLock.distributionId ||
|
|
541
|
-
predicate.distributionInputHash !== distributionLock.inputHash ||
|
|
542
|
-
predicate.normalizedImageLockDigest !== lockDigest ||
|
|
543
|
-
statement.subject[0].name !==
|
|
544
|
-
`xema-distribution/${distributionLock.distributionId}` ||
|
|
545
|
-
`sha256:${statement.subject[0].digest.sha256}` !== lockDigest
|
|
546
|
-
) {
|
|
547
|
-
throw new Error(`${label} does not bind the selected distribution locks`);
|
|
548
|
-
}
|
|
549
|
-
const expectedImages = imageLock.subjects.map((subject) => {
|
|
550
|
-
const key = sourceIdentity(subject);
|
|
551
|
-
const producer = verifiedProducers.get(key);
|
|
552
|
-
const descriptor = evidenceDescriptors.get(key);
|
|
553
|
-
if (!producer || !descriptor) {
|
|
554
|
-
throw new Error(`${label} omits producer evidence for ${key}`);
|
|
555
|
-
}
|
|
556
|
-
return {
|
|
557
|
-
subjectKind: subject.kind,
|
|
558
|
-
subjectId: subject.id,
|
|
559
|
-
imageRepository: subject.imageRef,
|
|
560
|
-
manifestDigest: producer.predicate.artifact.manifestDigest,
|
|
561
|
-
producerStatementDigest: descriptor.statementDigest,
|
|
562
|
-
producerBundleDigest: descriptor.bundleDigest,
|
|
563
|
-
};
|
|
564
|
-
});
|
|
565
|
-
const actual = normalizedCompletenessImages(predicate.images);
|
|
566
|
-
const expected = normalizedCompletenessImages(expectedImages);
|
|
567
|
-
if (
|
|
568
|
-
canonicalJson(predicate.images) !== canonicalJson(actual) ||
|
|
569
|
-
canonicalJson(actual) !== canonicalJson(expected)
|
|
570
|
-
) {
|
|
571
|
-
throw new Error(
|
|
572
|
-
`${label} image set is incomplete, contains extras, or is not canonically ordered`,
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
return statement;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
export function validateEntitlementStatementBinding({
|
|
579
|
-
statement,
|
|
580
|
-
completenessStatementDigest,
|
|
581
|
-
customerId,
|
|
582
|
-
distributionId,
|
|
583
|
-
mode,
|
|
584
|
-
targetRegistry,
|
|
585
|
-
now,
|
|
586
|
-
maxTtlSeconds,
|
|
587
|
-
consumedNonces = new Set(),
|
|
588
|
-
}) {
|
|
589
|
-
const { predicate } = statement;
|
|
590
|
-
const label = `delivery entitlement for ${customerId}`;
|
|
591
|
-
assertTimeWindow({
|
|
592
|
-
issuedAt: predicate.issuedAt,
|
|
593
|
-
expiresAt: predicate.expiresAt,
|
|
594
|
-
now,
|
|
595
|
-
maxTtlSeconds,
|
|
596
|
-
label,
|
|
597
|
-
});
|
|
598
|
-
// The predicate's `idempotencyKey` is the issuer's own request key. Nothing
|
|
599
|
-
// on the verifying side records keys across runs, so it is not checked here:
|
|
600
|
-
// replay is refused by the nonce, and by the replay state's release sequence.
|
|
601
|
-
if (consumedNonces.has(predicate.nonce)) {
|
|
602
|
-
throw new Error(`${label} is replayed`);
|
|
603
|
-
}
|
|
604
|
-
if (
|
|
605
|
-
predicate.customerId !== customerId ||
|
|
606
|
-
predicate.distributionId !== distributionId ||
|
|
607
|
-
predicate.distributionStatementDigest !== completenessStatementDigest ||
|
|
608
|
-
predicate.mode !== mode ||
|
|
609
|
-
predicate.targetRegistry !== targetRegistry
|
|
610
|
-
) {
|
|
611
|
-
throw new Error(
|
|
612
|
-
`${label} does not authorize the requested customer, distribution, mode, and target`,
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
if (
|
|
616
|
-
statement.subject[0].name !== `xema-distribution/${distributionId}` ||
|
|
617
|
-
`sha256:${statement.subject[0].digest.sha256}` !==
|
|
618
|
-
completenessStatementDigest
|
|
619
|
-
) {
|
|
620
|
-
throw new Error(`${label} substitutes the distribution release`);
|
|
621
|
-
}
|
|
622
|
-
return statement;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/** Throw the kernel's refusal code, verbatim, when a kernel predicate check returned one. */
|
|
626
|
-
function assertKernelPredicateAccepted(label, refusal) {
|
|
627
|
-
if (refusal !== undefined) {
|
|
628
|
-
throw new Error(`${label} is refused: ${refusal}`);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
function assertEvidenceChannel(channel) {
|
|
633
|
-
if (typeof channel !== 'string' || channel === '') {
|
|
634
|
-
throw new Error('evidence verification requires the channel of the edition being verified');
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* The policy rows that authorize `predicateType` on `channel`: a filter on the
|
|
640
|
-
* row's `channel` and `predicateTypes`, and, when `imageRepository` is given
|
|
641
|
-
* (producer evidence), on the row's `imageRepositories` holding that exact
|
|
642
|
-
* repository (no prefix match). It reads no certificate and proves nothing by
|
|
643
|
-
* itself.
|
|
644
|
-
*
|
|
645
|
-
* What ties a row to its channel is the kernel policy schema the policy was
|
|
646
|
-
* parsed by: a row's `githubWorkflow.ref` must be the ref of its `channel`
|
|
647
|
-
* (`EVIDENCE_CHANNEL_REFS`) and its `certificateIdentity` must be exactly
|
|
648
|
-
* `https://github.com/<repository>/<path>@<ref>`. One ROW is therefore one
|
|
649
|
-
* channel; the same workflow file may still be authorized on both channels, as
|
|
650
|
-
* two rows naming the two refs. The caller then runs cosign once per returned
|
|
651
|
-
* row with that row's `certificateIdentity`, `oidcIssuer`, workflow repository
|
|
652
|
-
* and ref (and the exact workflow commit), so evidence signed from the other
|
|
653
|
-
* channel's ref matches no row selected here and fails verification; an
|
|
654
|
-
* edition that states a channel with no row for the predicate is refused
|
|
655
|
-
* before cosign runs ("authorizes no signer").
|
|
656
|
-
*
|
|
657
|
-
* The channel an edition DESCRIPTOR states is unauthenticated input: it only
|
|
658
|
-
* selects which policy rows are tried; what authenticates a channel is the
|
|
659
|
-
* certificate matching a row of that channel.
|
|
660
|
-
*/
|
|
661
|
-
export function policyCandidates(policy, predicateType, channel, imageRepository) {
|
|
662
|
-
const candidates = policy.policies.filter(
|
|
663
|
-
(candidate) =>
|
|
664
|
-
candidate.channel === channel &&
|
|
665
|
-
candidate.predicateTypes.includes(predicateType) &&
|
|
666
|
-
(imageRepository === undefined ||
|
|
667
|
-
candidate.imageRepositories?.includes(imageRepository) === true),
|
|
668
|
-
);
|
|
669
|
-
// The kernel policy also admits `public-key` signers; this verifier runs cosign keyless only, so such a row is refused, never skipped.
|
|
670
|
-
const unsupported = candidates.find((candidate) => candidate.kind !== OIDC_CERTIFICATE_SIGNER);
|
|
671
|
-
if (unsupported !== undefined) {
|
|
672
|
-
throw new Error(
|
|
673
|
-
`policy row "${unsupported.policyId}" is a ${JSON.stringify(unsupported.kind)} signer; this CLI verifies ${OIDC_CERTIFICATE_SIGNER} signers only`,
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
return candidates;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
function cosignVersion(cosignBinary) {
|
|
680
|
-
const output = exactExecutableOutput(cosignBinary, ['version'], {
|
|
681
|
-
env: COSIGN_ENV,
|
|
682
|
-
timeout: 10_000,
|
|
683
|
-
});
|
|
684
|
-
const match = /\bGitVersion:\s*(v\d+\.\d+\.\d+)\b/.exec(output);
|
|
685
|
-
if (!match) {
|
|
686
|
-
throw new Error('cosign version output has no exact GitVersion');
|
|
687
|
-
}
|
|
688
|
-
return match[1];
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export function prepareTrustedCosignBinary({ cosignBinary, policy }) {
|
|
692
|
-
return prepareDigestPinnedExecutable({
|
|
693
|
-
executablePath: cosignBinary,
|
|
694
|
-
trustedDigests: policy.trustedCosignBinaryDigests,
|
|
695
|
-
label: 'cosign',
|
|
696
|
-
verifyVersion(privateBinary) {
|
|
697
|
-
if (cosignVersion(privateBinary) !== policy.cosignVersion) {
|
|
698
|
-
throw new Error(
|
|
699
|
-
`cosign version does not match pinned ${policy.cosignVersion}`,
|
|
700
|
-
);
|
|
701
|
-
}
|
|
702
|
-
},
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
export function verifySigstoreEvidenceDescriptor({
|
|
707
|
-
evidenceRoot,
|
|
708
|
-
descriptor,
|
|
709
|
-
policy,
|
|
710
|
-
predicateType,
|
|
711
|
-
channel,
|
|
712
|
-
imageRepository,
|
|
713
|
-
expectedWorkflowSha,
|
|
714
|
-
parseStatement,
|
|
715
|
-
policyRoot = evidenceRoot,
|
|
716
|
-
trustedCosign,
|
|
717
|
-
holdProducerWorkflow,
|
|
718
|
-
// The statement bytes are the DSSE payload a third party's tool signed (cosign's provenance), which is
|
|
719
|
-
// exact JSON but not this CLI's canonical form; only that caller replaces the canonical decoder.
|
|
720
|
-
decodeStatement = parseCanonicalJsonBytes,
|
|
721
|
-
}) {
|
|
722
|
-
assertEvidenceChannel(channel);
|
|
723
|
-
const statementFile = readExactDescriptorFile(
|
|
724
|
-
evidenceRoot,
|
|
725
|
-
descriptor.statementPath,
|
|
726
|
-
descriptor.statementDigest,
|
|
727
|
-
'evidence statement',
|
|
728
|
-
MAX_STATEMENT_BYTES,
|
|
729
|
-
);
|
|
730
|
-
const bundleFile = readExactDescriptorFile(
|
|
731
|
-
evidenceRoot,
|
|
732
|
-
descriptor.bundlePath,
|
|
733
|
-
descriptor.bundleDigest,
|
|
734
|
-
'Sigstore bundle',
|
|
735
|
-
MAX_SIGSTORE_BUNDLE_BYTES,
|
|
736
|
-
);
|
|
737
|
-
const bundle = JSON.parse(
|
|
738
|
-
decodeExactUtf8(bundleFile.bytes, 'Sigstore bundle'),
|
|
739
|
-
);
|
|
740
|
-
if (bundle.mediaType !== descriptor.bundleMediaType) {
|
|
741
|
-
throw new Error('Sigstore bundle mediaType does not match its descriptor');
|
|
742
|
-
}
|
|
743
|
-
if (
|
|
744
|
-
bundle.messageSignature !== undefined ||
|
|
745
|
-
typeof bundle.dsseEnvelope !== 'object' ||
|
|
746
|
-
bundle.dsseEnvelope === null ||
|
|
747
|
-
bundle.dsseEnvelope.payloadType !== 'application/vnd.in-toto+json' ||
|
|
748
|
-
!Array.isArray(bundle.dsseEnvelope.signatures) ||
|
|
749
|
-
bundle.dsseEnvelope.signatures.length !== 1 ||
|
|
750
|
-
typeof bundle.dsseEnvelope.payload !== 'string' ||
|
|
751
|
-
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(
|
|
752
|
-
bundle.dsseEnvelope.payload,
|
|
753
|
-
)
|
|
754
|
-
) {
|
|
755
|
-
throw new Error(
|
|
756
|
-
'Sigstore bundle must contain exactly one DSSE in-toto signature',
|
|
757
|
-
);
|
|
758
|
-
}
|
|
759
|
-
const dssePayload = Buffer.from(bundle.dsseEnvelope.payload, 'base64');
|
|
760
|
-
if (
|
|
761
|
-
dssePayload.toString('base64') !== bundle.dsseEnvelope.payload ||
|
|
762
|
-
!dssePayload.equals(statementFile.bytes)
|
|
763
|
-
) {
|
|
764
|
-
throw new Error(
|
|
765
|
-
'Sigstore DSSE payload does not exactly match the canonical statement',
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
const statement = parseStatement(
|
|
769
|
-
decodeStatement(statementFile.bytes, 'evidence statement'),
|
|
770
|
-
);
|
|
771
|
-
if (statement.predicateType !== predicateType) {
|
|
772
|
-
throw new Error('evidence statement predicateType is not authorized');
|
|
773
|
-
}
|
|
774
|
-
const candidates = policyCandidates(
|
|
775
|
-
policy,
|
|
776
|
-
predicateType,
|
|
777
|
-
channel,
|
|
778
|
-
imageRepository,
|
|
779
|
-
);
|
|
780
|
-
if (candidates.length === 0) {
|
|
781
|
-
throw new Error(
|
|
782
|
-
`offline trust policy authorizes no signer for ${predicateType} on channel "${channel}"` +
|
|
783
|
-
(imageRepository === undefined
|
|
784
|
-
? ''
|
|
785
|
-
: ` and image repository "${imageRepository}"`),
|
|
786
|
-
);
|
|
787
|
-
}
|
|
788
|
-
const trustedCosignPath = assertPreparedExecutable(trustedCosign, 'cosign');
|
|
789
|
-
const trustedRoot = validatePinnedTrustedRoot(policyRoot, policy);
|
|
790
|
-
const verifiedWorkflowSha =
|
|
791
|
-
expectedWorkflowSha ??
|
|
792
|
-
statement.predicate?.producerRun?.producerWorkflowSha;
|
|
793
|
-
let verified = false;
|
|
794
|
-
let verifiedIdentityPolicy;
|
|
795
|
-
let verifiedCandidateWorkflowSha;
|
|
796
|
-
const verificationErrors = [];
|
|
797
|
-
for (const candidate of candidates) {
|
|
798
|
-
if (!verifiedWorkflowSha) {
|
|
799
|
-
throw new Error(
|
|
800
|
-
`policy "${candidate.policyId}" requires an exact workflow SHA`,
|
|
801
|
-
);
|
|
802
|
-
}
|
|
803
|
-
const verifiedOutputDirectory = mkdtempSync(
|
|
804
|
-
join(tmpdir(), 'xema-verified-dsse-'),
|
|
805
|
-
);
|
|
806
|
-
try {
|
|
807
|
-
const verifiedBundlePath = join(
|
|
808
|
-
verifiedOutputDirectory,
|
|
809
|
-
'bundle.sigstore.json',
|
|
810
|
-
);
|
|
811
|
-
const verifiedTrustedRootPath = join(
|
|
812
|
-
verifiedOutputDirectory,
|
|
813
|
-
'trusted-root.json',
|
|
814
|
-
);
|
|
815
|
-
writeFileSync(verifiedBundlePath, bundleFile.bytes, {
|
|
816
|
-
flag: 'wx',
|
|
817
|
-
mode: 0o600,
|
|
818
|
-
});
|
|
819
|
-
writeFileSync(verifiedTrustedRootPath, trustedRoot.bytes, {
|
|
820
|
-
flag: 'wx',
|
|
821
|
-
mode: 0o600,
|
|
822
|
-
});
|
|
823
|
-
// `--check-claims=false`: the attested claim is bound to the statement,
|
|
824
|
-
// not to a blob — the DSSE payload was already proven byte-identical to
|
|
825
|
-
// the canonical statement above, so cosign only has to prove the
|
|
826
|
-
// signature under the exact certificate constraints.
|
|
827
|
-
const args = [
|
|
828
|
-
'verify-blob-attestation',
|
|
829
|
-
'--check-claims=false',
|
|
830
|
-
'--offline',
|
|
831
|
-
'--new-bundle-format',
|
|
832
|
-
'--trusted-root',
|
|
833
|
-
verifiedTrustedRootPath,
|
|
834
|
-
'--bundle',
|
|
835
|
-
verifiedBundlePath,
|
|
836
|
-
...certificateConstraintArgs(candidate, verifiedWorkflowSha),
|
|
837
|
-
'--type',
|
|
838
|
-
predicateType,
|
|
839
|
-
];
|
|
840
|
-
execFileSync(trustedCosignPath, args, {
|
|
841
|
-
env: COSIGN_ENV,
|
|
842
|
-
stdio: ['ignore', 'ignore', 'pipe'],
|
|
843
|
-
timeout: 60_000,
|
|
844
|
-
});
|
|
845
|
-
verified = true;
|
|
846
|
-
verifiedIdentityPolicy = candidate;
|
|
847
|
-
verifiedCandidateWorkflowSha = verifiedWorkflowSha;
|
|
848
|
-
break;
|
|
849
|
-
} catch (error) {
|
|
850
|
-
verificationErrors.push(
|
|
851
|
-
`${candidate.policyId}: ${
|
|
852
|
-
error instanceof Error ? error.message : String(error)
|
|
853
|
-
}`,
|
|
854
|
-
);
|
|
855
|
-
} finally {
|
|
856
|
-
rmSync(verifiedOutputDirectory, {
|
|
857
|
-
recursive: true,
|
|
858
|
-
force: true,
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
if (!verified) {
|
|
863
|
-
throw new Error(
|
|
864
|
-
'offline Sigstore verification failed for every trusted identity: ' +
|
|
865
|
-
verificationErrors.join('; '),
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
// The certificate's workflow commit is held to the data root's operator-stated pin for that workflow's repository.
|
|
869
|
-
if (holdProducerWorkflow !== undefined) {
|
|
870
|
-
assertKernelPredicateAccepted(
|
|
871
|
-
`${predicateType} signer ${verifiedIdentityPolicy.githubWorkflow.repository}`,
|
|
872
|
-
holdProducerWorkflow({
|
|
873
|
-
repository: verifiedIdentityPolicy.githubWorkflow.repository,
|
|
874
|
-
workflowCommit: verifiedCandidateWorkflowSha,
|
|
875
|
-
}),
|
|
876
|
-
);
|
|
877
|
-
}
|
|
878
|
-
return {
|
|
879
|
-
statement,
|
|
880
|
-
statementPath: statementFile.path,
|
|
881
|
-
bundle,
|
|
882
|
-
identityPolicy: verifiedIdentityPolicy,
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
export async function verifyDistributionImageEvidenceSet(options) {
|
|
887
|
-
assertEvidenceChannel(options.channel);
|
|
888
|
-
const trustedCosign = prepareTrustedCosignBinary({
|
|
889
|
-
cosignBinary: options.cosignBinary,
|
|
890
|
-
policy: options.policy,
|
|
891
|
-
});
|
|
892
|
-
try {
|
|
893
|
-
return await verifyDistributionImageEvidenceSetWithCosign({
|
|
894
|
-
...options,
|
|
895
|
-
trustedCosign,
|
|
896
|
-
});
|
|
897
|
-
} finally {
|
|
898
|
-
trustedCosign.dispose();
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
async function verifyDistributionImageEvidenceSetWithCosign({
|
|
903
|
-
distributionId,
|
|
904
|
-
channel,
|
|
905
|
-
dataRoot,
|
|
906
|
-
// WHERE THE LOCKS ARE READ FROM: the editions root. The runtime index comes
|
|
907
|
-
// from `dataRoot`: the index is the data root's, the lock is the edition's.
|
|
908
|
-
editionsRoot,
|
|
909
|
-
evidenceRoot,
|
|
910
|
-
evidenceSet,
|
|
911
|
-
policy,
|
|
912
|
-
now,
|
|
913
|
-
expectedReleaseSourceCommit,
|
|
914
|
-
policyRoot = evidenceRoot,
|
|
915
|
-
trustedCosign,
|
|
916
|
-
schemas,
|
|
917
|
-
}) {
|
|
918
|
-
// The authority the producer statements are held to is the one the data
|
|
919
|
-
// root's index states, which the entry has already held to the data root's
|
|
920
|
-
// evidence-trust pin (`readTrustAnchors`); the bundled kernel is held to that
|
|
921
|
-
// authority by the `--kernel` equality check at the entry, and the schemas that
|
|
922
|
-
// parse the index are the bundled ones, held to their edition protocol.
|
|
923
|
-
const {
|
|
924
|
-
distributionLock,
|
|
925
|
-
imageLock,
|
|
926
|
-
repositoryPins,
|
|
927
|
-
runtimeSourceIndex,
|
|
928
|
-
thirdPartySources,
|
|
929
|
-
} = await loadValidatedLocks(distributionId, dataRoot, schemas, {
|
|
930
|
-
editionsRoot,
|
|
931
|
-
channel,
|
|
932
|
-
});
|
|
933
|
-
const expectedContractAuthority = runtimeSourceIndex.contractAuthority;
|
|
934
|
-
if (evidenceSet.distributionId !== distributionId) {
|
|
935
|
-
throw new Error(
|
|
936
|
-
`evidence set "${evidenceSet.distributionId}" does not match requested distribution "${distributionId}"`,
|
|
937
|
-
);
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
const evidenceDescriptors = new Map();
|
|
941
|
-
for (const producerEvidence of evidenceSet.producerEvidence) {
|
|
942
|
-
const key = `${producerEvidence.subjectKind}\0${producerEvidence.subjectId}`;
|
|
943
|
-
if (evidenceDescriptors.has(key)) {
|
|
944
|
-
throw new Error(`evidence set duplicates producer subject ${key}`);
|
|
945
|
-
}
|
|
946
|
-
evidenceDescriptors.set(key, producerEvidence.evidence);
|
|
947
|
-
}
|
|
948
|
-
if (evidenceDescriptors.size !== imageLock.subjects.length) {
|
|
949
|
-
throw new Error(
|
|
950
|
-
`evidence set has ${evidenceDescriptors.size} producer subjects; images.lock.json requires exactly ${imageLock.subjects.length}`,
|
|
951
|
-
);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
const verifiedProducers = new Map();
|
|
955
|
-
const verifiedNonces = new Set();
|
|
956
|
-
for (const subject of imageLock.subjects) {
|
|
957
|
-
const key = sourceIdentity(subject);
|
|
958
|
-
const descriptor = evidenceDescriptors.get(key);
|
|
959
|
-
if (!descriptor) {
|
|
960
|
-
throw new Error(`evidence set omits producer subject ${key}`);
|
|
961
|
-
}
|
|
962
|
-
const repositoryPin = repositoryPins.get(subject.repo);
|
|
963
|
-
if (!repositoryPin) {
|
|
964
|
-
throw new Error(
|
|
965
|
-
`runtime subject "${subject.kind}:${subject.id}" has no committed repository pin`,
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
const { statement, identityPolicy, bundle } = verifySigstoreEvidenceDescriptor({
|
|
969
|
-
evidenceRoot,
|
|
970
|
-
descriptor,
|
|
971
|
-
policy,
|
|
972
|
-
predicateType: schemas.IMAGE_PRODUCER_PREDICATE_TYPE,
|
|
973
|
-
channel,
|
|
974
|
-
imageRepository: subject.imageRef,
|
|
975
|
-
parseStatement: schemas.parseImageProducerStatement,
|
|
976
|
-
policyRoot,
|
|
977
|
-
trustedCosign,
|
|
978
|
-
holdProducerWorkflow: (producer) => schemas.producerWorkflowPinRefusal(runtimeSourceIndex, producer),
|
|
979
|
-
});
|
|
980
|
-
const thirdPartySource = thirdPartySources.get(subject.repo);
|
|
981
|
-
if (thirdPartySource !== undefined) {
|
|
982
|
-
assertThirdPartySubjectAdmission({ subject, source: thirdPartySource, statement, identityPolicy, bundle });
|
|
983
|
-
}
|
|
984
|
-
// XEMA-1611 d9-1 first-party form pending d9: no repository-agreement assertion is made for a first-party subject
|
|
985
|
-
// (a monorepo path is not the signing row's repository, and the xema-deploy rows sign other repositories' images).
|
|
986
|
-
validateProducerStatementBinding({
|
|
987
|
-
statement,
|
|
988
|
-
subject,
|
|
989
|
-
sourceHashAlgorithm: imageLock.sourceHashAlgorithm,
|
|
990
|
-
expectedContractAuthority,
|
|
991
|
-
now,
|
|
992
|
-
maxTtlSeconds: policy.maxProducerTtlSeconds,
|
|
993
|
-
producerPolicy: identityPolicy.imageProducer,
|
|
994
|
-
repositoryPin,
|
|
995
|
-
consumedNonces: verifiedNonces,
|
|
996
|
-
});
|
|
997
|
-
verifiedNonces.add(statement.predicate.producerRun.nonce);
|
|
998
|
-
verifiedProducers.set(key, statement);
|
|
999
|
-
}
|
|
1000
|
-
for (const key of evidenceDescriptors.keys()) {
|
|
1001
|
-
if (!verifiedProducers.has(key)) {
|
|
1002
|
-
throw new Error(`evidence set contains unknown producer subject ${key}`);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
const { statement: completenessStatement, identityPolicy: completenessPolicy } = verifySigstoreEvidenceDescriptor(
|
|
1007
|
-
{
|
|
1008
|
-
evidenceRoot,
|
|
1009
|
-
descriptor: evidenceSet.completeness,
|
|
1010
|
-
policy,
|
|
1011
|
-
predicateType: schemas.DISTRIBUTION_IMAGES_PREDICATE_TYPE,
|
|
1012
|
-
channel,
|
|
1013
|
-
expectedWorkflowSha: expectedReleaseSourceCommit,
|
|
1014
|
-
parseStatement: schemas.parseDistributionImagesStatement,
|
|
1015
|
-
policyRoot,
|
|
1016
|
-
trustedCosign,
|
|
1017
|
-
},
|
|
1018
|
-
);
|
|
1019
|
-
// After the signature verified: the channel the predicate states is the verifying policy row's, and the
|
|
1020
|
-
// predicate completes THIS lock. A kernel refusal is terminal and is surfaced by its code.
|
|
1021
|
-
assertKernelPredicateAccepted(
|
|
1022
|
-
`distribution completeness for ${imageLock.distributionId}`,
|
|
1023
|
-
schemas.distributionImagesPredicateChannelRefusal(completenessStatement.predicate, completenessPolicy),
|
|
1024
|
-
);
|
|
1025
|
-
assertKernelPredicateAccepted(
|
|
1026
|
-
`distribution completeness for ${imageLock.distributionId}`,
|
|
1027
|
-
schemas.distributionImagesPredicateLockRefusal(completenessStatement.predicate, distributionLock),
|
|
1028
|
-
);
|
|
1029
|
-
validateCompletenessStatementBinding({
|
|
1030
|
-
statement: completenessStatement,
|
|
1031
|
-
distributionLock,
|
|
1032
|
-
imageLock,
|
|
1033
|
-
verifiedProducers,
|
|
1034
|
-
evidenceDescriptors,
|
|
1035
|
-
now,
|
|
1036
|
-
maxTtlSeconds: policy.maxCompletenessTtlSeconds,
|
|
1037
|
-
expectedReleaseSourceCommit,
|
|
1038
|
-
evidenceNonces: verifiedNonces,
|
|
1039
|
-
});
|
|
1040
|
-
verifiedNonces.add(completenessStatement.predicate.nonce);
|
|
1041
|
-
|
|
1042
|
-
return {
|
|
1043
|
-
distributionLock,
|
|
1044
|
-
imageLock,
|
|
1045
|
-
producerStatements: verifiedProducers,
|
|
1046
|
-
completenessStatement,
|
|
1047
|
-
completenessStatementDigest: evidenceSet.completeness.statementDigest,
|
|
1048
|
-
verifiedNonces,
|
|
1049
|
-
};
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
export async function verifyImageDeliveryEntitlement(options) {
|
|
1053
|
-
assertEvidenceChannel(options.channel);
|
|
1054
|
-
const trustedCosign = prepareTrustedCosignBinary({
|
|
1055
|
-
cosignBinary: options.cosignBinary,
|
|
1056
|
-
policy: options.policy,
|
|
1057
|
-
});
|
|
1058
|
-
try {
|
|
1059
|
-
return await verifyImageDeliveryEntitlementWithCosign({
|
|
1060
|
-
...options,
|
|
1061
|
-
trustedCosign,
|
|
1062
|
-
});
|
|
1063
|
-
} finally {
|
|
1064
|
-
trustedCosign.dispose();
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
async function verifyImageDeliveryEntitlementWithCosign({
|
|
1069
|
-
evidenceRoot,
|
|
1070
|
-
descriptor,
|
|
1071
|
-
policy,
|
|
1072
|
-
completenessStatementDigest,
|
|
1073
|
-
customerId,
|
|
1074
|
-
distributionId,
|
|
1075
|
-
distributionLock,
|
|
1076
|
-
channel,
|
|
1077
|
-
mode,
|
|
1078
|
-
targetRegistry,
|
|
1079
|
-
now,
|
|
1080
|
-
expectedWorkflowSha,
|
|
1081
|
-
consumedNonces = new Set(),
|
|
1082
|
-
policyRoot = evidenceRoot,
|
|
1083
|
-
trustedCosign,
|
|
1084
|
-
schemas,
|
|
1085
|
-
}) {
|
|
1086
|
-
const { statement } = verifySigstoreEvidenceDescriptor({
|
|
1087
|
-
evidenceRoot,
|
|
1088
|
-
descriptor,
|
|
1089
|
-
policy,
|
|
1090
|
-
predicateType: schemas.IMAGE_DELIVERY_ENTITLEMENT_PREDICATE_TYPE,
|
|
1091
|
-
channel,
|
|
1092
|
-
expectedWorkflowSha,
|
|
1093
|
-
parseStatement: schemas.parseImageDeliveryEntitlementStatement,
|
|
1094
|
-
policyRoot,
|
|
1095
|
-
trustedCosign,
|
|
1096
|
-
});
|
|
1097
|
-
assertKernelPredicateAccepted(
|
|
1098
|
-
`delivery entitlement for ${customerId}`,
|
|
1099
|
-
schemas.imageDeliveryEntitlementRefusal(statement.predicate, distributionLock),
|
|
1100
|
-
);
|
|
1101
|
-
validateEntitlementStatementBinding({
|
|
1102
|
-
statement,
|
|
1103
|
-
completenessStatementDigest,
|
|
1104
|
-
customerId,
|
|
1105
|
-
distributionId,
|
|
1106
|
-
mode,
|
|
1107
|
-
targetRegistry,
|
|
1108
|
-
now,
|
|
1109
|
-
maxTtlSeconds: policy.maxEntitlementTtlSeconds,
|
|
1110
|
-
consumedNonces,
|
|
1111
|
-
});
|
|
1112
|
-
return statement;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
/**
|
|
1116
|
-
* Repository pins for the sources an EDITION declares itself.
|
|
1117
|
-
*
|
|
1118
|
-
* The runtime image source index pins the VENDOR fleet — every `repos/…` and
|
|
1119
|
-
* `submodules/…` repository the vendor builds images from. A customer edition
|
|
1120
|
-
* additionally draws biomes from sources declared in its own manifest
|
|
1121
|
-
* (`sources[]`, `kind: git`), and the images built from those have a `repo`
|
|
1122
|
-
* the index has never heard of and never will: `<customer>-biomes`, not
|
|
1123
|
-
* `repos/xema-base`. Without a pin for it every such subject is refused as
|
|
1124
|
-
* "has no committed repository pin", so a customer could verify the vendor's
|
|
1125
|
-
* images in its edition and never its own.
|
|
1126
|
-
*
|
|
1127
|
-
* The pin for an external source is two facts from two records, and each is
|
|
1128
|
-
* the record that OWNS the fact:
|
|
1129
|
-
*
|
|
1130
|
-
* * `repositoryUrl` — the edition manifest's declaration, by source name.
|
|
1131
|
-
* The manifest is read from the editions checkout and is HEAD-bound there
|
|
1132
|
-
* exactly like the locks (the verifier names it a required edition path).
|
|
1133
|
-
* * `sourceCommit` — the image lock's subjects. A declared `ref` may be a
|
|
1134
|
-
* branch; the lock records the exact commit the walk materialised, and it
|
|
1135
|
-
* is the authority for what was built. Every subject of one source must
|
|
1136
|
-
* agree on one commit, or the lock describes a walk that never happened.
|
|
1137
|
-
*
|
|
1138
|
-
* A declared source that ships no image yields no pin. A source whose name
|
|
1139
|
-
* collides with an index pin is refused: the index is the vendor's record and
|
|
1140
|
-
* an edition may not restate it. Non-git sources build no images from a
|
|
1141
|
-
* repository and yield no pin; a subject naming one stays unpinned and is
|
|
1142
|
-
* refused downstream, as before.
|
|
1143
|
-
*/
|
|
1144
|
-
export function externalSourceRepositoryPins({
|
|
1145
|
-
distribution,
|
|
1146
|
-
imageLock,
|
|
1147
|
-
repositoryPins,
|
|
1148
|
-
}) {
|
|
1149
|
-
const derived = new Map();
|
|
1150
|
-
for (const source of distribution.sources ?? []) {
|
|
1151
|
-
if (source.kind !== 'git') {continue;}
|
|
1152
|
-
if (repositoryPins.has(source.name)) {
|
|
1153
|
-
throw new Error(
|
|
1154
|
-
`edition source "${source.name}" shadows a repository pin the runtime image source index already commits`,
|
|
1155
|
-
);
|
|
1156
|
-
}
|
|
1157
|
-
const subjects = imageLock.subjects.filter(
|
|
1158
|
-
(subject) => subject.repo === source.name,
|
|
1159
|
-
);
|
|
1160
|
-
if (subjects.length === 0) {continue;}
|
|
1161
|
-
const commits = [...new Set(subjects.map((subject) => subject.sourceCommit))];
|
|
1162
|
-
if (commits.length !== 1) {
|
|
1163
|
-
throw new Error(
|
|
1164
|
-
`edition source "${source.name}" is built at ${commits.length} different commits in images.lock.json (${commits.join(', ')}); one source resolves to one commit`,
|
|
1165
|
-
);
|
|
1166
|
-
}
|
|
1167
|
-
derived.set(source.name, {
|
|
1168
|
-
repo: source.name,
|
|
1169
|
-
repositoryUrl: source.url,
|
|
1170
|
-
sourceCommit: commits[0],
|
|
1171
|
-
});
|
|
1172
|
-
}
|
|
1173
|
-
return derived;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
/**
|
|
1177
|
-
* The ONLY facts image evidence binds from `distribution.lock.json`, read
|
|
1178
|
-
* TOLERANTLY and nothing else.
|
|
1179
|
-
*
|
|
1180
|
-
* WHY THIS IS NOT `schemas.parseDistributionLock`. The standalone verifier is a
|
|
1181
|
-
* bundle of the kernel at its contract-authority commit, and that commit's lock
|
|
1182
|
-
* schema is the strict WRITER schema of its day. Measured 2026-10-01 against
|
|
1183
|
-
* the kernel at `24e9f12` (the authority every committed image lock names): a
|
|
1184
|
-
* committed lock parses, and the SAME lock with one additive biome key
|
|
1185
|
-
* (`provenance`) is refused — `[biomes.0] Unrecognized key: "provenance"`. So
|
|
1186
|
-
* every additive section the resolver ever learns would have made every
|
|
1187
|
-
* edition unverifiable until the verifier was rebuilt from a newer kernel, and
|
|
1188
|
-
* rebuilding it meant re-pinning the authority, which rewrites
|
|
1189
|
-
* `contractAuthority` in every `images.lock.json` and thereby invalidates every
|
|
1190
|
-
* signed producer statement and completeness digest. An edition's image
|
|
1191
|
-
* evidence would have been unsealed by a change that touched no image.
|
|
1192
|
-
*
|
|
1193
|
-
* What the evidence actually binds is two fields: the completeness predicate
|
|
1194
|
-
* names `distributionId` and `distributionInputHash`
|
|
1195
|
-
* (`validateCompletenessStatementBinding`), and every other caller reads the
|
|
1196
|
-
* same two. The lock's biome roster is the PRODUCER's contract, graded by
|
|
1197
|
-
* dist-check with the current kernel; the verifier never read it. So read
|
|
1198
|
-
* exactly those, validate exactly those, and ignore every other key — the
|
|
1199
|
-
* tolerant read-only posture `parseDistributionLockForReading` gives a
|
|
1200
|
-
* runtime, without depending on which kernel the bundle was built from.
|
|
1201
|
-
*
|
|
1202
|
-
* Two grammars exist, and which one is accepted is decided by `channel`, the
|
|
1203
|
-
* kernel evidence channel (`EVIDENCE_CHANNELS`) the caller operates on; it is
|
|
1204
|
-
* required and has no default. An UNSEALED lock (it carries `unsealedSchema`;
|
|
1205
|
-
* every edition lock on develop is one by design) is accepted ONLY on the
|
|
1206
|
-
* develop channel, read through the kernel's `parseUnsealedDistributionLock`.
|
|
1207
|
-
* Every other channel requires a SEALED lock: images sealed over an unsealed
|
|
1208
|
-
* lock is the crash state every sealed reader refuses, and a release statement
|
|
1209
|
-
* must never verify over it. A SEALED lock must have `schemaVersion` 5 (the
|
|
1210
|
-
* kernel's `DISTRIBUTION_LOCK_SCHEMA_VERSIONS`): a pre-v5 lock is refused, with
|
|
1211
|
-
* no compatibility path. Both carry `distributionId` and `inputHash`, and
|
|
1212
|
-
* sealing preserves them, so either way the binding is the same two fields.
|
|
1213
|
-
*/
|
|
1214
|
-
/** The one evidence channel an unsealed distribution lock is read on. */
|
|
1215
|
-
const UNSEALED_LOCK_CHANNEL = 'develop';
|
|
1216
|
-
|
|
1217
|
-
/**
|
|
1218
|
-
* The channel a distribution lock is read for: one of the kernel's
|
|
1219
|
-
* `EVIDENCE_CHANNELS`, stated by the caller. Missing or unknown is refused.
|
|
1220
|
-
*/
|
|
1221
|
-
function assertLockReadingChannel(channel, schemas) {
|
|
1222
|
-
if (!schemas) {
|
|
1223
|
-
throw new Error('trusted kernel distribution schemas are required');
|
|
1224
|
-
}
|
|
1225
|
-
if (!schemas.EVIDENCE_CHANNELS.includes(channel)) {
|
|
1226
|
-
throw new Error(
|
|
1227
|
-
`a distribution lock is read for one evidence channel (${schemas.EVIDENCE_CHANNELS.join(' | ')}); got ${JSON.stringify(channel)}`,
|
|
1228
|
-
);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
export function readDistributionLockBinding(raw, schemas, channel) {
|
|
1233
|
-
assertLockReadingChannel(channel, schemas);
|
|
1234
|
-
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
1235
|
-
throw new Error('distribution.lock.json is not a JSON object');
|
|
1236
|
-
}
|
|
1237
|
-
let source = raw;
|
|
1238
|
-
if ('unsealedSchema' in raw) {
|
|
1239
|
-
if (channel !== UNSEALED_LOCK_CHANNEL) {
|
|
1240
|
-
throw new Error(
|
|
1241
|
-
`distribution.lock.json is unsealed; the ${channel} channel accepts only a sealed lock (schemaVersion 5) — ` +
|
|
1242
|
-
`an unsealed lock is read on the ${UNSEALED_LOCK_CHANNEL} channel only`,
|
|
1243
|
-
);
|
|
1244
|
-
}
|
|
1245
|
-
source = schemas.parseUnsealedDistributionLock(raw);
|
|
1246
|
-
} else if (raw.schemaVersion !== 5) {
|
|
1247
|
-
throw new Error(
|
|
1248
|
-
`distribution.lock.json is neither an unsealed lock (carrying unsealedSchema) ` +
|
|
1249
|
-
`nor a sealed lock (schemaVersion 5); it has schemaVersion ${String(raw.schemaVersion)} (regenerate the lock)`,
|
|
1250
|
-
);
|
|
1251
|
-
}
|
|
1252
|
-
const { distributionId, inputHash } = source;
|
|
1253
|
-
// The id is format-checked only. Which edition it must equal is decided by
|
|
1254
|
-
// the bindings that USE it — the completeness predicate and the evidence set
|
|
1255
|
-
// compare it to the image lock and the signed statement — exactly as before
|
|
1256
|
-
// this reader existed; tightening that here would be a second, unasked rule.
|
|
1257
|
-
assertCanonicalDistributionId(distributionId);
|
|
1258
|
-
if (typeof inputHash !== 'string' || !/^[a-f0-9]{64}$/.test(inputHash)) {
|
|
1259
|
-
throw new Error(
|
|
1260
|
-
`distribution "${distributionId}" has invalid inputHash "${String(inputHash)}" — ` +
|
|
1261
|
-
'expected 64 lowercase sha256 hex characters',
|
|
1262
|
-
);
|
|
1263
|
-
}
|
|
1264
|
-
return Object.freeze({ distributionId, inputHash });
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
/**
|
|
1268
|
-
* The edition's sealed `distribution.lock.json`, parsed by the kernel's strict lock parser: the document the
|
|
1269
|
-
* predicates' `lockDigest` is computed over. Held to the binding rules of {@link readDistributionLockBinding} first.
|
|
1270
|
-
*/
|
|
1271
|
-
export function readEditionDistributionLock(editionsRoot, distributionId, schemas, channel) {
|
|
1272
|
-
assertLockReadingChannel(channel, schemas);
|
|
1273
|
-
const raw = JSON.parse(
|
|
1274
|
-
readFileSync(join(editionsRoot, distributionId, 'distribution.lock.json'), 'utf8'),
|
|
1275
|
-
);
|
|
1276
|
-
readDistributionLockBinding(raw, schemas, channel);
|
|
1277
|
-
// The WHOLE lock in its own grammar, never the two-field binding: the
|
|
1278
|
-
// predicate and entitlement refusals recompute distributionLockDigest over
|
|
1279
|
-
// what this returns, so an unsealed develop lock is digested as itself.
|
|
1280
|
-
return 'unsealedSchema' in raw ? schemas.parseUnsealedDistributionLock(raw) : schemas.parseDistributionLock(raw);
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
/**
|
|
1284
|
-
* Read and validate an edition's two locks plus the runtime image source index
|
|
1285
|
-
* they must agree with. The distribution lock is read for `channel`
|
|
1286
|
-
* ({@link readDistributionLockBinding}); the rest is {@link loadValidatedImageLocks}.
|
|
1287
|
-
*/
|
|
1288
|
-
export async function loadValidatedLocks(
|
|
1289
|
-
distributionId,
|
|
1290
|
-
dataRoot,
|
|
1291
|
-
schemas,
|
|
1292
|
-
{ editionsRoot, channel },
|
|
1293
|
-
) {
|
|
1294
|
-
if (!schemas) {
|
|
1295
|
-
throw new Error('trusted kernel distribution schemas are required');
|
|
1296
|
-
}
|
|
1297
|
-
const distributionLock = readEditionDistributionLock(
|
|
1298
|
-
editionsRoot,
|
|
1299
|
-
distributionId,
|
|
1300
|
-
schemas,
|
|
1301
|
-
channel,
|
|
1302
|
-
);
|
|
1303
|
-
return {
|
|
1304
|
-
distributionLock,
|
|
1305
|
-
...(await loadValidatedImageLocks(distributionId, dataRoot, schemas, { editionsRoot })),
|
|
1306
|
-
};
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
/**
|
|
1310
|
-
* Read and validate an edition's image lock plus the runtime image source index
|
|
1311
|
-
* it must agree with; the distribution lock is not read.
|
|
1312
|
-
*
|
|
1313
|
-
* `dataRoot` holds the runtime image source index, read from it always.
|
|
1314
|
-
* `editionsRoot` is where `<id>/images.lock.json` and
|
|
1315
|
-
* `<id>/xema-distribution.json` are read from. The manifest extends the
|
|
1316
|
-
* repository pins with the edition's own declared sources
|
|
1317
|
-
* ({@link externalSourceRepositoryPins}); the verifier grades the editions
|
|
1318
|
-
* checkout so the files it reads are HEAD-bound.
|
|
1319
|
-
*/
|
|
1320
|
-
export async function loadValidatedImageLocks(
|
|
1321
|
-
distributionId,
|
|
1322
|
-
dataRoot,
|
|
1323
|
-
schemas,
|
|
1324
|
-
{ editionsRoot },
|
|
1325
|
-
) {
|
|
1326
|
-
if (!schemas) {
|
|
1327
|
-
throw new Error('trusted kernel distribution schemas are required');
|
|
1328
|
-
}
|
|
1329
|
-
const imageLock = validateImmutableImageLock(
|
|
1330
|
-
schemas.parseImageLock(
|
|
1331
|
-
JSON.parse(
|
|
1332
|
-
readFileSync(
|
|
1333
|
-
join(editionsRoot, distributionId, 'images.lock.json'),
|
|
1334
|
-
'utf8',
|
|
1335
|
-
),
|
|
1336
|
-
),
|
|
1337
|
-
),
|
|
1338
|
-
schemas,
|
|
1339
|
-
);
|
|
1340
|
-
const runtimeSourceIndex = schemas.parseRuntimeImageSourceIndex(
|
|
1341
|
-
JSON.parse(
|
|
1342
|
-
readFileSync(
|
|
1343
|
-
join(dataRoot, 'runtime-image-source-index.json'),
|
|
1344
|
-
'utf8',
|
|
1345
|
-
),
|
|
1346
|
-
),
|
|
1347
|
-
);
|
|
1348
|
-
if (
|
|
1349
|
-
canonicalJson(imageLock.contractAuthority) !==
|
|
1350
|
-
canonicalJson(runtimeSourceIndex.contractAuthority)
|
|
1351
|
-
) {
|
|
1352
|
-
throw new Error(
|
|
1353
|
-
'images.lock.json kernel contract authority does not exactly match runtime-image-source-index.json',
|
|
1354
|
-
);
|
|
1355
|
-
}
|
|
1356
|
-
// The definition's `sourcePins` (the repositories this edition holds at an exact commit) is parsed by the
|
|
1357
|
-
// kernel with the rest of it. Each subject is graded against the EFFECTIVE pin of THIS edition. Pure: no
|
|
1358
|
-
// git, no sources root (this runs inside the built verifier).
|
|
1359
|
-
const distribution = schemas.parseDistribution(
|
|
1360
|
-
JSON.parse(
|
|
1361
|
-
readFileSync(
|
|
1362
|
-
join(editionsRoot, distributionId, 'xema-distribution.json'),
|
|
1363
|
-
'utf8',
|
|
1364
|
-
),
|
|
1365
|
-
),
|
|
1366
|
-
);
|
|
1367
|
-
const sourcePins = manifestSourcePins(distribution);
|
|
1368
|
-
const repositoryPins = new Map(
|
|
1369
|
-
effectiveRepositoryPins(runtimeSourceIndex.repositoryPins, sourcePins, { id: distributionId, contractAuthority: runtimeSourceIndex.contractAuthority }).map((repository) => [
|
|
1370
|
-
repository.repo,
|
|
1371
|
-
repository,
|
|
1372
|
-
]),
|
|
1373
|
-
);
|
|
1374
|
-
if (distribution.id !== distributionId) {
|
|
1375
|
-
throw new Error(
|
|
1376
|
-
`edition manifest at ${editionsRoot} declares id "${distribution.id}", not "${distributionId}"`,
|
|
1377
|
-
);
|
|
1378
|
-
}
|
|
1379
|
-
for (const [repo, pin] of externalSourceRepositoryPins({
|
|
1380
|
-
distribution,
|
|
1381
|
-
imageLock,
|
|
1382
|
-
repositoryPins,
|
|
1383
|
-
})) {
|
|
1384
|
-
repositoryPins.set(repo, pin);
|
|
1385
|
-
}
|
|
1386
|
-
// Third-party OCI sources: pinned by the data root's third-party source pins (held to the evidence trust pin by the entry).
|
|
1387
|
-
const thirdPartyPins = loadThirdPartySourcePins(dataRoot);
|
|
1388
|
-
const thirdPartyRepositoryPins = thirdPartySourceRepositoryPins({
|
|
1389
|
-
distribution,
|
|
1390
|
-
imageLock,
|
|
1391
|
-
repositoryPins,
|
|
1392
|
-
thirdPartyPins,
|
|
1393
|
-
});
|
|
1394
|
-
for (const [repo, pin] of thirdPartyRepositoryPins) {
|
|
1395
|
-
repositoryPins.set(repo, pin);
|
|
1396
|
-
}
|
|
1397
|
-
return {
|
|
1398
|
-
schemas,
|
|
1399
|
-
imageLock,
|
|
1400
|
-
runtimeSourceIndex,
|
|
1401
|
-
repositoryPins,
|
|
1402
|
-
thirdPartySources: thirdPartySourcesByRepository(thirdPartyRepositoryPins, thirdPartyPins),
|
|
1403
|
-
};
|
|
1404
|
-
}
|