@kungfu-tech/buildchain 2.12.7-alpha.2 → 2.12.7-alpha.21
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/actions/promote-buildchain-ref/README.md +5 -1
- package/dist/site/buildchain-contract.json +53 -23
- package/dist/site/buildchain-site.json +16 -16
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/controller-registry.json +27 -3
- package/dist/site/kfd-claims.json +63 -9
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +2 -2
- package/dist/site/node-api-registry.json +6 -6
- package/dist/site/page-registry.json +10 -10
- package/dist/site/public-surface-audit.json +74 -10
- package/dist/site/publication-authority-registry.json +25 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/site-manifest.json +6 -6
- package/dist/site/workflow-registry.json +74 -7
- package/docs/publication-artifacts.md +30 -20
- package/docs/publication-authority.md +21 -9
- package/docs/release-candidate.md +19 -0
- package/docs/shifu-gate-profiles.md +6 -0
- package/package.json +1 -1
- package/packages/core/buildchain-publication-authority.js +1 -0
- package/packages/core/controller-evidence.js +1 -1
- package/packages/core/index.js +7 -0
- package/packages/core/publication-artifact-candidate.js +128 -0
- package/packages/core/publication-authority.js +41 -2
- package/packages/core/publication-control-plane-audit.js +3 -1
- package/scripts/assemble-publication-artifact-admission.mjs +190 -0
- package/scripts/assemble-self-publication-admission.mjs +19 -9
- package/scripts/audit-publication-control-plane.mjs +5 -2
- package/scripts/check-inventory.mjs +3 -0
- package/scripts/locked-source-checkout.mjs +22 -2
- package/scripts/publication-artifact-candidate.mjs +122 -0
- package/scripts/workflow-friction-report.mjs +13 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-publication-release-registry",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-15T12:26:57.218Z",
|
|
5
|
+
"publishedAt": "2026-07-15T12:26:57.218Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "97973413b7ba208df42a3ead28d2b80067a3ecc8",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"package": {
|
|
34
34
|
"name": "@kungfu-tech/buildchain",
|
|
35
|
-
"version": "2.12.7-alpha.
|
|
35
|
+
"version": "2.12.7-alpha.21",
|
|
36
36
|
"versionSource": "package.json#version"
|
|
37
37
|
},
|
|
38
38
|
"sourceKind": "package-site-bundle",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-manifest",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-15T12:26:57.218Z",
|
|
5
|
+
"publishedAt": "2026-07-15T12:26:57.218Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "97973413b7ba208df42a3ead28d2b80067a3ecc8",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "2.12.7-alpha.
|
|
40
|
+
"version": "2.12.7-alpha.21",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"path": "docs/publication-authority.md",
|
|
86
86
|
"plane": "verify",
|
|
87
87
|
"exists": true,
|
|
88
|
-
"digest": "sha256:
|
|
88
|
+
"digest": "sha256:05bc22c7e5885e591bc121fc43f588e5d49ccf0a8de6a642811c1d0d03dcee1e"
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"id": "release-candidate",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"path": "docs/release-candidate.md",
|
|
94
94
|
"plane": "verify",
|
|
95
95
|
"exists": true,
|
|
96
|
-
"digest": "sha256:
|
|
96
|
+
"digest": "sha256:6a20c6e3e483327d5ccbe70271f7fb4752731bd0523eeddb96f27b2b6ebf1cf8"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"id": "release-propagation",
|
|
@@ -215,14 +215,19 @@
|
|
|
215
215
|
"reusable": true,
|
|
216
216
|
"inputs": [
|
|
217
217
|
"admission-json",
|
|
218
|
+
"authority-workflow-path",
|
|
218
219
|
"auto-admission",
|
|
220
|
+
"auto-admission-kind",
|
|
221
|
+
"auto-no-gate",
|
|
219
222
|
"buildchain-ref",
|
|
220
223
|
"buildchain-repository",
|
|
221
224
|
"control-plane-audit-json",
|
|
222
225
|
"dry-run",
|
|
226
|
+
"evidence-controller-artifact",
|
|
223
227
|
"evidence-manifest-pattern",
|
|
224
228
|
"evidence-passport-artifact",
|
|
225
229
|
"evidence-payload-pattern",
|
|
230
|
+
"evidence-publication-artifact",
|
|
226
231
|
"evidence-repository",
|
|
227
232
|
"evidence-run-id",
|
|
228
233
|
"evidence-summary-artifact",
|
|
@@ -231,13 +236,15 @@
|
|
|
231
236
|
"package-name",
|
|
232
237
|
"product",
|
|
233
238
|
"publication-target",
|
|
239
|
+
"publication-version",
|
|
234
240
|
"publisher-workflow-path",
|
|
241
|
+
"required-status-check",
|
|
235
242
|
"runner-provenance-json",
|
|
236
243
|
"source-sha",
|
|
237
244
|
"target-ref",
|
|
238
245
|
"used-nonces-json"
|
|
239
246
|
],
|
|
240
|
-
"inputCount":
|
|
247
|
+
"inputCount": 29,
|
|
241
248
|
"secrets": [],
|
|
242
249
|
"secretCount": 0,
|
|
243
250
|
"outputs": [
|
|
@@ -1024,6 +1031,54 @@
|
|
|
1024
1031
|
"capabilityGroup": "api-cli-reference",
|
|
1025
1032
|
"status": "active"
|
|
1026
1033
|
},
|
|
1034
|
+
{
|
|
1035
|
+
"id": "paper-release-sealed",
|
|
1036
|
+
"path": ".github/workflows/paper-release-sealed.yml",
|
|
1037
|
+
"reusable": true,
|
|
1038
|
+
"inputs": [
|
|
1039
|
+
"artifact-name",
|
|
1040
|
+
"artifact-paths",
|
|
1041
|
+
"artifact-retention-days",
|
|
1042
|
+
"branch-protection-bypass-apps",
|
|
1043
|
+
"branch-protection-bypass-teams",
|
|
1044
|
+
"branch-protection-bypass-users",
|
|
1045
|
+
"buildchain-contract-lock-path",
|
|
1046
|
+
"buildchain-ref",
|
|
1047
|
+
"buildchain-repository",
|
|
1048
|
+
"dry-run",
|
|
1049
|
+
"github-release",
|
|
1050
|
+
"github-release-notes",
|
|
1051
|
+
"github-release-title",
|
|
1052
|
+
"package-name",
|
|
1053
|
+
"publish-dist-tag",
|
|
1054
|
+
"publisher-workflow-path",
|
|
1055
|
+
"release-passport-impact-json",
|
|
1056
|
+
"release-passport-output-dir",
|
|
1057
|
+
"release-passport-product-name",
|
|
1058
|
+
"required-status-check",
|
|
1059
|
+
"target-ref",
|
|
1060
|
+
"target-sha",
|
|
1061
|
+
"toolchain-type",
|
|
1062
|
+
"verify-command",
|
|
1063
|
+
"working-directory"
|
|
1064
|
+
],
|
|
1065
|
+
"inputCount": 25,
|
|
1066
|
+
"secrets": [
|
|
1067
|
+
"BUILDCHAIN_PROMOTION_TOKEN"
|
|
1068
|
+
],
|
|
1069
|
+
"secretCount": 1,
|
|
1070
|
+
"outputs": [
|
|
1071
|
+
"github-release-url",
|
|
1072
|
+
"package-name",
|
|
1073
|
+
"package-version",
|
|
1074
|
+
"public-release-tag",
|
|
1075
|
+
"release-passport-path"
|
|
1076
|
+
],
|
|
1077
|
+
"outputCount": 5,
|
|
1078
|
+
"surface": "repository-workflow",
|
|
1079
|
+
"capabilityGroup": "api-cli-reference",
|
|
1080
|
+
"status": "active"
|
|
1081
|
+
},
|
|
1027
1082
|
{
|
|
1028
1083
|
"id": "paper-release",
|
|
1029
1084
|
"path": ".github/workflows/paper-release.yml",
|
|
@@ -1193,7 +1248,11 @@
|
|
|
1193
1248
|
"buildchain-ref",
|
|
1194
1249
|
"buildchain-repository",
|
|
1195
1250
|
"node-version",
|
|
1251
|
+
"package-name",
|
|
1252
|
+
"prepare-paper-package",
|
|
1253
|
+
"publish-dist-tag",
|
|
1196
1254
|
"setup-node",
|
|
1255
|
+
"target-ref",
|
|
1197
1256
|
"toolchain-command",
|
|
1198
1257
|
"toolchain-digest",
|
|
1199
1258
|
"toolchain-image",
|
|
@@ -1201,7 +1260,7 @@
|
|
|
1201
1260
|
"verify-command",
|
|
1202
1261
|
"working-directory"
|
|
1203
1262
|
],
|
|
1204
|
-
"inputCount":
|
|
1263
|
+
"inputCount": 21,
|
|
1205
1264
|
"secrets": [],
|
|
1206
1265
|
"secretCount": 0,
|
|
1207
1266
|
"outputs": [
|
|
@@ -1212,13 +1271,17 @@
|
|
|
1212
1271
|
"controller-receipt-digest",
|
|
1213
1272
|
"controller-receipt-json",
|
|
1214
1273
|
"controller-receipt-status",
|
|
1274
|
+
"package-name",
|
|
1275
|
+
"package-version",
|
|
1215
1276
|
"publication-artifact-name",
|
|
1216
1277
|
"publication-manifest-json",
|
|
1217
1278
|
"publication-manifest-path",
|
|
1218
1279
|
"publication-passport-path",
|
|
1219
|
-
"publication-registry-path"
|
|
1280
|
+
"publication-registry-path",
|
|
1281
|
+
"publish-dist-tag",
|
|
1282
|
+
"runtime-sha"
|
|
1220
1283
|
],
|
|
1221
|
-
"outputCount":
|
|
1284
|
+
"outputCount": 16,
|
|
1222
1285
|
"surface": "repository-workflow",
|
|
1223
1286
|
"capabilityGroup": "reusable-build",
|
|
1224
1287
|
"status": "active"
|
|
@@ -1247,6 +1310,7 @@
|
|
|
1247
1310
|
"package-manager",
|
|
1248
1311
|
"publication-admission-json",
|
|
1249
1312
|
"publication-auto-admission",
|
|
1313
|
+
"publication-auto-no-gate",
|
|
1250
1314
|
"publication-control-plane-audit-json",
|
|
1251
1315
|
"publication-expected-json",
|
|
1252
1316
|
"publication-gate-aggregate-json",
|
|
@@ -1284,16 +1348,17 @@
|
|
|
1284
1348
|
"target-sha",
|
|
1285
1349
|
"trusted-publishing"
|
|
1286
1350
|
],
|
|
1287
|
-
"inputCount":
|
|
1351
|
+
"inputCount": 56,
|
|
1288
1352
|
"secrets": [
|
|
1289
1353
|
"BUILDCHAIN_ISSUE_APP_ID",
|
|
1290
1354
|
"BUILDCHAIN_ISSUE_APP_PRIVATE_KEY",
|
|
1291
1355
|
"BUILDCHAIN_ISSUE_TOKEN",
|
|
1356
|
+
"BUILDCHAIN_PROMOTION_TOKEN",
|
|
1292
1357
|
"buildchain-issue-app-id",
|
|
1293
1358
|
"buildchain-issue-app-private-key",
|
|
1294
1359
|
"buildchain-issue-token"
|
|
1295
1360
|
],
|
|
1296
|
-
"secretCount":
|
|
1361
|
+
"secretCount": 7,
|
|
1297
1362
|
"outputs": [
|
|
1298
1363
|
"built-source-sha",
|
|
1299
1364
|
"controller-plan-artifact",
|
|
@@ -1486,9 +1551,11 @@
|
|
|
1486
1551
|
"branch-protection-bypass-teams",
|
|
1487
1552
|
"branch-protection-bypass-users",
|
|
1488
1553
|
"dry-run",
|
|
1554
|
+
"expected-publication-version",
|
|
1489
1555
|
"generated-ref-update-token",
|
|
1490
1556
|
"generated-status-check-token",
|
|
1491
1557
|
"github-release",
|
|
1558
|
+
"github-release-artifact-paths",
|
|
1492
1559
|
"github-release-notes",
|
|
1493
1560
|
"github-release-title",
|
|
1494
1561
|
"promote-only-release-candidate",
|
|
@@ -1534,7 +1601,7 @@
|
|
|
1534
1601
|
"transaction-state-path",
|
|
1535
1602
|
"verification-command"
|
|
1536
1603
|
],
|
|
1537
|
-
"inputCount":
|
|
1604
|
+
"inputCount": 54,
|
|
1538
1605
|
"capabilityGroup": "release-passport-trust",
|
|
1539
1606
|
"status": "active"
|
|
1540
1607
|
},
|
|
@@ -182,40 +182,42 @@ on:
|
|
|
182
182
|
|
|
183
183
|
jobs:
|
|
184
184
|
paper-release:
|
|
185
|
-
uses: kungfu-systems/buildchain/.github/workflows/paper-release.yml@v2
|
|
185
|
+
uses: kungfu-systems/buildchain/.github/workflows/paper-release-sealed.yml@v2
|
|
186
186
|
permissions:
|
|
187
|
+
actions: read
|
|
187
188
|
checks: write
|
|
188
189
|
contents: write
|
|
189
190
|
id-token: write
|
|
190
191
|
issues: write
|
|
191
192
|
with:
|
|
192
193
|
buildchain-ref: ${{ inputs.buildchain-ref || '' }}
|
|
193
|
-
|
|
194
|
-
publication-runner-provenance-json: ${{ needs.authority.outputs.runner-provenance-json }}
|
|
195
|
-
publication-control-plane-audit-json: ${{ needs.authority.outputs.control-plane-audit-json }}
|
|
196
|
-
publication-expected-json: ${{ needs.authority.outputs.expected-json }}
|
|
194
|
+
publisher-workflow-path: .github/workflows/paper-release.yml
|
|
197
195
|
toolchain-type: config
|
|
198
196
|
verify-command: make check
|
|
197
|
+
artifact-paths: _build/paper-name.pdf
|
|
199
198
|
buildchain-contract-lock-path: .buildchain/contract-lock.json
|
|
199
|
+
secrets:
|
|
200
|
+
BUILDCHAIN_PROMOTION_TOKEN: ${{ secrets.BUILDCHAIN_PROMOTION_TOKEN }}
|
|
200
201
|
```
|
|
201
202
|
|
|
202
|
-
The
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
203
|
+
The sealed preset does not use a long-lived token for npm publication. It may
|
|
204
|
+
accept an optional `BUILDCHAIN_PROMOTION_TOKEN` only for machine-generated
|
|
205
|
+
version-state updates on protected channel branches; npm publication remains
|
|
206
|
+
bound to GitHub OIDC trusted publishing. The preset builds and packages the
|
|
207
|
+
paper in a read-only job, then a credential-free authority job downloads that
|
|
208
|
+
exact candidate, audits the external control plane, and seals a capability over
|
|
209
|
+
the source tree, Buildchain runtime, controller receipt, PDF, and npm package
|
|
210
|
+
bytes. Only the final job receives write and OIDC permissions; it downloads the
|
|
211
|
+
admitted candidate, recomputes the capability binding, and publishes without
|
|
212
|
+
executing consumer build commands. npm binds the OIDC identity to the consumer
|
|
213
|
+
workflow named by `publisher-workflow-path`.
|
|
210
214
|
|
|
211
215
|
The preset:
|
|
212
216
|
|
|
213
|
-
- resolves the
|
|
214
|
-
|
|
215
|
-
- verifies that the declared promotion authority can read the protected target
|
|
216
|
-
channel before starting the publication build;
|
|
217
|
+
- resolves the floating Buildchain runtime once and binds the exact SHA into the
|
|
218
|
+
publication candidate and authority capability;
|
|
217
219
|
- builds the PDF through the declared pinned LaTeX Docker toolchain or custom
|
|
218
|
-
command;
|
|
220
|
+
command in a read-only job;
|
|
219
221
|
- verifies the paper repository;
|
|
220
222
|
- writes the publication manifest, publication passport, optional archive
|
|
221
223
|
registry, and source bundle;
|
|
@@ -226,14 +228,22 @@ The preset:
|
|
|
226
228
|
- creates a `publish-gate/<alpha|release>/.../<version>` source lock for the
|
|
227
229
|
channel commit and requires `promote-buildchain-ref` to verify that lock
|
|
228
230
|
before any publish side effect;
|
|
229
|
-
-
|
|
231
|
+
- verifies the complete candidate again after authority and publishes the
|
|
232
|
+
package through npm Trusted Publishing without rebuilding it;
|
|
230
233
|
- writes Buildchain release/passport evidence; and
|
|
231
|
-
- creates or updates the exact-version GitHub Release by default
|
|
234
|
+
- creates or updates the exact-version GitHub Release by default, uploading
|
|
235
|
+
every file declared by `publication.primary_artifact` and
|
|
236
|
+
`publication.artifact_paths` alongside the release evidence.
|
|
232
237
|
|
|
233
238
|
Consumers can opt out of the GitHub Release with `github-release: false`, but
|
|
234
239
|
the default is on so downstream release propagation can observe
|
|
235
240
|
`release.published` without hand-written `gh release` steps.
|
|
236
241
|
|
|
242
|
+
Declared publication artifacts are resolved from the generated publication
|
|
243
|
+
manifest rather than repeated in consumer workflow YAML. Publication fails
|
|
244
|
+
before upload if a declared artifact is missing or if its basename would
|
|
245
|
+
collide with another GitHub Release asset.
|
|
246
|
+
|
|
237
247
|
For npm Trusted Publishing, register the consumer workflow file that calls this
|
|
238
248
|
preset, for example `.github/workflows/paper-release.yml`, against the declared
|
|
239
249
|
package in npm. The trusted publisher is the consumer repository and workflow
|
|
@@ -8,11 +8,11 @@ confidence: high
|
|
|
8
8
|
sensitivity: public
|
|
9
9
|
evidence_grade: B
|
|
10
10
|
review_state: unreviewed
|
|
11
|
-
last_reviewed: 2026-07-
|
|
11
|
+
last_reviewed: 2026-07-15
|
|
12
12
|
ai_provenance:
|
|
13
13
|
model_family: GPT-5
|
|
14
14
|
product: Codex
|
|
15
|
-
generated_at: 2026-07-
|
|
15
|
+
generated_at: 2026-07-15
|
|
16
16
|
limits: Live provider configuration must be re-audited; no credential values are represented.
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -109,17 +109,29 @@ artifacts. The denial explicitly records that npm Trusted Publishing and OIDC
|
|
|
109
109
|
were not evaluated, so downstream diagnostics cannot misclassify an admission
|
|
110
110
|
assembly failure as an npm authentication failure.
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
An explicitly opted-in managed `workflow_run` promotion lane may assemble those
|
|
113
|
+
inputs from evidence owned by its caller repository. It downloads the exact prior RC passport,
|
|
114
114
|
summary, referenced controller receipt, manifests, and product payloads; proves
|
|
115
115
|
the admitted channel commit has the same Git tree as the RC; performs the live
|
|
116
116
|
read-only control-plane audit; records the GitHub-hosted job as ephemeral runner
|
|
117
|
-
provenance; and
|
|
117
|
+
provenance; and consumes either a caller-supplied Gate aggregate or an explicit
|
|
118
|
+
caller no-Gate decision. The
|
|
118
119
|
independent verifier then recomputes every receipt and payload digest exactly as
|
|
119
|
-
it does for externally supplied admission.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
it does for externally supplied admission. Automatic assembly keeps Buildchain
|
|
121
|
+
as the canonical authority repository, requires evidence to belong to the
|
|
122
|
+
caller, binds a repository-local publisher workflow, and rejects unknown refs,
|
|
123
|
+
non-exact source SHAs, package/target mismatches, or an undeclared Gate policy.
|
|
124
|
+
Manual apply and consumers that do not opt in still require their own explicit
|
|
125
|
+
admission inputs.
|
|
126
|
+
|
|
127
|
+
Before authority verification, the reusable promotion controller runs the same
|
|
128
|
+
release transaction selector in read-only mode. That plan supplies one exact
|
|
129
|
+
publication version and tag to the admission verifier, the publish-gate source
|
|
130
|
+
lock, and the real promotion action. The verifier rejects a capability for a
|
|
131
|
+
different version, and the promotion action rechecks the planned version before
|
|
132
|
+
any publish transaction side effect. Release-candidate fixture versions are
|
|
133
|
+
artifact evidence only; they never name Buildchain's own source-lock or
|
|
134
|
+
publication capability.
|
|
123
135
|
|
|
124
136
|
Evidence publication is a separate authority class and never grants product
|
|
125
137
|
publication.
|
|
@@ -116,3 +116,22 @@ KFD-1 source/artifact contract surfaces, audits KFD-2 public release claims, and
|
|
|
116
116
|
compares KFD-3 declared shipped public surfaces with artifact-exposed public
|
|
117
117
|
surfaces. The release passport records the results under `kfd-1`, `kfd-2`, and
|
|
118
118
|
the KFD-provided `kfd-3` section.
|
|
119
|
+
|
|
120
|
+
Managed consumers may also ask the promotion wrapper to assemble sealed
|
|
121
|
+
publication evidence from the exact release candidate instead of producing
|
|
122
|
+
short-lived admission JSON in repository-specific workflow code:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
publication-auto-admission: true
|
|
126
|
+
publication-auto-no-gate: true
|
|
127
|
+
publication-publisher-workflow-path: .github/workflows/buildchain-ref-promotion.yml
|
|
128
|
+
publication-product: Example Product
|
|
129
|
+
publication-target: npm:@example/product
|
|
130
|
+
publication-package-name: "@example/product"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`publication-auto-no-gate` is an explicit consumer decision, not a default. A
|
|
134
|
+
consumer with a Shifu Gate registry supplies `publication-gate-aggregate-json`
|
|
135
|
+
instead. Buildchain still requires caller-owned RC evidence, an exact authority
|
|
136
|
+
runtime and source SHA, a repository-local publisher workflow, matching npm
|
|
137
|
+
target/package identity, and a qualifying control-plane audit.
|
|
@@ -78,6 +78,12 @@ Every matrix job checks out the exact source SHA planned by Buildchain, invokes
|
|
|
78
78
|
then invokes `shifu gate receipt validate`. Buildchain uploads the original
|
|
79
79
|
receipt and validation result even when the run fails.
|
|
80
80
|
|
|
81
|
+
Before invoking the project-owned command, the workflow adds the runner
|
|
82
|
+
account's `~/.local/bin` directory to `PATH` on Windows, Linux, and macOS. This
|
|
83
|
+
keeps user-scoped tools such as `uv` available to strict Shifu cache profiles
|
|
84
|
+
without assuming an administrator-managed system installation. The consumer or
|
|
85
|
+
runner owner remains responsible for provisioning the declared tools.
|
|
86
|
+
|
|
81
87
|
The fixed `Gate profile / aggregate` job fails closed for missing receipts,
|
|
82
88
|
invalid or stale Shifu validation, dirty or mismatched source SHA, registry or
|
|
83
89
|
plan drift, missing required results, required failures/skips, or gate action
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.12.7-alpha.
|
|
3
|
+
"version": "2.12.7-alpha.21",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -27,6 +27,7 @@ const DESCRIPTORS = Object.freeze([
|
|
|
27
27
|
[".github/workflows/buildchain-stable-candidate-patrol.yml", "governance-write"],
|
|
28
28
|
[".github/workflows/dev-pr-auto-merge.yml", "governance-write"],
|
|
29
29
|
[".github/workflows/npm-publish.yml", "dry-run-only"],
|
|
30
|
+
[".github/workflows/paper-release-sealed.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
|
|
30
31
|
[".github/workflows/paper-release.yml", "product-publication", true, ["npm-publish", "github-release"], "trusted-publishing", "none", "fixed", "caller-bound"],
|
|
31
32
|
[".github/workflows/patrol-daily.yml", "governance-write"],
|
|
32
33
|
[".github/workflows/patrol-monthly.yml", "governance-write"],
|
|
@@ -303,7 +303,7 @@ function normalizeEvidence(entries = []) {
|
|
|
303
303
|
function receiptStatus(plan, stages) {
|
|
304
304
|
if (stages.some((stage) => stage.status === "failed")) return "failed";
|
|
305
305
|
if (stages.length > 0 && stages.every((stage) => stage.status === "skipped")) return "skipped";
|
|
306
|
-
if (stages.some((stage) => stage.status === "cancelled" || stage.status === "missing")) return "partial";
|
|
306
|
+
if (stages.some((stage) => stage.status === "cancelled" || (stage.required && stage.status === "missing"))) return "partial";
|
|
307
307
|
const required = new Set(plan.expected.stages.filter((stage) => stage.required).map((stage) => stage.id));
|
|
308
308
|
if (stages.filter((stage) => required.has(stage.id)).every((stage) => stage.status === "passed")) return "passed";
|
|
309
309
|
return "partial";
|
package/packages/core/index.js
CHANGED
|
@@ -153,6 +153,13 @@ export {
|
|
|
153
153
|
verifyPublicationAdmission,
|
|
154
154
|
} from "./publication-authority.js";
|
|
155
155
|
|
|
156
|
+
export {
|
|
157
|
+
PUBLICATION_ARTIFACT_CANDIDATE_CONTRACT,
|
|
158
|
+
createPublicationArtifactCandidate,
|
|
159
|
+
publicationArtifactCandidateDigest,
|
|
160
|
+
resolvePublicationCandidateFile,
|
|
161
|
+
} from "./publication-artifact-candidate.js";
|
|
162
|
+
|
|
156
163
|
export {
|
|
157
164
|
buildchainPublicationAuthorityDescriptors,
|
|
158
165
|
createBuildchainPublicationAuthorityRegistry,
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import { validateControllerReceipt } from "./controller-evidence.js";
|
|
4
|
+
|
|
5
|
+
export const PUBLICATION_ARTIFACT_CANDIDATE_CONTRACT =
|
|
6
|
+
"kungfu-buildchain-publication-artifact-candidate";
|
|
7
|
+
|
|
8
|
+
const MANIFEST_CONTRACT = "kungfu-buildchain-publication-artifact-manifest";
|
|
9
|
+
const PASSPORT_CONTRACT = "kungfu-buildchain-publication-artifact-passport";
|
|
10
|
+
|
|
11
|
+
function stableJson(value) {
|
|
12
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
13
|
+
if (value && typeof value === "object") {
|
|
14
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
15
|
+
}
|
|
16
|
+
return JSON.stringify(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function publicationArtifactCandidateDigest(value) {
|
|
20
|
+
return crypto.createHash("sha256").update(stableJson(value)).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolvePublicationCandidateFile(files = [], candidatePath) {
|
|
24
|
+
const normalizedPath = requiredString(candidatePath, "candidatePath").replaceAll("\\", "/");
|
|
25
|
+
if (normalizedPath.startsWith("/") || normalizedPath.split("/").includes("..")) {
|
|
26
|
+
throw new Error(`publication artifact candidate contains an unsafe path: ${normalizedPath}`);
|
|
27
|
+
}
|
|
28
|
+
const matches = files.filter((entry) => entry.path === normalizedPath);
|
|
29
|
+
if (matches.length !== 1) {
|
|
30
|
+
throw new Error(`expected exactly one publication candidate file at ${normalizedPath}, found ${matches.length}`);
|
|
31
|
+
}
|
|
32
|
+
return matches[0].path;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function requiredString(value, label) {
|
|
36
|
+
const normalized = String(value || "").trim();
|
|
37
|
+
if (!normalized) throw new Error(`${label} must be a non-empty string`);
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeDigest(value, label) {
|
|
42
|
+
const normalized = requiredString(value, label).replace(/^sha256:/, "").toLowerCase();
|
|
43
|
+
if (!/^[0-9a-f]{64}$/.test(normalized)) throw new Error(`${label} must be a sha256 digest`);
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeGitSha(value, label) {
|
|
48
|
+
const normalized = requiredString(value, label).toLowerCase();
|
|
49
|
+
if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(normalized)) {
|
|
50
|
+
throw new Error(`${label} must be a 40- or 64-character Git SHA`);
|
|
51
|
+
}
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createPublicationArtifactCandidate({
|
|
56
|
+
repository,
|
|
57
|
+
sourceSha,
|
|
58
|
+
sourceTreeSha,
|
|
59
|
+
runtimeSha,
|
|
60
|
+
manifest,
|
|
61
|
+
passport,
|
|
62
|
+
controllerReceipt,
|
|
63
|
+
files = [],
|
|
64
|
+
} = {}) {
|
|
65
|
+
const normalizedRepository = requiredString(repository, "repository");
|
|
66
|
+
const normalizedSourceSha = normalizeGitSha(sourceSha, "sourceSha");
|
|
67
|
+
const normalizedSourceTreeSha = normalizeGitSha(sourceTreeSha, "sourceTreeSha");
|
|
68
|
+
const normalizedRuntimeSha = normalizeGitSha(runtimeSha, "runtimeSha");
|
|
69
|
+
if (manifest?.contract !== MANIFEST_CONTRACT) throw new Error("publication artifact manifest contract mismatch");
|
|
70
|
+
if (passport?.contract !== PASSPORT_CONTRACT || passport.status !== "passed") {
|
|
71
|
+
throw new Error("publication artifact passport is not qualifying");
|
|
72
|
+
}
|
|
73
|
+
if (manifest.source?.sha !== normalizedSourceSha || passport.source?.sha !== normalizedSourceSha) {
|
|
74
|
+
throw new Error("publication artifact source SHA mismatch");
|
|
75
|
+
}
|
|
76
|
+
if (manifest.source?.treeSha !== normalizedSourceTreeSha || passport.source?.treeSha !== normalizedSourceTreeSha) {
|
|
77
|
+
throw new Error("publication artifact source tree mismatch");
|
|
78
|
+
}
|
|
79
|
+
const manifestDigest = crypto.createHash("sha256").update(JSON.stringify(manifest, null, 2)).digest("hex");
|
|
80
|
+
if (normalizeDigest(passport.manifestDigest, "passport.manifestDigest") !== manifestDigest) {
|
|
81
|
+
throw new Error("publication artifact passport manifest digest mismatch");
|
|
82
|
+
}
|
|
83
|
+
const controllerValidation = validateControllerReceipt(controllerReceipt, {
|
|
84
|
+
expectedSourceSha: normalizedSourceSha,
|
|
85
|
+
expectedRuntimeSha: normalizedRuntimeSha,
|
|
86
|
+
});
|
|
87
|
+
if (!controllerValidation.ok || !controllerValidation.qualifying) {
|
|
88
|
+
throw new Error(`publication artifact controller receipt did not qualify: ${controllerValidation.issues.join("; ")}`);
|
|
89
|
+
}
|
|
90
|
+
const evidenceKinds = new Set((controllerReceipt.evidence || []).map((entry) => entry.kind));
|
|
91
|
+
for (const kind of ["publication-manifest", "publication-passport"]) {
|
|
92
|
+
if (!evidenceKinds.has(kind)) throw new Error(`publication artifact controller receipt is missing ${kind} evidence`);
|
|
93
|
+
}
|
|
94
|
+
const normalizedFiles = files.map((entry, index) => {
|
|
95
|
+
const filePath = requiredString(entry.path, `files[${index}].path`).replaceAll("\\", "/");
|
|
96
|
+
if (filePath.startsWith("/") || filePath.split("/").includes("..")) {
|
|
97
|
+
throw new Error(`publication artifact candidate contains an unsafe path: ${filePath}`);
|
|
98
|
+
}
|
|
99
|
+
const size = Number(entry.size ?? entry.bytes);
|
|
100
|
+
if (!Number.isSafeInteger(size) || size < 0) throw new Error(`files[${index}].size must be a non-negative safe integer`);
|
|
101
|
+
return { path: filePath, size, sha256: normalizeDigest(entry.sha256, `files[${index}].sha256`) };
|
|
102
|
+
}).sort((left, right) => left.path.localeCompare(right.path));
|
|
103
|
+
if (new Set(normalizedFiles.map((entry) => entry.path)).size !== normalizedFiles.length) {
|
|
104
|
+
throw new Error("publication artifact candidate file paths must be unique");
|
|
105
|
+
}
|
|
106
|
+
const byPath = new Map(normalizedFiles.map((entry) => [entry.path, entry]));
|
|
107
|
+
for (const [index, artifact] of (manifest.artifacts || []).entries()) {
|
|
108
|
+
const artifactPath = requiredString(artifact.path, `manifest.artifacts[${index}].path`);
|
|
109
|
+
const actual = byPath.get(artifactPath);
|
|
110
|
+
if (!actual) throw new Error(`publication artifact candidate is missing declared artifact: ${artifactPath}`);
|
|
111
|
+
if (actual.size !== Number(artifact.bytes) || actual.sha256 !== normalizeDigest(artifact.sha256, `manifest.artifacts[${index}].sha256`)) {
|
|
112
|
+
throw new Error(`publication artifact candidate bytes do not match manifest: ${artifactPath}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const payload = {
|
|
116
|
+
schemaVersion: 1,
|
|
117
|
+
contract: PUBLICATION_ARTIFACT_CANDIDATE_CONTRACT,
|
|
118
|
+
repository: normalizedRepository,
|
|
119
|
+
sourceSha: normalizedSourceSha,
|
|
120
|
+
sourceTreeSha: normalizedSourceTreeSha,
|
|
121
|
+
runtimeSha: normalizedRuntimeSha,
|
|
122
|
+
manifestDigest,
|
|
123
|
+
passportDigest: publicationArtifactCandidateDigest(passport),
|
|
124
|
+
controllerReceiptDigest: normalizeDigest(controllerReceipt.digest, "controllerReceipt.digest"),
|
|
125
|
+
files: normalizedFiles,
|
|
126
|
+
};
|
|
127
|
+
return { ...payload, candidateDigest: publicationArtifactCandidateDigest(payload) };
|
|
128
|
+
}
|