@kungfu-tech/buildchain 3.0.9-alpha.8 → 4.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +39 -0
- package/README.md +17 -13
- package/actions/promote-buildchain-ref/README.md +0 -10
- package/actions/release-tail/README.md +6 -10
- package/architecture/internal-capabilities.json +502 -0
- package/architecture/maintainability-baseline.json +59 -0
- package/architecture/maintainability-policy.json +1384 -0
- package/architecture/release-tail-contract-inventory.json +587 -0
- package/architecture/v3-core-mechanism-inventory.json +1043 -0
- package/architecture/v3-core-mechanism-inventory.md +67 -0
- package/architecture/v4-adopter-delivery-parity.json +47 -0
- package/architecture/v4-architecture-constitution.md +210 -0
- package/architecture/v4-bootstrap-authority.json +56 -0
- package/architecture/v4-canonical-contract-fixtures.json +105 -0
- package/architecture/v4-capability-state-machine-manifest.json +480 -0
- package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
- package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
- package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
- package/architecture/v4-exception-ledger.json +5 -0
- package/architecture/v4-exception-ledger.schema.json +56 -0
- package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
- package/architecture/v4-platform-stage-checkpoints.json +97 -0
- package/architecture/v4-provider-operation-journal-contract.json +113 -0
- package/architecture/v4-release-activation-shadow-domain.json +61 -0
- package/architecture/v4-release-train-parity.json +68 -0
- package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
- package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
- package/architecture/v4-stable-publication-fence.json +66 -0
- package/architecture/v4-stage-capsule-contract.json +90 -0
- package/architecture/v4-stage-capsule-qualification.json +99 -0
- package/architecture/v4-stage-capsule-resume-planner.json +52 -0
- package/architecture/v4-stage-capsule-store-contract.json +72 -0
- package/bin/buildchain.mjs +15 -6
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/buildchain-v2-residuals-v1.json +2 -2
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
- package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
- package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
- package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
- package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
- package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
- package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
- package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
- package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
- package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
- package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
- package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
- package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
- package/contracts/v4-host-contract-v1.schema.json +135 -0
- package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
- package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
- package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
- package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
- package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
- package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
- package/contracts/v4-stage-capsule-v1.schema.json +178 -0
- package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
- package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
- package/crates/buildchain-v4-bridge/src/main.rs +495 -0
- package/dist/site/agent-index.json +0 -3
- package/dist/site/artifact-schemas.json +0 -6
- package/dist/site/buildchain-contract.json +103 -397
- package/dist/site/buildchain-site.json +302 -270
- package/dist/site/capability-registry.json +9 -11
- package/dist/site/cli-registry.json +8 -30
- package/dist/site/controller-registry.json +48 -8
- package/dist/site/kfd-claims.json +129 -142
- package/dist/site/kfd-upstream-aggregate.json +9 -9
- package/dist/site/manual-registry.json +18 -49
- package/dist/site/node-api-registry.json +1882 -4118
- package/dist/site/page-registry.json +278 -230
- package/dist/site/public-surface-audit.json +87 -185
- package/dist/site/publication-authority-registry.json +11 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +3 -7
- package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
- package/dist/site/site-manifest.json +22 -38
- package/dist/site/workflow-registry.json +80 -35
- package/docs/MAP.md +8 -4
- package/docs/auditable-demo.md +2 -2
- package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
- package/docs/cli-reference.md +15 -92
- package/docs/cli.md +14 -14
- package/docs/dev-alpha-candidate-patrol.md +3 -11
- package/docs/dev-delivery-warrant.md +28 -100
- package/docs/engineering-housekeeper.md +12 -4
- package/docs/getting-started.md +1 -1
- package/docs/install.md +5 -5
- package/docs/kfd-support.md +2 -7
- package/docs/lifecycle-protocol.md +2 -4
- package/docs/node-api-reference.md +414 -523
- package/docs/release-flow.md +31 -31
- package/docs/release-governance.md +16 -28
- package/docs/release-passport.md +2 -15
- package/docs/release-tail-contract.md +13 -6
- package/docs/release-tail-provider-plane.md +13 -19
- package/docs/release-train.md +4 -4
- package/docs/reusable-build-surface.md +14 -27
- package/docs/site-bundle-contract.md +1 -5
- package/docs/stable-candidate-patrol.md +7 -7
- package/docs/v4-canonical-contracts.md +92 -0
- package/docs/v4-delivery-warrant-read-candidate.md +73 -0
- package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
- package/docs/v4-production-release.md +63 -0
- package/docs/v4-stage-capsule.md +193 -0
- package/docs/versioning.md +3 -3
- package/package.json +19 -17
- package/packages/core/adopter-delivery-gate.js +576 -0
- package/packages/core/adopter-delivery-json.js +66 -0
- package/packages/core/artifact-signing.js +0 -61
- package/packages/core/artifact-verification-envelope.js +5 -5
- package/packages/core/build-facts.js +12 -4
- package/packages/core/buildchain-agent-manuals.js +0 -2
- package/packages/core/buildchain-config.js +4 -69
- package/packages/core/buildchain-contract.js +232 -46
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/channel-candidate.js +0 -8
- package/packages/core/channel-promotion-baseline.js +0 -52
- package/packages/core/controller-evidence.js +3 -4
- package/packages/core/dev-alpha-candidate-selection.js +2 -10
- package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
- package/packages/core/dev-delivery-warrant-settlement.js +1 -5
- package/packages/core/dev-delivery-warrant.js +12 -52
- package/packages/core/diagnostics.js +3 -8
- package/packages/core/github-governance-authority.js +3 -1
- package/packages/core/kfd-adopter-category-driver.js +167 -0
- package/packages/core/kfd-adopter-manifest.js +47 -46
- package/packages/core/kfd-gate.js +15 -45
- package/packages/core/paper-agent-entry.js +7 -16
- package/packages/core/paper-fleet.js +2 -2
- package/packages/core/paper-repository.js +2 -3
- package/packages/core/paper-scaffold-content.js +2 -23
- package/packages/core/paper.js +11 -43
- package/packages/core/publication-reproducibility.js +4 -4
- package/packages/core/release-line-bootstrap.js +21 -8
- package/packages/core/release-passport-contract.js +5 -5
- package/packages/core/release-passport.js +20 -131
- package/packages/core/self-dogfood-version.js +48 -35
- package/packages/core/spawn-command.js +232 -8
- package/packages/core/v4-adopter-delivery-parity.js +219 -0
- package/packages/core/v4-canonical-contracts.js +284 -0
- package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
- package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
- package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
- package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
- package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
- package/packages/core/v4-platform-stage-checkpoints.js +471 -0
- package/packages/core/v4-provider-operation-journal.js +531 -0
- package/packages/core/v4-provider-readback-idempotency.js +394 -0
- package/packages/core/v4-release-activation-shadow.js +519 -0
- package/packages/core/v4-stable-publication-fence.js +357 -0
- package/packages/core/v4-stage-capsule-local-store.js +426 -0
- package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
- package/packages/core/v4-stage-capsule-qualification.js +456 -0
- package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
- package/packages/core/v4-stage-capsule-store.js +401 -0
- package/packages/core/v4-stage-capsule.js +319 -0
- package/scripts/assemble-self-publication-admission.mjs +1 -1
- package/scripts/audit-publication-control-plane.mjs +5 -5
- package/scripts/auditable-demo-bundle-verification.mjs +3 -2
- package/scripts/auditable-demo-platform.mjs +2 -2
- package/scripts/auditable-demo-renditions.mjs +1 -1
- package/scripts/auditable-demo.mjs +2 -2
- package/scripts/aws-macos-jit-controller-core.mjs +13 -346
- package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
- package/scripts/aws-macos-jit-controller.mjs +37 -275
- package/scripts/aws-macos-jit-core.mjs +3 -41
- package/scripts/aws-macos-jit.mjs +0 -12
- package/scripts/aws-windows-jit-campaign.mjs +2 -2
- package/scripts/aws-windows-jit-controller-core.mjs +1 -1
- package/scripts/aws-windows-jit-controller.mjs +3 -3
- package/scripts/build-contract-core.mjs +3 -8
- package/scripts/build-standalone-binary.mjs +3 -14
- package/scripts/buildchain-channel-router.mjs +4 -0
- package/scripts/buildchain-cli-help.mjs +7 -13
- package/scripts/buildchain-contract-lock.mjs +0 -6
- package/scripts/check-action-bundles.mjs +3 -4
- package/scripts/check-internal-architecture.mjs +7 -6
- package/scripts/check-inventory.mjs +30 -25
- package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
- package/scripts/create-release-bundle.mjs +8 -4
- package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
- package/scripts/dev-delivery-proof.mjs +2 -54
- package/scripts/dev-delivery-warrant.mjs +59 -77
- package/scripts/dev-pr-auto-merge.mjs +4 -30
- package/scripts/dev-pr-delivery-warrant.mjs +0 -55
- package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
- package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
- package/scripts/generate-channel-build-workflow.mjs +54 -24
- package/scripts/generate-channel-promotion-workflow.mjs +15 -6
- package/scripts/generate-site-bundle.mjs +5 -21
- package/scripts/git-fetch-process-tree.mjs +34 -1
- package/scripts/init-repo.mjs +64 -159
- package/scripts/inspect-artifact-signing-requests.mjs +0 -6
- package/scripts/locked-source-checkout.mjs +3 -4
- package/scripts/maintainability-metrics.mjs +8 -2
- package/scripts/npm-publish-dry-run.mjs +2 -2
- package/scripts/npm-publish-transaction.mjs +2 -2
- package/scripts/publication-commit-evidence.mjs +23 -69
- package/scripts/release-candidate-resolver.mjs +11 -17
- package/scripts/release-tail.mjs +3 -49
- package/scripts/resume-from-candidate-run.mjs +9 -123
- package/scripts/run-lifecycle-core.mjs +12 -13
- package/scripts/seal-artifact-signing-requests.mjs +17 -21
- package/scripts/site-capability-metadata.mjs +2 -7
- package/scripts/stable-candidate-qualification.mjs +10 -10
- package/scripts/v4-architecture.mjs +12 -2
- package/scripts/v4-bridge-bootstrap.mjs +141 -0
- package/scripts/v4-bridge-evidence.mjs +141 -0
- package/scripts/v4-host-adapter.mjs +182 -0
- package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
- package/scripts/v4-stage-capsule-qualification.mjs +528 -0
- package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
- package/scripts/v4-warrant-shadow-plan.mjs +601 -0
- package/scripts/verify-golden-path.mjs +5 -5
- package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
- package/scripts/web-surface-core.mjs +2 -8
- package/scripts/workflow-call-contract.mjs +5 -184
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
- package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
- package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
- package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
- package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
- package/contracts/next-development-request-v1.schema.json +0 -66
- package/contracts/next-development-transition-v1.schema.json +0 -292
- package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
- package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
- package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
- package/docs/next-development-transition.md +0 -119
- package/docs/publication-rehearsal.md +0 -94
- package/packages/core/buildchain-compatibility-proof.js +0 -631
- package/packages/core/dev-delivery-native-proof.js +0 -333
- package/packages/core/dev-delivery-warrant-shadow.js +0 -502
- package/packages/core/next-development-candidate-reservation.js +0 -186
- package/packages/core/next-development-controller.js +0 -726
- package/packages/core/next-development-projection.js +0 -288
- package/packages/core/next-development-transition.js +0 -738
- package/packages/core/publication-rehearsal-projection.js +0 -173
- package/packages/core/publication-rehearsal-runtime.js +0 -921
- package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
- package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
- package/scripts/dev-delivery-native-run.mjs +0 -187
- package/scripts/dev-delivery-two-phase.mjs +0 -598
- package/scripts/generate-next-development-guidance.mjs +0 -49
- package/scripts/materialize-self-release-candidate-version.mjs +0 -137
- package/scripts/next-development-self-dogfood-harness.mjs +0 -409
- package/scripts/next-development-self-dogfood.mjs +0 -532
- package/scripts/next-development-transition.mjs +0 -47
- package/scripts/release-candidate-tail-reseal.mjs +0 -426
- package/templates/native-dev-delivery.yml +0 -82
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
import {
|
|
2
|
+
adopterDeliveryGateDigest,
|
|
3
|
+
isAdopterDeliveryJsonValue,
|
|
4
|
+
} from "./adopter-delivery-json.js";
|
|
5
|
+
|
|
6
|
+
export { adopterDeliveryGateDigest } from "./adopter-delivery-json.js";
|
|
7
|
+
|
|
8
|
+
export const ADOPTER_DELIVERY_GATE_REQUEST_CONTRACT =
|
|
9
|
+
"kungfu-buildchain-adopter-delivery-request";
|
|
10
|
+
export const ADOPTER_DELIVERY_GATE_RESULT_CONTRACT =
|
|
11
|
+
"kungfu-buildchain-adopter-delivery-result";
|
|
12
|
+
export const ADOPTER_PROTOCOL_DRIVER_INTERFACE =
|
|
13
|
+
"kungfu-buildchain-adopter-protocol-driver/v1";
|
|
14
|
+
export const ADOPTER_ARTIFACT_PROFILE_INTERFACE =
|
|
15
|
+
"kungfu-buildchain-adopter-artifact-profile/v1";
|
|
16
|
+
|
|
17
|
+
const ROOT_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
18
|
+
const ID_PATTERN = /^[a-z][a-z0-9.-]*(?:\/[a-z][a-z0-9.-]*)*$/;
|
|
19
|
+
const VERSION_PATTERN = /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/;
|
|
20
|
+
const GIT_COMMIT_COORDINATE_PATTERN = /^[^@\s]+@[0-9a-f]{40}$/;
|
|
21
|
+
const PACKAGE_COORDINATE_PATTERN =
|
|
22
|
+
/^(?:@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*|[a-z0-9][a-z0-9._-]*)@[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
23
|
+
|
|
24
|
+
function issue(code, path, message) {
|
|
25
|
+
return { code, path, message };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function compareIssues(left, right) {
|
|
29
|
+
return Buffer.compare(
|
|
30
|
+
Buffer.from(`${left.code}\0${left.path}\0${left.message}`, "utf8"),
|
|
31
|
+
Buffer.from(`${right.code}\0${right.path}\0${right.message}`, "utf8"),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function exactObject(value, path, fields, issues) {
|
|
36
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
37
|
+
issues.push(
|
|
38
|
+
issue("delivery-input-invalid", path, "Expected a JSON object."),
|
|
39
|
+
);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const admitted = new Set(fields);
|
|
43
|
+
for (const key of Object.keys(value)) {
|
|
44
|
+
if (!admitted.has(key)) {
|
|
45
|
+
issues.push(
|
|
46
|
+
issue(
|
|
47
|
+
"delivery-input-invalid",
|
|
48
|
+
`${path}/${key}`,
|
|
49
|
+
"Unknown fields fail closed.",
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const key of fields) {
|
|
55
|
+
if (!Object.hasOwn(value, key)) {
|
|
56
|
+
issues.push(
|
|
57
|
+
issue(
|
|
58
|
+
"delivery-input-invalid",
|
|
59
|
+
`${path}/${key}`,
|
|
60
|
+
"Required field is missing.",
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function exactReference(value, path, issues) {
|
|
69
|
+
if (!exactObject(value, path, ["id", "version"], issues)) return;
|
|
70
|
+
if (!ID_PATTERN.test(value.id ?? "")) {
|
|
71
|
+
issues.push(
|
|
72
|
+
issue("delivery-input-invalid", `${path}/id`, "Identifier is invalid."),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (!VERSION_PATTERN.test(value.version ?? "")) {
|
|
76
|
+
issues.push(
|
|
77
|
+
issue("delivery-input-invalid", `${path}/version`, "Version is invalid."),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function validateRequest(request) {
|
|
83
|
+
const issues = [];
|
|
84
|
+
if (
|
|
85
|
+
!exactObject(
|
|
86
|
+
request,
|
|
87
|
+
"/",
|
|
88
|
+
[
|
|
89
|
+
"schemaVersion",
|
|
90
|
+
"contract",
|
|
91
|
+
"protocol",
|
|
92
|
+
"artifactProfile",
|
|
93
|
+
"project",
|
|
94
|
+
"artifact",
|
|
95
|
+
"declaration",
|
|
96
|
+
],
|
|
97
|
+
issues,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
return issues;
|
|
101
|
+
if (
|
|
102
|
+
request.schemaVersion !== 1 ||
|
|
103
|
+
request.contract !== ADOPTER_DELIVERY_GATE_REQUEST_CONTRACT
|
|
104
|
+
) {
|
|
105
|
+
issues.push(
|
|
106
|
+
issue(
|
|
107
|
+
"delivery-input-invalid",
|
|
108
|
+
"/contract",
|
|
109
|
+
"Delivery request contract is unsupported.",
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
exactReference(request.protocol, "/protocol", issues);
|
|
114
|
+
exactReference(request.artifactProfile, "/artifactProfile", issues);
|
|
115
|
+
if (
|
|
116
|
+
exactObject(
|
|
117
|
+
request.project,
|
|
118
|
+
"/project",
|
|
119
|
+
["instanceId", "adopterId"],
|
|
120
|
+
issues,
|
|
121
|
+
)
|
|
122
|
+
) {
|
|
123
|
+
for (const field of ["instanceId", "adopterId"]) {
|
|
124
|
+
if (
|
|
125
|
+
typeof request.project[field] !== "string" ||
|
|
126
|
+
request.project[field].length === 0
|
|
127
|
+
) {
|
|
128
|
+
issues.push(
|
|
129
|
+
issue(
|
|
130
|
+
"delivery-input-invalid",
|
|
131
|
+
`/project/${field}`,
|
|
132
|
+
"Expected a non-empty string.",
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (
|
|
139
|
+
exactObject(
|
|
140
|
+
request.artifact,
|
|
141
|
+
"/artifact",
|
|
142
|
+
["kind", "coordinate", "root"],
|
|
143
|
+
issues,
|
|
144
|
+
)
|
|
145
|
+
) {
|
|
146
|
+
for (const field of ["kind", "coordinate"]) {
|
|
147
|
+
if (
|
|
148
|
+
typeof request.artifact[field] !== "string" ||
|
|
149
|
+
request.artifact[field].length === 0
|
|
150
|
+
) {
|
|
151
|
+
issues.push(
|
|
152
|
+
issue(
|
|
153
|
+
"delivery-input-invalid",
|
|
154
|
+
`/artifact/${field}`,
|
|
155
|
+
"Expected a non-empty string.",
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!ROOT_PATTERN.test(request.artifact.root ?? "")) {
|
|
161
|
+
issues.push(
|
|
162
|
+
issue(
|
|
163
|
+
"delivery-input-invalid",
|
|
164
|
+
"/artifact/root",
|
|
165
|
+
"Expected a canonical lowercase sha256 root.",
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (
|
|
171
|
+
!request.declaration ||
|
|
172
|
+
typeof request.declaration !== "object" ||
|
|
173
|
+
Array.isArray(request.declaration) ||
|
|
174
|
+
!isAdopterDeliveryJsonValue(request.declaration)
|
|
175
|
+
) {
|
|
176
|
+
issues.push(
|
|
177
|
+
issue(
|
|
178
|
+
"delivery-input-invalid",
|
|
179
|
+
"/declaration",
|
|
180
|
+
"Expected a finite acyclic protocol-owned JSON object.",
|
|
181
|
+
),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return issues;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function definitionReference(definition, interfaceContract, label) {
|
|
188
|
+
if (
|
|
189
|
+
!definition ||
|
|
190
|
+
typeof definition !== "object" ||
|
|
191
|
+
Array.isArray(definition)
|
|
192
|
+
) {
|
|
193
|
+
throw new TypeError(`${label} must be an object`);
|
|
194
|
+
}
|
|
195
|
+
if (definition.interface !== interfaceContract) {
|
|
196
|
+
throw new TypeError(`${label}.interface must be ${interfaceContract}`);
|
|
197
|
+
}
|
|
198
|
+
if (
|
|
199
|
+
!ID_PATTERN.test(definition.id ?? "") ||
|
|
200
|
+
!VERSION_PATTERN.test(definition.version ?? "")
|
|
201
|
+
) {
|
|
202
|
+
throw new TypeError(`${label} identity or version is invalid`);
|
|
203
|
+
}
|
|
204
|
+
if (typeof definition.verify !== "function") {
|
|
205
|
+
throw new TypeError(`${label}.verify must be a function`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function validIssue(entry) {
|
|
210
|
+
return (
|
|
211
|
+
entry &&
|
|
212
|
+
typeof entry === "object" &&
|
|
213
|
+
!Array.isArray(entry) &&
|
|
214
|
+
typeof entry.code === "string" &&
|
|
215
|
+
entry.code.length > 0 &&
|
|
216
|
+
typeof entry.path === "string" &&
|
|
217
|
+
typeof entry.message === "string" &&
|
|
218
|
+
entry.message.length > 0
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function defineAdopterProtocolDriver(definition) {
|
|
223
|
+
definitionReference(
|
|
224
|
+
definition,
|
|
225
|
+
ADOPTER_PROTOCOL_DRIVER_INTERFACE,
|
|
226
|
+
"protocol driver",
|
|
227
|
+
);
|
|
228
|
+
return Object.freeze({
|
|
229
|
+
interface: definition.interface,
|
|
230
|
+
id: definition.id,
|
|
231
|
+
version: definition.version,
|
|
232
|
+
verify: definition.verify,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function defineAdopterArtifactProfile(definition) {
|
|
237
|
+
definitionReference(
|
|
238
|
+
definition,
|
|
239
|
+
ADOPTER_ARTIFACT_PROFILE_INTERFACE,
|
|
240
|
+
"artifact profile",
|
|
241
|
+
);
|
|
242
|
+
if (
|
|
243
|
+
!Array.isArray(definition.kinds) ||
|
|
244
|
+
definition.kinds.length === 0 ||
|
|
245
|
+
definition.kinds.some(
|
|
246
|
+
(kind) => typeof kind !== "string" || kind.length === 0,
|
|
247
|
+
) ||
|
|
248
|
+
new Set(definition.kinds).size !== definition.kinds.length
|
|
249
|
+
) {
|
|
250
|
+
throw new TypeError(
|
|
251
|
+
"artifact profile kinds must be a non-empty unique string array",
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
return Object.freeze({
|
|
255
|
+
interface: definition.interface,
|
|
256
|
+
id: definition.id,
|
|
257
|
+
version: definition.version,
|
|
258
|
+
kinds: Object.freeze([...definition.kinds]),
|
|
259
|
+
verify: definition.verify,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function coordinateProfile({ id, version, kind, pattern, message }) {
|
|
264
|
+
return defineAdopterArtifactProfile({
|
|
265
|
+
interface: ADOPTER_ARTIFACT_PROFILE_INTERFACE,
|
|
266
|
+
id,
|
|
267
|
+
version,
|
|
268
|
+
kinds: [kind],
|
|
269
|
+
verify(artifact) {
|
|
270
|
+
const issues = [];
|
|
271
|
+
if (!pattern.test(artifact.coordinate)) {
|
|
272
|
+
issues.push(
|
|
273
|
+
issue("delivery-artifact-invalid", "/artifact/coordinate", message),
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
return { valid: issues.length === 0, issues };
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function createGitCommitArtifactProfile({
|
|
282
|
+
id = "buildchain.artifact/git-commit",
|
|
283
|
+
version = "1.0.0",
|
|
284
|
+
} = {}) {
|
|
285
|
+
return coordinateProfile({
|
|
286
|
+
id,
|
|
287
|
+
version,
|
|
288
|
+
kind: "git-commit",
|
|
289
|
+
pattern: GIT_COMMIT_COORDINATE_PATTERN,
|
|
290
|
+
message:
|
|
291
|
+
"Git commit coordinates must bind one repository and exact lowercase 40-hex commit.",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function createPackageArtifactProfile({
|
|
296
|
+
id = "buildchain.artifact/package",
|
|
297
|
+
version = "1.0.0",
|
|
298
|
+
} = {}) {
|
|
299
|
+
return coordinateProfile({
|
|
300
|
+
id,
|
|
301
|
+
version,
|
|
302
|
+
kind: "package",
|
|
303
|
+
pattern: PACKAGE_COORDINATE_PATTERN,
|
|
304
|
+
message:
|
|
305
|
+
"Package coordinates must bind one package name and immutable version.",
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function registry(definitions, label) {
|
|
310
|
+
const result = new Map();
|
|
311
|
+
for (const definition of definitions) {
|
|
312
|
+
const key = `${definition.id}@${definition.version}`;
|
|
313
|
+
if (result.has(key)) throw new TypeError(`${label} ${key} is duplicated`);
|
|
314
|
+
result.set(key, definition);
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function normalizeProfileResult(result) {
|
|
320
|
+
if (
|
|
321
|
+
!result ||
|
|
322
|
+
typeof result !== "object" ||
|
|
323
|
+
Array.isArray(result) ||
|
|
324
|
+
typeof result.valid !== "boolean" ||
|
|
325
|
+
!Array.isArray(result.issues) ||
|
|
326
|
+
result.issues.some((entry) => !validIssue(entry))
|
|
327
|
+
) {
|
|
328
|
+
return {
|
|
329
|
+
valid: false,
|
|
330
|
+
issues: [
|
|
331
|
+
issue(
|
|
332
|
+
"delivery-artifact-profile-result-invalid",
|
|
333
|
+
"/artifactProfile",
|
|
334
|
+
"Artifact profile returned an invalid result.",
|
|
335
|
+
),
|
|
336
|
+
],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
valid: result.valid,
|
|
341
|
+
issues: result.issues.map(({ code, path, message }) => ({
|
|
342
|
+
code,
|
|
343
|
+
path,
|
|
344
|
+
message,
|
|
345
|
+
})),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function normalizeDriverResult(result) {
|
|
350
|
+
if (
|
|
351
|
+
!result ||
|
|
352
|
+
typeof result !== "object" ||
|
|
353
|
+
Array.isArray(result) ||
|
|
354
|
+
typeof result.valid !== "boolean" ||
|
|
355
|
+
!ROOT_PATTERN.test(result.reportRoot ?? "") ||
|
|
356
|
+
!Array.isArray(result.issues) ||
|
|
357
|
+
result.issues.some((entry) => !validIssue(entry)) ||
|
|
358
|
+
!Object.hasOwn(result, "report") ||
|
|
359
|
+
!isAdopterDeliveryJsonValue(result.report)
|
|
360
|
+
) {
|
|
361
|
+
return {
|
|
362
|
+
valid: false,
|
|
363
|
+
report: null,
|
|
364
|
+
reportRoot: null,
|
|
365
|
+
issues: [
|
|
366
|
+
issue(
|
|
367
|
+
"delivery-driver-result-invalid",
|
|
368
|
+
"/protocol",
|
|
369
|
+
"Protocol driver returned an invalid result.",
|
|
370
|
+
),
|
|
371
|
+
],
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
valid: result.valid,
|
|
376
|
+
report: structuredClone(result.report),
|
|
377
|
+
reportRoot: result.reportRoot,
|
|
378
|
+
issues: result.issues.map(({ code, path, message }) => ({
|
|
379
|
+
code,
|
|
380
|
+
path,
|
|
381
|
+
message,
|
|
382
|
+
})),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function gateResult(
|
|
387
|
+
request,
|
|
388
|
+
protocolDriver,
|
|
389
|
+
artifactProfile,
|
|
390
|
+
profileResult,
|
|
391
|
+
driverResult,
|
|
392
|
+
issues,
|
|
393
|
+
) {
|
|
394
|
+
const result = {
|
|
395
|
+
schemaVersion: 1,
|
|
396
|
+
contract: ADOPTER_DELIVERY_GATE_RESULT_CONTRACT,
|
|
397
|
+
status: issues.length === 0 ? "passed" : "failed",
|
|
398
|
+
qualifying: false,
|
|
399
|
+
selfCertified: false,
|
|
400
|
+
project: request?.project ? structuredClone(request.project) : null,
|
|
401
|
+
artifact: request?.artifact ? structuredClone(request.artifact) : null,
|
|
402
|
+
protocol: request?.protocol
|
|
403
|
+
? {
|
|
404
|
+
...structuredClone(request.protocol),
|
|
405
|
+
loaded: Boolean(protocolDriver),
|
|
406
|
+
reportRoot: driverResult?.reportRoot ?? null,
|
|
407
|
+
}
|
|
408
|
+
: null,
|
|
409
|
+
artifactProfile: request?.artifactProfile
|
|
410
|
+
? {
|
|
411
|
+
...structuredClone(request.artifactProfile),
|
|
412
|
+
loaded: Boolean(artifactProfile),
|
|
413
|
+
valid: profileResult?.valid ?? false,
|
|
414
|
+
}
|
|
415
|
+
: null,
|
|
416
|
+
semanticReport: driverResult?.report ?? null,
|
|
417
|
+
nonClaims: [
|
|
418
|
+
"A passing delivery gate does not grant runtime permission, release authorization, or independent certification.",
|
|
419
|
+
"Protocol semantics remain owned by the selected protocol driver and its published authority.",
|
|
420
|
+
],
|
|
421
|
+
issues: [...issues].sort(compareIssues),
|
|
422
|
+
};
|
|
423
|
+
result.gateRoot = adopterDeliveryGateDigest(result);
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function createAdopterDeliveryGate({
|
|
428
|
+
drivers = [],
|
|
429
|
+
artifactProfiles = [],
|
|
430
|
+
} = {}) {
|
|
431
|
+
if (!Array.isArray(drivers) || !Array.isArray(artifactProfiles)) {
|
|
432
|
+
throw new TypeError("drivers and artifactProfiles must be arrays");
|
|
433
|
+
}
|
|
434
|
+
drivers.forEach((driver) =>
|
|
435
|
+
definitionReference(
|
|
436
|
+
driver,
|
|
437
|
+
ADOPTER_PROTOCOL_DRIVER_INTERFACE,
|
|
438
|
+
"protocol driver",
|
|
439
|
+
),
|
|
440
|
+
);
|
|
441
|
+
artifactProfiles.forEach((profile) => {
|
|
442
|
+
definitionReference(
|
|
443
|
+
profile,
|
|
444
|
+
ADOPTER_ARTIFACT_PROFILE_INTERFACE,
|
|
445
|
+
"artifact profile",
|
|
446
|
+
);
|
|
447
|
+
if (
|
|
448
|
+
!Array.isArray(profile.kinds) ||
|
|
449
|
+
profile.kinds.length === 0 ||
|
|
450
|
+
profile.kinds.some(
|
|
451
|
+
(kind) => typeof kind !== "string" || kind.length === 0,
|
|
452
|
+
) ||
|
|
453
|
+
new Set(profile.kinds).size !== profile.kinds.length
|
|
454
|
+
) {
|
|
455
|
+
throw new TypeError(
|
|
456
|
+
"artifact profile kinds must be a non-empty unique string array",
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
const driverRegistry = registry(drivers, "protocol driver");
|
|
461
|
+
const profileRegistry = registry(artifactProfiles, "artifact profile");
|
|
462
|
+
|
|
463
|
+
return Object.freeze({
|
|
464
|
+
evaluate(request, context = {}) {
|
|
465
|
+
const issues = validateRequest(request);
|
|
466
|
+
if (issues.length > 0)
|
|
467
|
+
return gateResult(request, null, null, null, null, issues);
|
|
468
|
+
|
|
469
|
+
const driver = driverRegistry.get(
|
|
470
|
+
`${request.protocol.id}@${request.protocol.version}`,
|
|
471
|
+
);
|
|
472
|
+
if (!driver) {
|
|
473
|
+
issues.push(
|
|
474
|
+
issue(
|
|
475
|
+
"delivery-driver-unknown",
|
|
476
|
+
"/protocol",
|
|
477
|
+
"No exact protocol driver is registered.",
|
|
478
|
+
),
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
const profile = profileRegistry.get(
|
|
482
|
+
`${request.artifactProfile.id}@${request.artifactProfile.version}`,
|
|
483
|
+
);
|
|
484
|
+
if (!profile) {
|
|
485
|
+
issues.push(
|
|
486
|
+
issue(
|
|
487
|
+
"delivery-artifact-profile-unknown",
|
|
488
|
+
"/artifactProfile",
|
|
489
|
+
"No exact artifact profile is registered.",
|
|
490
|
+
),
|
|
491
|
+
);
|
|
492
|
+
} else if (!profile.kinds.includes(request.artifact.kind)) {
|
|
493
|
+
issues.push(
|
|
494
|
+
issue(
|
|
495
|
+
"delivery-artifact-kind-mismatch",
|
|
496
|
+
"/artifact/kind",
|
|
497
|
+
"Artifact kind is not admitted by the selected profile.",
|
|
498
|
+
),
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
let profileResult = null;
|
|
503
|
+
if (profile && profile.kinds.includes(request.artifact.kind)) {
|
|
504
|
+
try {
|
|
505
|
+
profileResult = normalizeProfileResult(
|
|
506
|
+
profile.verify(structuredClone(request.artifact)),
|
|
507
|
+
);
|
|
508
|
+
} catch {
|
|
509
|
+
profileResult = {
|
|
510
|
+
valid: false,
|
|
511
|
+
issues: [
|
|
512
|
+
issue(
|
|
513
|
+
"delivery-artifact-profile-error",
|
|
514
|
+
"/artifactProfile",
|
|
515
|
+
"Artifact profile execution failed closed.",
|
|
516
|
+
),
|
|
517
|
+
],
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
issues.push(...profileResult.issues);
|
|
521
|
+
if (!profileResult.valid && profileResult.issues.length === 0) {
|
|
522
|
+
issues.push(
|
|
523
|
+
issue(
|
|
524
|
+
"delivery-artifact-rejected",
|
|
525
|
+
"/artifact",
|
|
526
|
+
"Artifact profile rejected the artifact.",
|
|
527
|
+
),
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
let driverResult = null;
|
|
533
|
+
if (driver && profileResult?.valid === true) {
|
|
534
|
+
try {
|
|
535
|
+
driverResult = normalizeDriverResult(
|
|
536
|
+
driver.verify({
|
|
537
|
+
request: structuredClone(request),
|
|
538
|
+
context: structuredClone(context),
|
|
539
|
+
}),
|
|
540
|
+
);
|
|
541
|
+
} catch {
|
|
542
|
+
driverResult = {
|
|
543
|
+
valid: false,
|
|
544
|
+
report: null,
|
|
545
|
+
reportRoot: null,
|
|
546
|
+
issues: [
|
|
547
|
+
issue(
|
|
548
|
+
"delivery-driver-error",
|
|
549
|
+
"/protocol",
|
|
550
|
+
"Protocol driver execution failed closed.",
|
|
551
|
+
),
|
|
552
|
+
],
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
issues.push(...driverResult.issues);
|
|
556
|
+
if (!driverResult.valid && driverResult.issues.length === 0) {
|
|
557
|
+
issues.push(
|
|
558
|
+
issue(
|
|
559
|
+
"delivery-semantic-rejected",
|
|
560
|
+
"/declaration",
|
|
561
|
+
"Protocol driver rejected the declaration.",
|
|
562
|
+
),
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return gateResult(
|
|
567
|
+
request,
|
|
568
|
+
driver,
|
|
569
|
+
profile,
|
|
570
|
+
profileResult,
|
|
571
|
+
driverResult,
|
|
572
|
+
issues,
|
|
573
|
+
);
|
|
574
|
+
},
|
|
575
|
+
});
|
|
576
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export function isAdopterDeliveryJsonValue(value, ancestors = new Set()) {
|
|
4
|
+
try {
|
|
5
|
+
if (
|
|
6
|
+
value === null ||
|
|
7
|
+
typeof value === "string" ||
|
|
8
|
+
typeof value === "boolean"
|
|
9
|
+
) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
13
|
+
if (typeof value !== "object" || ancestors.has(value)) return false;
|
|
14
|
+
|
|
15
|
+
ancestors.add(value);
|
|
16
|
+
let valid = true;
|
|
17
|
+
if (Array.isArray(value)) {
|
|
18
|
+
valid =
|
|
19
|
+
Reflect.ownKeys(value).length === value.length + 1 &&
|
|
20
|
+
Array.from(
|
|
21
|
+
{ length: value.length },
|
|
22
|
+
(_, index) =>
|
|
23
|
+
Object.hasOwn(value, index) &&
|
|
24
|
+
isAdopterDeliveryJsonValue(value[index], ancestors),
|
|
25
|
+
).every(Boolean);
|
|
26
|
+
} else {
|
|
27
|
+
const prototype = Object.getPrototypeOf(value);
|
|
28
|
+
const keys = Reflect.ownKeys(value);
|
|
29
|
+
valid =
|
|
30
|
+
(prototype === Object.prototype || prototype === null) &&
|
|
31
|
+
keys.every((key) => {
|
|
32
|
+
if (typeof key !== "string") return false;
|
|
33
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
34
|
+
return (
|
|
35
|
+
descriptor?.enumerable === true &&
|
|
36
|
+
Object.hasOwn(descriptor, "value") &&
|
|
37
|
+
isAdopterDeliveryJsonValue(descriptor.value, ancestors)
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
ancestors.delete(value);
|
|
42
|
+
return valid;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function stableJson(value) {
|
|
49
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
50
|
+
if (value && typeof value === "object") {
|
|
51
|
+
return `{${Object.keys(value)
|
|
52
|
+
.sort()
|
|
53
|
+
.map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`)
|
|
54
|
+
.join(",")}}`;
|
|
55
|
+
}
|
|
56
|
+
return JSON.stringify(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function adopterDeliveryGateDigest(value) {
|
|
60
|
+
if (!isAdopterDeliveryJsonValue(value)) {
|
|
61
|
+
throw new TypeError(
|
|
62
|
+
"delivery gate digests require finite acyclic JSON values",
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return `sha256:${crypto.createHash("sha256").update(stableJson(value)).digest("hex")}`;
|
|
66
|
+
}
|