@kungfu-tech/buildchain 4.0.8 → 4.0.9-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.
Files changed (51) hide show
  1. package/AGENTS.md +12 -19
  2. package/CONTRIBUTING.md +6 -9
  3. package/README.md +11 -1
  4. package/architecture/agent-change-map.md +11 -4
  5. package/architecture/build-environments.json +71 -0
  6. package/architecture/build-orchestration.json +18 -0
  7. package/architecture/ci-lane-change-budget.json +34 -2
  8. package/architecture/internal-capabilities.json +17 -4
  9. package/architecture/maintainability-debt.json +34 -123
  10. package/architecture/maintainability-policy.json +19 -210
  11. package/architecture/release-topology.json +6 -5
  12. package/architecture/universal-workflow-bootstrap.json +5 -4
  13. package/architecture/universal-workflow-train-admission.json +2 -2
  14. package/bin/buildchain.mjs +1 -1
  15. package/contracts/fixtures/v4-tail-reseal-v1/valid.json +1 -1
  16. package/dist/site/buildchain-contract.json +41 -1131
  17. package/dist/site/buildchain-site.json +39 -144
  18. package/dist/site/capability-registry.json +1 -1
  19. package/dist/site/cli-registry.json +1 -1
  20. package/dist/site/controller-registry.json +30 -868
  21. package/dist/site/kfd-claims.json +132 -394
  22. package/dist/site/kfd-upstream-aggregate.json +1 -1
  23. package/dist/site/manual-registry.json +6 -6
  24. package/dist/site/node-api-registry.json +34 -34
  25. package/dist/site/page-registry.json +30 -135
  26. package/dist/site/public-surface-audit.json +81 -401
  27. package/dist/site/publication-registry.json +2 -2
  28. package/dist/site/site-manifest.json +8 -8
  29. package/dist/site/workflow-registry.json +135 -391
  30. package/docs/MAP.md +5 -4
  31. package/docs/cli-reference.md +1 -1
  32. package/docs/getting-started.md +8 -7
  33. package/docs/node-api-reference.md +79 -79
  34. package/docs/reusable-build-surface.md +159 -1430
  35. package/docs/runtime-train-validation.md +45 -172
  36. package/package.json +1 -1
  37. package/packages/core/build-configuration.js +117 -0
  38. package/packages/core/buildchain-config.js +2 -0
  39. package/packages/core/buildchain-contract.js +14 -44
  40. package/packages/core/controller-evidence.js +2 -9
  41. package/scripts/buildchain-cli-help.mjs +1 -1
  42. package/scripts/check-floating-consumer-policy-contract.mjs +4 -7
  43. package/scripts/check-inventory.mjs +81 -54
  44. package/scripts/check-universal-workflow-bootstrap.mjs +12 -2
  45. package/scripts/generate-channel-build-workflow.mjs +50 -410
  46. package/scripts/generate-site-bundle.mjs +2 -1
  47. package/scripts/generate-universal-workflow-facades.mjs +1 -0
  48. package/scripts/init-repo.mjs +25 -26
  49. package/scripts/resolve-build-configuration.mjs +105 -0
  50. package/scripts/universal-workflow-self-dogfood.mjs +8 -1
  51. package/scripts/verify-golden-path.mjs +3 -3
@@ -6,1463 +6,192 @@ doc_type: technical-reference
6
6
  source_level: local-files
7
7
  confidence: high
8
8
  sensitivity: public
9
- evidence_grade: A
9
+ evidence_grade: B
10
10
  review_state: unreviewed
11
- last_reviewed: 2026-08-03
11
+ last_reviewed: 2026-09-08
12
12
  ai_provenance:
13
- model_family: GPT-5
13
+ model_family: GPT-6
14
14
  product: Codex
15
- generated_at: 2026-08-03
16
- invisible_context: not asserted
15
+ generated_at: 2026-09-08
16
+ invisible_context_boundary: No private credentials or unrelated repositories inspected.
17
17
  ---
18
18
 
19
19
  # Reusable Build Surface
20
20
 
21
- Buildchain v3 provides a reusable build workflow for repositories that need
22
- Buildchain's release semantics but cannot be described as a simple Node package.
23
- The first target shape is `libnode`: expensive native builds, multiple operating
24
- systems, self-hosted runner labels, and release artifacts that must be auditable.
25
-
26
- For Linux release artifacts, the build workflow can hand the sealed artifact,
27
- platform manifest, and Release Passport to the separate GitHub-hosted keyless
28
- attester. The compiler runner remains the recorded build identity; the attester
29
- only signs and verifies immutable data. See
30
- [`github-artifact-attestation.md`](github-artifact-attestation.md).
31
-
32
- ## Automatic Channel Router
33
-
34
- The preferred consumer surface is one reusable workflow call. Consumers keep
35
- this configuration for both alpha development and stable release work:
36
-
37
- ```yaml
38
- jobs:
39
- build:
40
- uses: kungfu-systems/buildchain/.github/workflows/build.yml@v3
41
- permissions:
42
- contents: read
43
- issues: write
44
- id-token: write
45
- with:
46
- working-directory: .
47
- artifact-name: libnode
48
- runner-preset: kungfu-v4-self-hosted
49
- publish-channel: none
50
- secrets: inherit
51
- ```
52
-
53
- `buildchain-channel` defaults to `auto`. Selection uses this precedence:
54
-
55
- 1. an explicit `buildchain-ref` protected authority, train, SHA, or official channel;
56
- 2. an explicit `buildchain-channel: alpha|stable`;
57
- 3. `publish-channel: alpha|release|major`;
58
- 4. GitHub release prerelease metadata;
59
- 5. a canonical semver tag;
60
- 6. non-release PR, push, dispatch, schedule, and workflow-run events default to
61
- alpha.
62
-
63
- The resolved runtime is `vN-alpha` for development and prerelease intent and
64
- `vN` for stable release intent. Unknown custom publish channels, malformed
65
- release events, and non-semver release-like tags fail before the build matrix;
66
- they never guess alpha for a stable release.
67
-
68
- The router automatically selects `.buildchain/alpha-contract-lock.json` for
69
- alpha and `.buildchain/contract-lock.json` for stable. The generated workflow
70
- uses two static advanced-workflow calls: `@vN-alpha` for alpha and `@vN` for
71
- stable. A repository can override the common path with
72
- `buildchain-contract-lock-path`, or override one channel with
73
- `buildchain-alpha-contract-lock-path` / `buildchain-stable-contract-lock-path`,
74
- but the path is never channel authority. The selected file's
75
- `buildchain.ref` must prove the same channel and major as the workflow shell and
76
- runtime.
77
-
78
- Channel binding is independent of the current Buildchain major. Every
79
- channel-bound run validates the complete triad before compatibility drift:
80
-
81
- - stable = shell `vN` + runtime `vN` + a lock whose ref is stable `vN`;
82
- - alpha = shell `vN-alpha` + runtime `vN-alpha` + a lock whose ref is alpha
83
- `vN-alpha`.
84
-
85
- Exact release refs are classified the same way. A missing lock, an ambiguous
86
- identity, a major mismatch, or any stable/alpha mixture fails even when the
87
- contract digest is unchanged or the drift would otherwise be additive.
88
-
89
- Only repositories changing the default policy need extra routing input:
90
-
91
- ```yaml
92
- with:
93
- buildchain-channel: stable
94
- ```
95
-
96
- During v3 prerelease evaluation windows, canaries use `build.yml@v3-alpha`.
97
- The same router then selects `v3-alpha` or stable `v3` as the runtime.
98
- Production consumers use `build.yml@v3`; this keeps the routing shell itself on
99
- a stable ref.
100
-
101
- The router is generated from `.build.yml`'s input/output surface. Run
102
- `node scripts/generate-channel-build-workflow.mjs` after changing the advanced
103
- build workflow; inventory and unit tests reject a stale generated router.
104
-
105
- ### v4 floating consumer admission
106
-
107
- The v4 public surfaces add a source policy gate before dependency setup, matrix
108
- planning, build, publication, or promotion work. Tracked caller YAML may use
109
- only `@v4` or `@v4-alpha`, and the repository must commit both
110
- `.buildchain/contract-lock.json` and `.buildchain/alpha-contract-lock.json`.
111
- Source-persisted exact SHAs, exact defaults, repository/input/environment
112
- indirection, nested composite indirection, a missing lock, or a stale selected
113
- lock fail closed.
114
-
115
- Admission emits a rooted receipt binding the caller source, public workflow,
116
- visible selector and workflow-shell SHA, actual runtime SHA, both lock roots,
117
- and policy/scanner roots. A trusted `workflow_dispatch` train or SHA remains a
118
- non-persistent runtime override: the tracked floating selector and its selected
119
- lock still bind the visible workflow shell. Release-candidate evidence carries
120
- the receipt; promotion certifies it independently; final v4 Release Passport
121
- construction requires the matching certification.
122
-
123
- Transient train, authority, and exact-SHA selections additionally produce a
124
- provider-readback-bound runtime authorization receipt. A resumed failed tail
125
- uses a fresh attempt and records the original build runtime, the resume runtime,
126
- the exact sealed Stage Capsules reused per platform, and the platforms rebuilt
127
- in the final Release Passport. See
128
- [`v4-runtime-ref-resume-authority.md`](v4-runtime-ref-resume-authority.md).
129
-
130
- See the normative
131
- [`v4 floating consumer policy`](../architecture/floating-consumer-policy.md)
132
- and its machine declaration for the exact acceptance boundary. This policy does
133
- not change v3 behavior.
134
-
135
- ### Universal Bootstrap verified-caller admission
136
-
137
- The v4 Universal Bootstrap does not maintain a repository allowlist. Any
138
- repository that can call the public reusable workflow may submit the single
139
- versioned request envelope. Admission still fails closed unless the request's
140
- consumer repository, caller workflow path, and source SHA exactly match the
141
- GitHub-observed caller context.
142
-
143
- Open consumer admission does not mean open execution authority. The candidate
144
- must still be an exact Buildchain SHA selected through a governed discovery
145
- ref, bind the current candidate-owned admission root, have exact-head
146
- independent review and required checks, request an admitted capability and
147
- contract-root set, stay within the permission ceiling and validity window, and
148
- inherit only authority available from the caller. The candidate engine exposes
149
- bounded capability adapters; it does not accept caller-provided shell or code.
150
-
151
- ## Advanced Workflow
152
-
153
- Consumers that need direct workflow-shell or runtime control call the advanced
154
- surface:
21
+ A project declares its build in `buildchain.toml`. The reusable workflow owns
22
+ job sequencing, admission, runner boundaries and evidence aggregation. Ordinary
23
+ single-project calls have no inputs:
155
24
 
156
25
  ```yaml
26
+ permissions:
27
+ actions: read
28
+ contents: read
29
+ issues: write
30
+ id-token: write
157
31
  jobs:
158
32
  build:
159
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
160
- with:
161
- working-directory: .
162
- artifact-name: libnode
163
- runner-preset: kungfu-v4-self-hosted
164
- linux-container-preset: kungfu-verify
165
- artifact-name-template: "{artifact}-{platform}-{sha}"
166
- artifact-paths: |
167
- dist
168
- build/stage
169
- pre-upload-transport-smoke-scenario-path: .buildchain/auditable-demo.json
170
- pre-upload-transport-smoke-artifact-root: .
171
- expected-artifacts-json: >-
172
- {"minFiles":2,"requiredPaths":["dist/libnode.tar.gz","dist/checksums.txt"]}
173
- process-summary-path: .buildchain/diagnostics/process-summary.json
174
- release-candidate: true
175
- publish-channel: release
176
- publish-source-ref: publish-gate/release/v22/v22.22/22.22.3-kf.0
177
- ```
178
-
179
- For a standalone Linux binary demo, the optional pre-upload transport smoke
180
- uses the same declarative scenario as the later capture workflow. Buildchain
181
- copies the distribution containing the scenario metadata, strips file execute
182
- bits to model GitHub Artifact transport, restores only the declared
183
- digest-bound executable closure, and runs `transportSmoke` before either the
184
- GitHub Artifact or S3 relay upload step. The smoke must be non-interactive and
185
- is hard-capped at 60 seconds. Omitting the input preserves the ordinary build
186
- surface; enabling it requires a scenario with `transportSmoke`.
187
-
188
- `runner-preset` is the stable first-class surface for known runner fleets:
189
-
190
- | Preset | Platforms |
191
- | ----------------------- | -------------------------------------------------------------------------------------------------------------- |
192
- | `github-hosted` | `ubuntu-24.04`, `macos-latest`, `windows-2022` |
193
- | `kungfu-v4-self-hosted` | Kungfu Linux x64, macOS ARM64, and Windows x64 self-hosted runner labels |
194
- | `kungfu-v4-native` | Kungfu Linux x64, Linux ARM64, macOS ARM64, and Windows x64; Linux ARM64 uses GitHub-hosted `ubuntu-24.04-arm` |
195
- | `custom` | Requires `platforms-json` |
196
-
197
- Set `self-hosted-offline-fallback: true` to inspect every exact-label
198
- self-hosted lane from the trusted Buildchain workflow shell before the matrix
199
- starts. A lane with no matching online runner is replaced independently by its
200
- supported GitHub-hosted runner: Kungfu Linux x64 uses `ubuntu-24.04`, macOS ARM64
201
- uses `macos-15`, and Windows x64 uses `windows-2022`. Online-but-busy runners
202
- remain online and keep their declared self-hosted route. Organization-owned
203
- repositories inspect organization runner inventory so selected-repository runner
204
- groups are not mistaken for an empty repository runner inventory. If the
205
- inventory token, permission, or API is unavailable, Buildchain preserves the
206
- original matrix instead of guessing that the fleet is offline. The public
207
- workflow output `runner-routing-json` records only de-identified counts,
208
- inventory scope, and routing decisions.
209
-
210
- ```yaml
211
- with:
212
- runner-preset: kungfu-v4-native
213
- self-hosted-offline-fallback: true
214
- secrets:
215
- BUILDCHAIN_PROMOTION_TOKEN: ${{ secrets.KUNGFU_GITHUB_TOKEN }}
33
+ uses: kungfu-systems/buildchain/.github/workflows/build.yml@v4
216
34
  ```
217
35
 
218
- Callers can still provide a custom matrix with `platforms-json`. Each platform
219
- object has:
220
-
221
- | Field | Meaning |
222
- | -------- | ------------------------------------------------- |
223
- | `id` | Stable artifact/platform key, such as `linux-x64` |
224
- | `name` | Human-readable job name |
225
- | `runner` | JSON string passed to `runs-on` after `fromJSON` |
226
-
227
- The runner field is intentionally a JSON string so callers can pass either
228
- GitHub-hosted runners or multi-label self-hosted runners without Buildchain
229
- guessing the labels.
36
+ Use `@v4-alpha` to select the alpha runtime. The exact called workflow SHA is
37
+ the runtime SHA; a second runtime selector cannot override it. Both the public
38
+ `build.yml` and the `.build.yml` backbone expose exactly one optional input:
39
+ `config-path`. Neither accepts command overrides, runtime refs, arbitrary JSON
40
+ configuration or legacy aliases.
230
41
 
231
- Only include platforms that should run. GitHub schedules matrix jobs before
232
- steps execute, so a disabled entry with unavailable runner labels can still
233
- block the workflow queue.
42
+ ## Project discovery
234
43
 
235
- `fail-fast` defaults to `false`, preserving the diagnostic behavior that
236
- collects every platform result. Required promotion callers can set it to `true`
237
- to cancel sibling native, container, and relay matrix lanes after the first
238
- failure. This input changes scheduling only: it does not reduce the declared
239
- platform matrix, turn cancellation into a pass, or alter artifact and release
240
- admission.
241
-
242
- ## Linux Job Containers
243
-
244
- Linux build platforms can run inside a digest-pinned job container while macOS
245
- and Windows keep using native runners. This is the recommended way to remove
246
- moving Linux runner prerequisites from Buildchain consumers: the Linux host only
247
- needs a GitHub Actions runner, Docker, and network access; common verification
248
- tools come from the image contract.
249
-
250
- Use the Kungfu verification image for lifecycle stages that need Git, jq,
251
- Python, uv, and fnm, but do not need native compilation:
44
+ At the repository root, exactly one of `.buildchain/buildchain.toml` and
45
+ `buildchain.toml` must exist. Missing or ambiguous configurations fail before
46
+ matrix scheduling. A nested project is selected with its configuration location:
252
47
 
253
48
  ```yaml
254
49
  jobs:
255
- build:
256
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
50
+ native:
51
+ uses: kungfu-systems/buildchain/.github/workflows/build.yml@v4-alpha
257
52
  with:
258
- runner-preset: kungfu-v4-self-hosted
259
- linux-container-preset: kungfu-verify
260
- ```
261
-
262
- `kungfu-verify` resolves to:
263
-
264
- ```text
265
- ghcr.io/kungfu-systems/build-images/kungfu-verify@sha256:11f0ba64267ce88174a4f73a9bf833ff4e9c59cd16ec3d08a6432a06c2be6fb1
266
- ```
267
-
268
- Callers that own a different Linux image can pass it explicitly:
269
-
270
- ```yaml
271
- with:
272
- linux-container-preset: custom
273
- linux-container-image: ghcr.io/example/project-build@sha256:<digest>
274
- ```
275
-
276
- `linux-container-image` should be pinned by digest. A floating tag makes the
277
- runner surface mutable and weakens Buildchain's release evidence.
278
-
279
- The workflow splits the matrix into two build jobs:
280
-
281
- - Linux platforms go to `build-linux-container` when a Linux container is
282
- configured.
283
- - All other platforms go to `build-native`.
284
-
285
- Artifact names, manifest paths, expected artifact checks, publish-source locks,
286
- and aggregate summaries are the same in both jobs. The split is an execution
287
- detail, not a different artifact contract.
288
-
289
- ## Native Rust Toolchains
290
-
291
- Native lifecycle jobs can request an isolated Rust installation instead of
292
- depending on a self-hosted runner user's PATH:
293
-
294
- ```yaml
295
- with:
296
- setup-rust: true
297
- rust-toolchain: "1.96.0"
298
- rustup-dist-server: "https://rsproxy.cn"
299
- rustup-update-root: "https://rsproxy.cn/rustup"
300
- cargo-registry-index: ${{ vars.BUILDCHAIN_CARGO_REGISTRY_INDEX }}
53
+ config-path: packages/native/.buildchain/buildchain.toml
301
54
  ```
302
55
 
303
- `setup-rust` defaults to `false`, so existing consumers are unchanged. When it
304
- is enabled, Buildchain installs `rust-toolchain` before the install, build, and
305
- verify lifecycle stages on every native matrix platform. Windows uses the
306
- official rustup bootstrap through `cmd.exe` and `curl.exe` into runner-temporary
307
- Cargo and rustup homes, so it works under a restrictive PowerShell execution
308
- policy and the service account does not depend on another user's PATH or mutate
309
- host toolchain state. Pin an exact toolchain for release builds. Linux container jobs continue
310
- to obtain Rust from their digest-pinned image contract; Buildchain does not
311
- mutate that container surface.
312
-
313
- The rustup server inputs are optional and default to Rust's official servers.
314
- Consumers behind a slow cross-border link may select a trusted transport mirror;
315
- rustup still verifies the selected toolchain's distribution metadata and
316
- component checksums.
317
-
318
- `cargo-registry-index` is also optional. When set, Buildchain exposes it to
319
- Cargo as `CARGO_REGISTRIES_CRATES_IO_INDEX` for every native lifecycle stage,
320
- so a self-hosted runner can use a repository or organization variable without
321
- committing private LAN topology to public workflow YAML. The endpoint must be a
322
- crates.io-compatible index whose `config.json` download contract serves the
323
- matching checksum-verified crate archives. An empty value preserves Cargo's
324
- normal crates.io behavior.
325
-
326
- The container image provides `fnm` but does not preinstall Node. Buildchain uses
327
- `fnm` inside the container to install the requested `node-version` before it
328
- runs Buildchain runtime scripts or lifecycle actions.
329
-
330
- Do not use `kungfu-verify` for stages that need CMake, Ninja, ccache, Conan, or
331
- Docker image publishing. Those should use a heavier native-build image or remain
332
- on a host runner until their image contract is explicit.
56
+ The config location determines the project working directory. Planning and
57
+ lifecycle execution discover the same file. Absolute paths, parent traversal,
58
+ symlink escape, malformed TOML and unknown `[build]` keys fail closed. A
59
+ configuration locator cannot carry a profile override or execution authority.
333
60
 
334
- ## Buildchain Runtime Override
335
-
336
- Stable consumers should keep the reusable workflow pinned to stable refs such as
337
- `@v3`. The optional `buildchain-ref` input is empty by default; empty means
338
- Buildchain resolves and executes the stable runtime selected by the workflow
339
- shell. The full train validation protocol is documented in
340
- [`runtime-train-validation.md`](runtime-train-validation.md).
341
-
342
- For one-off manual validation, a trusted maintainer can run the caller workflow
343
- with a temporary runtime override. The override inherits the caller's declared
344
- alpha or stable lane; trust authorizes the opaque ref, not cross-channel use:
345
-
346
- ```yaml
347
- on:
348
- workflow_dispatch:
349
- inputs:
350
- buildchain-ref:
351
- description: "Temporary Buildchain runtime ref for trusted manual validation"
352
- required: false
353
- default: ""
354
-
355
- jobs:
356
- build:
357
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
358
- with:
359
- buildchain-ref: ${{ inputs.buildchain-ref || '' }}
360
- ```
361
-
362
- Allowed override refs are deliberately narrow:
363
-
364
- | Ref form | Meaning |
365
- | --------------------------------------- | ------------------------------------------------------ |
366
- | `train/v3/v3.0/<capability>` | Temporary capability train under the active minor line |
367
- | `refs/heads/train/v3/v3.0/<capability>` | Explicit branch ref for the same train |
368
- | `<40-character SHA>` | Exact immutable Buildchain runtime commit |
369
-
370
- Override requests fail closed unless the event is `workflow_dispatch` and the
371
- actor has write, maintain, or admin permission on the caller repository. One
372
- non-override exact-pin case is also admitted: when the reusable workflow itself
373
- is invoked from an exact Buildchain SHA and `buildchain-ref` names that identical
374
- SHA, the run records `pinned-self`. The input cannot select code other than the
375
- already-running workflow shell, so protected push and pull-request publication
376
- jobs can retain one immutable runtime root. Different SHA and train requests
377
- still fail closed outside trusted manual dispatch.
378
-
379
- Pull requests, including same-repository pull requests and fork-originated pull
380
- requests, cannot select an independent `buildchain-ref` override. This keeps
381
- automated PR builds on the stable or exact pinned-self runtime surface.
382
-
383
- Every run resolves the runtime ref to an immutable SHA before checkout. The job
384
- summary and aggregate build summary record the workflow shell ref, requested
385
- runtime ref, resolved runtime ref, runtime SHA, stability class, trust decision,
386
- and rollback ref. Train refs are development validation refs: they do not move
387
- `v3`, `vX.Y`, `vX.Y-alpha`, npm dist-tags, or production release refs, and they
388
- must not be pinned as long-term production dependencies.
389
-
390
- Runtime override validates Buildchain runtime scripts, CLI code, local actions,
391
- config parsing, and lifecycle behavior. It cannot validate changes that require
392
- the outer reusable workflow YAML itself to change, such as new jobs,
393
- permissions, workflow outputs, or matrix topology. Those changes need a canary
394
- workflow path or a temporary explicit workflow ref.
395
-
396
- ## Floating Ref Contract Lock
397
-
398
- Stable consumers should use floating major refs such as `@v3`, but a floating
399
- ref is not blind trust. Each released Buildchain ref carries a package-owned
400
- runtime contract world in `dist/site/buildchain-contract.json`. Consumers may
401
- keep a small lock file, `.buildchain/contract-lock.json`, recording the
402
- Buildchain ref, resolved SHA, contract digest, compatibility digest, accepted
403
- major line, and compatibility policy they reviewed.
404
-
405
- The reusable build trust gate checks this lock before any heavy matrix job:
406
-
407
- 1. resolve the Buildchain runtime ref, for example `v3`, to an immutable SHA;
408
- 2. read `dist/site/buildchain-contract.json` from that checked-out Buildchain
409
- ref;
410
- 3. read the consumer's `.buildchain/contract-lock.json`;
411
- 4. compare the accepted contract with the current contract.
412
-
413
- SHA drift alone is not a failure. `v3` is expected to advance. Buildchain only
414
- fails fast when the accepted contract is no longer compatible, for example a
415
- required input is removed, a required output disappears, a protected behavior
416
- promise changes, or the major line changes. Additive changes such as optional
417
- inputs, optional outputs, diagnostics, or documentation updates continue under
418
- the default `major-compatible` policy.
419
-
420
- ```yaml
421
- jobs:
422
- build:
423
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
424
- permissions:
425
- contents: read
426
- issues: write
427
- id-token: write
428
- with:
429
- buildchain-contract-lock-path: .buildchain/contract-lock.json
430
- buildchain-contract-compatibility-policy: major-compatible
431
- buildchain-contract-drift-issue-mode: compatible-and-breaking
432
- ```
433
-
434
- When compatible drift is detected, the build continues and Buildchain opens or
435
- updates a low-priority issue in the consumer repository. The issue records the
436
- old SHA/digest, new SHA/digest, compatibility result, workflow run, and the next
437
- action: review the Buildchain release notes and update the lock. When breaking
438
- drift is detected, the same issue path is used, but the trust gate fails before
439
- matrix build or publish work starts. If the workflow token cannot write issues,
440
- Buildchain writes a copyable issue body into the job summary.
441
-
442
- The lock is intentionally small. It does not copy the full contract. The full
443
- contract remains in the Buildchain ref and package; the consumer records only
444
- what it accepted and the policy used to compare future floating-ref movement.
445
-
446
- Advanced alpha-channel consumers select the matching workflow shell:
447
-
448
- ```yaml
449
- jobs:
450
- build:
451
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3-alpha
452
- with:
453
- buildchain-contract-lock-path: .buildchain/contract-lock.json
454
- ```
455
-
456
- The runtime follows the called workflow through `job.workflow_ref`. Callers may
457
- also pass `buildchain-ref: v3-alpha` explicitly; official floating refs are
458
- ordinary channel selections and are allowed on pull requests and pushes. Train
459
- refs and exact SHAs remain trusted manual overrides.
460
-
461
- ## Shifu Cache Profile Passthrough
462
-
463
- Buildchain can carry one trusted Shifu cache-profile reference and its exact
464
- digest into lifecycle execution. Its contract is an opaque reference and digest
465
- only. This surface is deliberately opaque:
466
- Buildchain does not fetch the profile, parse JSON, select cache services,
467
- rewrite bindings, decide fallback, or emit Shifu resolution evidence. Those
468
- semantics remain owned by the consumer's pinned Shifu implementation.
469
-
470
- ```yaml
471
- jobs:
472
- build:
473
- uses: kungfu-systems/buildchain/.github/workflows/build.yml@v3
474
- with:
475
- shifu-cache-profile-ref: ${{ vars.SHIFU_CACHE_PROFILE_REF }}
476
- shifu-cache-profile-digest: ${{ vars.SHIFU_CACHE_PROFILE_DIGEST }}
477
- ```
478
-
479
- The reusable workflow passes the pair as `SHIFU_CACHE_PROFILE_REF` and
480
- `SHIFU_CACHE_PROFILE_DIGEST` to install, build, and verify lifecycle commands.
481
- The consumer must invoke its Shifu cache-aware execution surface. An empty pair
482
- preserves existing behavior; a consumer Shifu should fail closed when exactly
483
- one value is present or the resolved bytes do not match the expected digest.
484
-
485
- Use trusted repository or organization variables rather than PR-controlled
486
- files for private/LAN references. The variables must remain secret-free; any
487
- credentials use a separate provider-approved secret surface and must not be
488
- embedded in the profile reference. This passthrough is separate from
489
- Buildchain's locked source checkout cache below: Buildchain owns checkout
490
- transport and source identity, while Shifu owns post-checkout execution cache
491
- bindings and receipts.
492
-
493
- ## Locked Source Checkout Cache
494
-
495
- Self-hosted runners that build large repositories can opt into a locked checkout
496
- cache for both the consumer source and the Buildchain runtime. This changes only
497
- the Git object transport. Buildchain still resolves `publish-source-sha` and the
498
- runtime SHA before any build runner starts, checks out those exact commits, and
499
- verifies each final `HEAD` plus the resolved consumer source tree SHA before
500
- lifecycle commands run.
501
-
502
- ```yaml
503
- jobs:
504
- build:
505
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
506
- with:
507
- runner-preset: kungfu-v4-self-hosted
508
- checkout-cache-mode: auto
509
- checkout-cache-mirror-url-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE }}
510
- checkout-cache-reference-repository-template: ${{ vars.BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE }}
511
- checkout-cache-fallback: github
512
- checkout-cache-timeout-seconds: 60
513
- checkout-cache-github-timeout-seconds: 600
514
- checkout-cache-fetch-attempts: 3
515
- checkout-history-mode: shallow
516
- ```
517
-
518
- `checkout-cache-mode` accepts:
519
-
520
- | Mode | Behavior |
521
- | --------- | ----------------------------------------------------------------------------------------------------------- |
522
- | `off` | Default. Buildchain fetches the locked commit from GitHub. |
523
- | `auto` | Try the trusted cache first; on miss, record the miss and fall back according to `checkout-cache-fallback`. |
524
- | `require` | Require the cache to provide the locked commit and fail before lifecycle work if unavailable. |
525
-
526
- `checkout-history-mode` defaults to `shallow`, preserving the bounded single-
527
- commit transport used by ordinary builds. Set it to `full` only when a
528
- consumer gate must inspect source ancestry, for example when an Alpha pull
529
- request qualifies GitHub's synthetic merge ref while retained evidence is
530
- bound to an ancestor of the source-lock head. Full mode still verifies the
531
- resolved immutable `HEAD` and tree; it changes only whether the advertised
532
- source ref is fetched with depth one or with its reachable history.
533
-
534
- The cache can be a local/LAN mirror URL template or a runner-local bare
535
- reference repository template. Templates support `{owner}`, `{repo}`,
536
- `{repository}`, `{repositorySlug}`, and `{sha}`. The workflow also reads
537
- repository or organization variables named
538
- `BUILDCHAIN_CHECKOUT_CACHE_MIRROR_URL_TEMPLATE` and
539
- `BUILDCHAIN_CHECKOUT_CACHE_REFERENCE_REPOSITORY_TEMPLATE`, so consumers can keep
540
- private LAN topology out of repository YAML.
541
-
542
- The GitHub-hosted trust gate resolves the reusable workflow shell to an exact
543
- commit and uploads that shell's small checkout bootstrap script. Native and
544
- Linux-container build jobs download the bootstrap, then use the same cache
545
- policy to obtain both the selected Buildchain runtime and consumer source at
546
- their already resolved immutable SHAs. Keeping the bootstrap owned by the
547
- workflow shell is important when `@vN-alpha` routes a stable release to an older
548
- `vN` runtime: the stable runtime does not need to already contain the newest
549
- checkout transport implementation. This also prevents a large direct
550
- `actions/checkout` runtime clone from becoming a separate timeout path on
551
- constrained self-hosted uplinks. The bootstrap artifact does not contain the
552
- runtime repository and cannot move either selected ref.
553
-
554
- Do not read cache URLs or reference paths from PR-controlled files such as
555
- `.buildchain/buildchain.toml`. These values are trusted workflow inputs or repo/org
556
- variables. Buildchain does not pass GitHub credentials to cache mirrors or
557
- reference repositories. If it must fall back to GitHub, the workflow token is
558
- used only for the GitHub fetch path. Cache attempts use
559
- `checkout-cache-timeout-seconds`; the potentially larger GitHub fallback uses
560
- the independent `checkout-cache-github-timeout-seconds` budget (600 seconds by
561
- default). Buildchain fetches the advertised source ref before trying an exact
562
- SHA, so a cache hit or stale-cache seed can contribute objects and the fallback
563
- does not first waste a full timeout on an unadvertised SHA. Retryable timeout
564
- and transient network failures use the bounded `checkout-cache-fetch-attempts`
565
- budget; permanent failures stop immediately. Diagnostics record both timeout
566
- budgets and the actual GitHub fetch attempts before exact HEAD/tree
567
- verification.
568
-
569
- Each platform diagnostics artifact includes `source-checkout.json` and embeds a
570
- compact `sourceCheckout` summary in `diagnostics.json`: mode, transport,
571
- hit/miss, fallback reason, duration, final HEAD verification, and tree
572
- verification. Remote URLs are sanitized and local reference paths are represented
573
- by a short display name plus fingerprint, not by secret-bearing credentials.
574
- Runtime checkout evidence is uploaded separately as `runtime-checkout.json`,
575
- including cache transport, fallback attempts, and exact runtime `HEAD`
576
- verification, even when a later lifecycle step fails.
577
-
578
- ## Auditable Compiler Cache
579
-
580
- Consumers can prepare `sccache` on selected platforms after the install
581
- lifecycle and before compilation:
582
-
583
- ```yaml
584
- with:
585
- compiler-cache-provider: sccache
586
- compiler-cache-platforms-json: '["windows-x64"]'
587
- compiler-cache-required: true
588
- ```
589
-
590
- The consumer remains responsible for installing and pinning the tool before
591
- the preparation step. Buildchain probes its version, runs `sccache
592
- --zero-stats`, and writes
593
- `compiler-cache-preparation.json`. The receipt binds the source commit/tree,
594
- Buildchain runtime, platform, cache profile, and any declared dependency,
595
- toolchain, or policy roots. It resets counters only; it does not delete cached
596
- compiler outputs. The same preparation exports `RUSTC_WRAPPER`,
597
- `CMAKE_C_COMPILER_LAUNCHER`, and `CMAKE_CXX_COMPILER_LAUNCHER` so Cargo and
598
- CMake/Ninja compilation actually passes through the audited tool.
599
-
600
- After the build lifecycle, Buildchain probes the reset counter set again. When
601
- `compiler-cache-required` is true, the build fails closed if sccache observed
602
- zero compiler requests or zero cacheable requests. This prevents an installed
603
- but unbound sccache binary from being reported as an active compiler cache.
604
-
605
- Final diagnostics admit sccache hit/miss outcomes as current-run evidence only
606
- when that preparation receipt is present and valid. A bare `sccache
607
- --show-stats` result without the reset receipt remains cumulative and is
608
- reported as unavailable for the current run. The preparation receipt is copied
609
- into the small diagnostics artifact and sealed by
610
- `diagnostics-manifest.json`.
611
-
612
- When a Buildchain maintainer asks for downstream validation, the expected
613
- request is:
614
-
615
- ```text
616
- Buildchain train ready: buildchain-ref=train/v3/v3.0/<capability>.
617
- Keep uses: ...@v3; run workflow_dispatch with that buildchain-ref and report the runtime evidence summary.
618
- ```
619
-
620
- After validation succeeds, the Buildchain change should continue through the
621
- normal mainline and release path. Do not treat the train as a pending merge
622
- item; it is only a temporary fast-use, diagnostic, and rollback channel. It may
623
- remain for a retention window after release, with old trains handled by a
624
- separate periodic cleanup task.
625
-
626
- ## Workflow Outputs
627
-
628
- The reusable workflow exposes the resolved contract:
629
-
630
- | Output | Meaning |
631
- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
632
- | `runner-preset` | Resolved preset, or `custom` when `platforms-json` was provided |
633
- | `platforms-json` | Exact matrix JSON used by the build job |
634
- | `platform-count` | Number of matrix platforms |
635
- | `linux-container-enabled` | `true` when Linux platforms are routed through a job container |
636
- | `linux-container-image` | Resolved digest-pinned Linux job container image |
637
- | `build-summary-artifact` | Uploaded aggregate summary artifact name |
638
- | `build-diagnostics-summary-artifact` | Uploaded aggregate diagnostics summary artifact name |
639
- | `release-candidate-passport-artifact` | Uploaded PR-stage release-candidate passport artifact name when `release-candidate` is enabled |
640
- | `release-candidate-passport-json` | Compact release-candidate passport JSON when `release-candidate` is enabled |
641
- | `build-summary-json` | Compact aggregate JSON with platform count, file count, and byte total |
642
- | `build-diagnostics-summary-json` | Compact aggregate diagnostics JSON with platform, lifecycle warning/error, diagnostics contract warning, and sidecar manifest warning totals |
643
- | `trusted-event` | `true` when the event is trusted enough to reach build runners |
644
- | `buildchain-runtime-ref` | Runtime ref selected after applying the empty-default or override policy |
645
- | `buildchain-runtime-sha` | Immutable Buildchain runtime commit used by all runtime checkouts |
646
- | `buildchain-runtime-class` | `stable`, `alpha`, `authority`, `train`, `exact-sha`, or `development` |
647
- | `buildchain-runtime-override` | `true` when an authority, train, or exact-SHA `buildchain-ref` override was accepted |
648
- | `buildchain-runtime-trust-decision` | Runtime override trust decision |
649
- | `buildchain-contract-lock-status` | `unchanged`, `compatible-drift`, `breaking-drift`, `missing-lock`, `non-floating-runtime`, or first-release `runtime-contract-unavailable` |
650
- | `buildchain-contract-lock-drift` | `true` when the floating runtime SHA or contract digest changed |
651
- | `buildchain-contract-digest` | Current Buildchain runtime contract digest |
652
- | `publish-channel` | Resolved publish channel requested by the caller |
653
- | `publish-allowed` | `true` only when this event/ref may publish after verification |
654
- | `publish-reason` | Human-readable reason for the publish gate decision |
655
- | `publish-source-ref` | Gate source ref that was resolved before checkout |
656
- | `publish-source-sha` | Exact source commit used by checkout, build, verify, and artifacts |
657
- | `publish-source-locked` | `true` when a `publish-gate/*` source ref was explicitly locked |
658
- | `publish-source-channel` | `alpha`, `release`, `anchor`, or `major` parsed from the source ref |
659
- | `publish-source-line` | Product line parsed from source refs such as `v22/v22.22` |
660
- | `publish-source-consumer-version` | Consumer package version parsed from source refs |
661
- | `release-manifest-json` | Resolved release manifest including source lock, version state, and anchor data |
662
-
663
- The aggregate summaries are intentionally artifacts as well as outputs. GitHub
664
- Actions matrix outputs are not a reliable place to carry every platform's full
665
- manifest, so Buildchain uploads each platform manifest and then emits one
666
- aggregate build summary artifact after the matrix completes. Buildchain uploads
667
- `diagnostics-summary.json` as a separate aggregate diagnostics summary artifact,
668
- a compact rollup of each platform's small diagnostics upload. The rollup keeps
669
- per-platform runner facts, checked tool versions/missing tools, package
670
- manager/cache directory details, compiler-cache availability, lifecycle timing,
671
- process sampler context, and links back to the exact platform artifacts. Each
672
- platform diagnostics upload includes `diagnostics.json`,
673
- `diagnostics-manifest.json`, the lifecycle `events.jsonl`, and process sampler
674
- sidecars when enabled, so slow-build diagnosis does not require downloading the
675
- binary platform artifact or the aggregate build summary. The sidecar manifest
676
- records the uploaded diagnostics files with bytes and sha256 hashes. Each
677
- `diagnostics.json` also records the related binary artifact name, manifest
678
- artifact name, diagnostics artifact name, diagnostics sidecar manifest path, and
679
- platform id in `links`, so a reviewer can navigate from the small diagnostics
680
- artifact back to the exact platform outputs when deeper inspection is needed.
681
- The workflow output `build-diagnostics-summary-json` includes
682
- `diagnosticsContractWarningCount` and `diagnosticsManifestWarningCount` so
683
- release jobs can detect drifting diagnostics JSON contracts and missing or
684
- drifting diagnostics sidecar manifests without downloading the per-platform
685
- diagnostics artifacts first.
686
-
687
- ## Artifact Signing Authority
688
-
689
- Artifact signing is a Buildchain capability, not a macOS application workflow.
690
- Consumers declare desired signature state next to their artifact facts; they do
691
- not configure certificates, Team IDs, notary credentials, protected
692
- environments, authority roles, or signing jobs:
61
+ ## Project configuration
693
62
 
694
63
  ```toml
695
- [[signing.artifacts]]
696
- id = "native-engine"
697
- path = "dist/kungfu-engine"
698
- profile = "auto"
699
- kind = "mach-o"
700
- platforms = ["macos-arm64", "macos-x64"]
701
- ```
64
+ schema = 1
702
65
 
703
- Every native and container build lane reads this declaration after the build
704
- lifecycle and before verification. Buildchain binds the exact artifact bytes or directory tree to
705
- the caller repository, source commit, source tree, immutable runtime, platform,
706
- and requested signature semantics, then publishes a deterministic
707
- `<artifact>-signing-request-<platform>-<source-sha>-<run-id>-<run-attempt>`
708
- request. No consumer
709
- workflow step is required. The lifecycle runner automatically adds declarations
710
- selected for the current platform to the `build` manifest scan, including
711
- subjects outside the caller's ordinary `artifact-paths`; this extends the
712
- evidence preimage without silently adding those subjects to the ordinary
713
- artifact upload.
714
-
715
- The request root is a Buildchain-owned generated output. After the declaration,
716
- lifecycle manifest, and source paths pass validation, sealing replaces that root
717
- before materializing the current request set. This keeps repeated jobs on a
718
- self-hosted runner idempotent and prevents stale requests from an earlier run
719
- from entering the uploaded request artifact. An output root that contains the
720
- workspace, working directory, lifecycle manifest, or any declared subject is
721
- rejected before cleanup.
722
-
723
- Self-hosted runners whose network requires different routes for Artifact upload
724
- and download can scope an upload-only proxy bypass to the sealed signing request:
725
-
726
- ```yaml
727
- with:
728
- artifact-signing-request-upload-no-proxy: ".blob.core.windows.net"
729
- ```
730
-
731
- The caller repository variable
732
- `BUILDCHAIN_ARTIFACT_SIGNING_REQUEST_UPLOAD_NO_PROXY` provides the same value
733
- without changing a consumer workflow; an explicit workflow input takes
734
- precedence. When neither is set, Buildchain preserves the runner's existing
735
- `NO_PROXY` and `no_proxy` values. The resolved value applies only to the
736
- Buildchain-owned signing-request upload. Authority dispatch and immutable
737
- signed-result download keep the runner's original proxy route. This is a
738
- transport control only: it does not change request bytes, signing authority,
739
- artifact identity, or verification policy.
740
-
741
- `profile = "auto"` resolves signable Apple artifacts such as Mach-O files,
742
- `.dylib`, `.framework`, `.app`, `.xpc`, `.plugin`, `.pkg`, `.dmg`, and macOS
743
- archives containing native code to the native `apple-developer-id` provider.
744
- For a declared macOS `archive`, the authority safely extracts the sealed
745
- container, signs and verifies every Mach-O payload, signs Mach-O payloads inside
746
- embedded Python wheels, rebuilds each affected wheel's PEP 427 `RECORD`, and
747
- recreates the original zip or tar.gz before returning the exact final bytes.
748
- Archives whose executable hosts a JIT runtime can additionally request the
749
- Buildchain-owned `entitlements_profile = "jit-executable-v1"` and exact paths,
750
- for example `entitlements_paths = ["product/runtime/python/bin/python3"]`.
751
- Paths are relative to the extracted archive root. The authority
752
- then attaches only `com.apple.security.cs.allow-jit` to the exact executable
753
- Mach-O files sealed in `entitlements_paths`, leaves every other executable and
754
- library without exception entitlements, and records the profile, paths, and
755
- entitled executable count in provider evidence. Consumer-provided entitlement
756
- files and wildcard target paths are unsupported. The profile is valid only for
757
- Apple `archive` requests and fails closed on an unsafe, missing,
758
- non-executable, duplicate, or unsealed target path.
759
- Windows `pe` and `binary` artifacts
760
- resolve to timestamped native `windows-authenticode`; Windows PE never falls
761
- back to a detached signature. Linux and other non-native binary files,
762
- archives, blobs, and directories resolve to `detached-signature-v1`. Buildchain records that as a
763
- detached cryptographic signature and never misrepresents it as an operating
764
- system code signature. Explicit incompatible provider/kind/platform
765
- combinations fail closed.
766
-
767
- The request schema rejects credential and authority-infrastructure fields. The
768
- Buildchain-owned signing authority is responsible for credential selection,
769
- native signing, notarization where applicable, immutable result delivery, and a
770
- receipt bound to the request digest, runtime SHA, output digest, and signature
771
- evidence. Consumer repositories neither receive nor duplicate credential-island
772
- material. Each platform lane seals and uploads the unsigned request plus a
773
- run-attempt-bound control request, completes functional verification, and exits.
774
- It does not dispatch or poll the authority. A separate `ubuntu-24.04` controller
775
- starts only after the build matrices complete, validates the exact source,
776
- tree, runtime, request-set root, platform, run attempt, and correlation, then
777
- dispatches and awaits the protected authority workflow. Its retained receipt
778
- records two independent immutable identities: the consumer Buildchain runtime
779
- SHA carried by the control request and the exact authority-ref commit resolved
780
- immediately before dispatch. The former validates the request-producing
781
- runtime; the latter must equal the authority workflow run's `head_sha` and is
782
- retained with the exact authority run and result artifact. If the protected ref
783
- moves between resolution and dispatch, settlement fails closed. Failure, timeout, or
784
- cancellation produces a non-qualifying receipt and no finalization delegation.
785
-
786
- A second GitHub-hosted finalization lane downloads the original control request,
787
- controller receipt, and delegation, verifies their roots and coordinates agree,
788
- then verifies the authority result against the sealed request, imports the exact
789
- signed bytes, and recomputes the final manifest before replacing the
790
- deterministic artifact. The signing result is never downloaded back to a
791
- self-hosted native runner, so a macOS caller is released before credential-island
792
- signing and notarization complete.
793
- Platform manifests, KFD evidence, checksums, and Release Passport inputs
794
- therefore observe the final signed artifact rather than the pre-signing build
795
- output.
796
-
797
- For a standalone Mach-O request, the authority requires strict Developer ID
798
- verification, the declared Team ID, hardened runtime, and an `Accepted`
799
- `notarytool` result for the exact submission. Apple creates the notarization
800
- ticket for that binary and publishes it online, but
801
- [standalone binaries do not support stapling](https://developer.apple.com/documentation/security/customizing-the-notarization-workflow).
802
- Buildchain therefore records
803
- `standalone-notary-ticket-online` and does not misapply app-bundle
804
- `spctl --assess --type execute` semantics to the raw executable.
805
-
806
- For a compound archive request, the authority notarizes the complete extracted
807
- signed product tree and records `compound-notary-ticket-online`. A generic
808
- archive container cannot carry a stapled ticket and is not itself a Gatekeeper
809
- execution target; Gatekeeper evaluates the extracted signed code. Archive path
810
- and symlink validation fail closed before any payload is signed.
811
-
812
- For a declared `app-bundle`, the same protected authority extracts the sealed
813
- application, derives and verifies its bundle identity, signs nested native code,
814
- submits both the application and disk image for notarization, staples and
815
- Gatekeeper-assesses both deliverables, and returns a ZIP, DMG, evidence document,
816
- and source-bound manifest. The reusable workflow verifies those returned bytes
817
- on GitHub-hosted infrastructure, adds them to the normal macOS platform payload,
818
- and publishes a separate `<artifact>-macos-credential-<source-sha>` projection
819
- for release pipelines that consume the credential-island evidence contract.
820
- Consumers declare the `.app` under `[[signing.artifacts]]`; they do not configure
821
- an environment, certificate, notary credential, or authority workflow.
822
-
823
- The durable v3 authority runtime is
824
- `authority/v3/v3.0/artifact-signing`. It is channel-neutral: alpha and stable
825
- release work use the same protected `buildchain-artifact-signing` environment
826
- and provider identities. The authority ref is protected independently from
827
- release channels and can advance only through reviewed, checked changes; the
828
- temporary `train/v3/v3.0/artifact-signing-authority` ref is retained only as a
829
- bounded migration rollback.
830
-
831
- The older `credential-island-macos-*` reusable-workflow inputs remain a
832
- compatibility surface while existing callers migrate. They are not the target
833
- consumer contract and must not be used to design new integrations.
834
-
835
- ## Artifact Transfer Relay
836
-
837
- By default, platform jobs upload payloads, manifests, and diagnostics directly
838
- to GitHub artifacts:
839
-
840
- ```yaml
841
- with:
842
- artifact-transfer-mode: github-artifacts
843
- artifact-compression-level: 0
844
- ```
845
-
846
- Direct GitHub Artifact payloads default to compression level `0`. Buildchain
847
- artifacts are commonly already-compressed archives; storing them without a
848
- second compression pass shortens the upload window while preserving the same
849
- artifact name, run/id/digest binding, retention, and no-overwrite behavior.
850
- Direct build and signed-finalization payload uploads include hidden files under
851
- the caller-declared artifact paths, matching the relay path so manifest-bound
852
- dotfiles are not silently removed in transit.
853
- Callers may select `1` through `9` for payloads that materially benefit from
854
- compression. Manifests and diagnostics retain their existing small-artifact
855
- behavior.
856
-
857
- Large self-hosted native builds can opt into the first-class S3 relay path:
858
-
859
- ```yaml
860
- jobs:
861
- build:
862
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
863
- with:
864
- runner-preset: kungfu-v4-self-hosted
865
- artifact-transfer-mode: s3-to-github-artifacts
866
- artifact-relay-s3-bucket: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET }}
867
- artifact-relay-s3-region: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_REGION }}
868
- artifact-relay-s3-prefix: ${{ vars.BUILDCHAIN_ARTIFACT_RELAY_S3_PREFIX }}
869
- ```
870
-
871
- The mode is a policy for platforms that run outside GitHub. GitHub-hosted
872
- platforms always upload directly with `actions/upload-artifact`, even when a
873
- mixed matrix requests `s3-to-github-artifacts`; they never send their payloads
874
- through S3 or the replay job. Buildchain recognizes its hosted presets and the
875
- standard hosted runner labels. Custom matrices with non-standard hosted labels
876
- must declare `"githubHosted": true` on those platform rows.
877
-
878
- For remaining relay-mode platforms, each job uploads the heavy payload files to
879
- S3 and uploads only a small `relay-manifest.json` to GitHub. A GitHub-hosted
880
- `relay-artifacts` job then assumes the configured download role, downloads the
881
- payloads from S3, verifies every file by SHA256, and re-uploads the normal
882
- GitHub artifacts under the same artifact names that direct mode uses. Downstream
883
- summary, release-candidate, and promote-only workflows therefore continue to
884
- consume GitHub artifacts and do not need custom S3 logic.
885
- The relay implementation uses Node.js plus the standard AWS environment
886
- credentials from GitHub OIDC; runner images and build containers do not need the
887
- AWS CLI installed.
888
-
889
- After the GitHub artifact uploads succeed, Buildchain deletes the S3 objects
890
- listed in the relay manifest for that platform. If any download, verification,
891
- or GitHub artifact upload fails, cleanup is skipped so maintainers can inspect
892
- the retained S3 payload. Configure a short bucket lifecycle expiration as a
893
- cost and cleanup backstop.
894
-
895
- The relay configuration is intentionally generic. Buildchain does not hard-code
896
- organization buckets, regions, or role ARNs. Callers may pass explicit inputs,
897
- or set repository/organization variables and secrets using these names:
898
-
899
- | Variable or secret | Meaning |
900
- | ------------------------------------------------ | ------------------------------------------------------- |
901
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_BUCKET` | Relay bucket name |
902
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_REGION` | Relay bucket region |
903
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_PREFIX` | Relay object prefix; defaults to `buildchain-artifacts` |
904
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_ROLE_ARN` | Shared OIDC role ARN for upload and download |
905
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_UPLOAD_ROLE_ARN` | Upload OIDC role ARN for self-hosted build jobs |
906
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_DOWNLOAD_ROLE_ARN` | Download OIDC role ARN for the GitHub-hosted relay job |
907
- | `BUILDCHAIN_ARTIFACT_RELAY_S3_OIDC_AUDIENCE` | Optional OIDC audience override |
908
-
909
- For AWS China regions, Buildchain defaults the OIDC audience to
910
- `sts.amazonaws.com.cn`; other regions default to `sts.amazonaws.com`. The caller
911
- workflow must allow `id-token: write`, and the target role trust policy should
912
- restrict GitHub OIDC claims to the expected organization, repository, workflow,
913
- and branch/ref. The S3 permissions should be scoped to the relay bucket/prefix
914
- used by the repository.
915
- Upload roles need write/delete access under the relay prefix; download roles
916
- need read access plus delete access for successful cleanup.
917
-
918
- Relay mode is opt-in and does not affect forks or open-source users that do not
919
- configure S3. Missing bucket, region, upload role, or download role values fail
920
- before the heavy build matrix is scheduled. Buildchain treats S3 as a transport
921
- cache, not as the final release evidence store; the final audit entry remains
922
- the GitHub artifact set plus the Buildchain build summary and release-candidate
923
- passport.
924
-
925
- Set `release-candidate: true` when the successful reusable build is meant to be
926
- the artifact source promoted later. Buildchain then uploads
927
- `release-candidate-passport.json` under the
928
- `<artifact-name>-release-candidate-<publish-source-sha>` artifact name. Promotion
929
- jobs can pass that passport to `promote-buildchain-ref` with
930
- `promote-only-release-candidate: "true"` so source, channel, platforms, and the
931
- aggregate build-summary hash are checked before publish-gate side effects. The
932
- passport records the locked commit's Git tree SHA, so a post-merge channel HEAD
933
- can be accepted only when it is tree-equivalent to the PR-stage build evidence.
934
-
935
- ## Publish Gate
936
-
937
- Buildchain separates "may build/verify" from "may publish." A same-repository
938
- pull request may be trusted enough to run the build matrix, but it still must
939
- not publish packages, S3 objects, release pages, or preview aliases. Publishing
940
- is allowed only when the caller explicitly requests a channel and the current
941
- event/ref matches that channel.
942
-
943
- Use `publish-channel` to request a channel:
944
-
945
- ```yaml
946
- jobs:
947
- build:
948
- uses: kungfu-systems/buildchain/.github/workflows/.build.yml@v3
949
- with:
950
- publish-channel: release
951
-
952
- publish:
953
- needs: build
954
- if: ${{ needs.build.outputs.publish-allowed == 'true' }}
955
- runs-on: ubuntu-24.04
956
- steps:
957
- - run: ./scripts/publish.sh
958
- ```
959
-
960
- Default channels are:
961
-
962
- | Channel | Allowed refs |
963
- | --------- | ---------------------------------------------------------------------------------------------------- |
964
- | `none` | Never publishes; this is the default |
965
- | `alpha` | `alpha/vN/vN.M` branches or exact `vN.M.P-alpha.K` tags |
966
- | `release` | `release/vN/vN.M` branches or release tags such as `vN.M.P`, `vN.M`, `vN` |
967
- | `major` | `publish-gate/major`, legacy `major-gate`, or next-major release tags such as `vN.0.0`, `vN.0`, `vN` |
968
-
969
- Pull request events always produce `publish-allowed=false`, even when the PR is
970
- from the same repository. Untrusted fork events also produce
971
- `publish-allowed=false`; with the default `untrusted-policy: fail`, the workflow
972
- then fails before any build runner starts.
973
-
974
- Projects with their own channel names can pass `publish-refs-json`:
975
-
976
- ```yaml
977
- with:
978
- publish-channel: nightly
979
- publish-refs-json: >-
980
- {"nightly":["^refs/heads/nightly/v\\d+$"]}
981
- ```
982
-
983
- The aggregate build summary includes the same publish gate decision under
984
- `publishGate`, so a downloaded artifact summary explains both what was built and
985
- why it was or was not eligible to publish.
986
-
987
- ## Publish Source Lock
988
-
989
- `publish-channel` answers "may this event publish?" Source lock answers "which
990
- source tree is the publish decision about?" A caller can pass `publish-source-ref`
991
- to bind a publish run to a reviewed gate branch before any checkout happens:
992
-
993
- | Ref | Meaning |
994
- | ------------------------------------------------ | --------------------------------------------------------------------------- |
995
- | `publish-gate/alpha/<line>/<consumer-version>` | Build and publish an alpha candidate for a consumer line |
996
- | `publish-gate/release/<line>/<consumer-version>` | Build and publish a production candidate for a consumer line |
997
- | `publish-gate/anchor` | Resolve an explicit anchor request; it does not publish artifacts by itself |
998
- | `publish-gate/major` | Gate the next major source state |
999
- | `major-gate` | Legacy compatibility alias for the major gate |
1000
-
1001
- For alpha and release refs, `<line>` is intentionally allowed to contain `/`, so
1002
- Kungfu-style lines such as `v22/v22.22` stay readable. The final path segment is
1003
- the consumer-visible version, for example `22.22.3-kf.0`.
1004
-
1005
- The reusable workflow resolves the branch tip to `publish-source-sha`, checks out
1006
- that SHA in every build job, and uses the same SHA in artifact names, manifests,
1007
- and aggregate summaries. Reruns therefore rebuild the same source tree even if a
1008
- gate branch moves later.
1009
-
1010
- Before any heavy build matrix is scheduled, the workflow also verifies that the
1011
- target channel ref implied by the source lock already points at
1012
- `publish-source-sha` and that the target channel HEAD came from the required
1013
- merged same-repository channel PR. `publish-gate/alpha/<line>/<version>` must
1014
- match `alpha/<line>` and have PR lineage `dev/<line> -> alpha/<line>`;
1015
- `publish-gate/release/<line>/<version>` must match `release/<line>` and have PR
1016
- lineage `alpha/<line> -> release/<line>`. If either check fails, the run fails
1017
- fast with a diagnostic telling maintainers to merge the source commit through
1018
- the channel PR first. This keeps verify from spending runner time on a source
1019
- tree that cannot legally enter the requested publish channel.
1020
-
1021
- The resolved release manifest is uploaded as an artifact and emitted as
1022
- `release-manifest-json`. It records:
1023
-
1024
- - source ref, source SHA, channel, line, and consumer version;
1025
- - configured version strategy and configured version-state files;
1026
- - each version file's value, with release gates failing closed if the configured
1027
- files do not equal the consumer version;
1028
- - anchor manifest summary for anchored/manual projects;
1029
- - explicit anchor request JSON for `publish-gate/anchor`;
1030
- - publish registry, dist-tag, and gate visibility metadata.
1031
-
1032
- Publish side-effect jobs should verify the lock immediately before publishing:
1033
-
1034
- ```yaml
1035
- - name: Verify publish gate did not move
1036
- run: node .buildchain/runtime/scripts/verify-publish-source-lock.mjs
1037
- env:
1038
- BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}
1039
- BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}
1040
- BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}
1041
- GITHUB_TOKEN: ${{ github.token }}
1042
- ```
1043
-
1044
- If the branch tip no longer matches the manifest SHA, the publish job must fail
1045
- closed. Moving a gate branch creates a new publish decision and should produce a
1046
- new build run.
1047
-
1048
- ## Release Candidate Promote-Only
1049
-
1050
- For native package sets, the PR build is the only heavy build. When a PR targets
1051
- `alpha/<line>` or `release/<line>`, `.build.yml` uploads a release-candidate
1052
- bundle next to the platform artifacts. The bundle contains:
1053
-
1054
- - `release-candidate.passport.json`;
1055
- - the aggregate `build-summary.json`;
1056
- - copied platform manifest evidence for the built platforms.
1057
-
1058
- The passport records two separate source identities:
1059
-
1060
- - `builtSourceSha` / `builtSourceTreeSha`: the PR-stage source that produced the
1061
- artifacts, usually the PR merge ref;
1062
- - `promotionChannelSha` / `promotionChannelTreeSha`: the post-merge channel
1063
- commit used for publish authority.
1064
-
1065
- The reusable promote wrapper resolves the merged PR, finds exactly one matching
1066
- PR-stage release-candidate artifact, downloads it with the build summary and
1067
- payload artifacts from the same PR-stage run, validates the payload count,
1068
- compares the built tree with the promotion channel tree, locks
1069
- `publish-gate/{alpha,release,major}` to the promotion channel commit, and then
1070
- calls `actions/promote-buildchain-ref` with
1071
- `promote-only-release-candidate: "true"` and
1072
- `require-publish-source-lock: "true"`. The wrapper passes the created
1073
- `publish-gate/*` ref, target SHA, and `locked=true` into the promote action, so
1074
- floating `@v3` consumers receive publish-side source-lock drift protection by
1075
- default. It also defaults `branch-protection-bypass-apps` to `github-actions`
1076
- so the workflow automation can apply generated version-state and channel
1077
- bookkeeping on protected `dev`/`alpha`/`release` branches after the reviewed
1078
- channel PR has merged. Other App slugs and all user or team bypass actors are
1079
- rejected. The wrapper uses the run-scoped `github.token` as the generated ref
1080
- update token for protected bookkeeping PATCH calls, so the exact GitHub Actions
1081
- App authority can sync dev immediately after alpha/release publish without a
1082
- post-publish PR.
1083
- It does not call `.build.yml`, does not create a matrix, and must fail before
1084
- publish if the RC evidence, payload set, or source-lock ref is missing or
1085
- ambiguous.
1086
-
1087
- The public `release-candidate-promote.yml` is a generated channel router. It
1088
- derives the publication lane from `target-ref`, then selects the matching
1089
- advanced workflow shell, runtime, and consumer lock before the advanced
1090
- promotion starts:
1091
-
1092
- - alpha targets use `.release-candidate-promote.yml@vN-alpha`, runtime
1093
- `vN-alpha`, and `buildchain-alpha-contract-lock-path`;
1094
- - release and major targets use `.release-candidate-promote.yml@vN`, runtime
1095
- `vN`, and `buildchain-stable-contract-lock-path`.
1096
-
1097
- The generated router also owns the stable-shell layout transition through
1098
- `.buildchain/promotion-shell-routing.json`. The v3 stable and alpha lanes call
1099
- the hidden advanced workflow at the exact immutable SHA behind their selected
1100
- v3 channel state and forward the complete internal promotion identity surface.
1101
- The logical shell identity remains `vN`, and the router retains it in the public
1102
- audit outputs. The internal advanced-shell call receives the exact call ref
1103
- selected by the routing configuration, so its called-workflow ref check and
1104
- checkout SHA both bind to the same immutable identity. Updating a routing pin
1105
- after a release does not require any consumer declaration change.
1106
-
1107
- The router resolves immutable SHAs and the selected lock digest before candidate
1108
- download. The advanced shell verifies the same router, shell, runtime, lock,
1109
- channel, and target binding again. Train and exact-SHA runtime overrides remain
1110
- restricted to trusted `workflow_dispatch` actors with write, maintain, or admin
1111
- permission. Promotion controller evidence, the promotion copy of the release
1112
- candidate passport, and the final release passport record these identities.
1113
-
1114
- ```yaml
1115
- jobs:
1116
- promote:
1117
- uses: kungfu-systems/buildchain/.github/workflows/release-candidate-promote.yml@v3
1118
- secrets:
1119
- buildchain-issue-app-id: ${{ secrets.BUILDCHAIN_ISSUE_APP_ID }}
1120
- buildchain-issue-app-private-key: ${{ secrets.BUILDCHAIN_ISSUE_APP_PRIVATE_KEY }}
1121
- with:
1122
- buildchain-channel: auto
1123
- buildchain-alpha-contract-lock-path: .buildchain/alpha-contract-lock.json
1124
- buildchain-stable-contract-lock-path: .buildchain/contract-lock.json
1125
- channel: alpha
1126
- target-ref: alpha/v22/v22.22
1127
- artifact-name: libnode
1128
- # Defaults to build.yml / Build. Override only when the PR-stage build
1129
- # workflow uses a different file or display name.
1130
- release-candidate-workflow-file: build.yml
1131
- release-candidate-workflow-name: Build
1132
- package-manager: npm
1133
- publish-target: npm
1134
- runner-preset: github-hosted
1135
- trusted-publishing: true
1136
- github-release: true
1137
- required-status-check: check / check
1138
- required-artifact-count: 3
1139
- publish-dist-tag: alpha
1140
- publish-package-set-order: platforms-first-main-last
1141
- publish-package-main: "@kungfu-tech/libnode"
1142
- release-passport-product-name: Libnode
1143
- buildchain-contract-drift-issue-mode: compatible-and-breaking
1144
- ```
1145
-
1146
- Existing callers may keep `buildchain-contract-lock-path`; a non-empty explicit
1147
- path overrides channel-specific path selection only. Its lock content must
1148
- still prove the selected channel and major. Migration only requires adding the
1149
- two channel lock inputs and may retain the remaining common promotion
1150
- declaration unchanged. Consumers must not call the dot-prefixed advanced
1151
- workflow directly.
1152
-
1153
- `buildchain-issue-app-id` and `buildchain-issue-app-private-key` are optional
1154
- but recommended for cross-repository consumers. They should identify a GitHub
1155
- App installation with `issues: write` on `kungfu-systems/buildchain`; the
1156
- wrapper mints the installation token before calling
1157
- `actions/report-buildchain-issue`. Consumers can also pass a pre-minted
1158
- `buildchain-issue-token`. If both are omitted, the wrapper falls back to
1159
- `BUILDCHAIN_ISSUE_TOKEN`, `BUILDCHAIN_PROMOTION_TOKEN`, and then the consumer
1160
- workflow's `github.token`; the last fallback can only report issues when it has
1161
- write access to the target Buildchain repository.
1162
-
1163
- `publish-required-artifacts-json` can still be passed explicitly for custom
1164
- publish targets. Custom OCI requirements may omit pre-publish refs and digests;
1165
- the action resolves the exact version ref and validates final digests and any
1166
- built/reused provenance after `lifecycle.publish`. For the default
1167
- `publish-artifact-kind: npm` path, consumers do
1168
- not download artifacts or run repository scripts to build publish evidence. The
1169
- wrapper downloads the PR-stage payload artifacts, finds the downloaded `.tgz`
1170
- packages, reads each tarball's `package/package.json` for the real scoped
1171
- package name and version, computes the npm `sha512-...` integrity from the
1172
- tarball bytes, marks the package matching `publish-package-main` as `role:
1173
- main`, marks the rest as `role: platform`, and passes the generated
1174
- `publish-required-artifacts-json` to `promote-buildchain-ref` before any publish
1175
- side effect. Downloaded platform manifests are still passed into the release
1176
- passport unless `release-passport-platform-manifest-paths` is set explicitly.
1177
- The same Buildchain contract lock check runs before release-candidate
1178
- resolution and before publish. A compatible `v3` drift leaves an issue in the
1179
- consumer repository but does not trigger a second heavy build; an incompatible
1180
- drift fails before publish side effects.
1181
-
1182
- The wrapper publishes the public release tag as a GitHub Release by default.
1183
- After `promote-buildchain-ref` reports a complete release transaction, the
1184
- wrapper creates or updates the public release, marks semver prerelease tags
1185
- such as `v1.2.3-alpha.0`, `v1.2.3-rc.1`, or `v22.22.3-kf.3-alpha.7` as
1186
- `prerelease=true` and `make_latest=false`, marks stable semver tags as latest,
1187
- and uploads the publish evidence file plus every file in the generated release
1188
- passport directory, including `buildchain.release.json` and `check-report.json`.
1189
- For anchored/manual package releases, the public release tag is derived from the
1190
- published package version and the internal exact transaction tag remains
1191
- available in the release passport.
1192
- Consumers do not need to hand-write `gh release` logic to trigger
1193
- `release.published` propagation. Set `github-release: false` only for
1194
- repositories that intentionally do not maintain GitHub Releases.
1195
- If the transaction still needs protected-ref finalization, the wrapper defers
1196
- GitHub Release creation until the later run that reaches `state=complete`.
1197
-
1198
- Custom publish jobs can also repeat the channel-ref preflight:
1199
-
1200
- ```yaml
1201
- - name: Verify publish channel ref still matches
1202
- run: node .buildchain/runtime/scripts/verify-publish-channel-ref.mjs
1203
- env:
1204
- BUILDCHAIN_PUBLISH_SOURCE_REF: ${{ needs.build.outputs.publish-source-ref }}
1205
- BUILDCHAIN_PUBLISH_SOURCE_SHA: ${{ needs.build.outputs.publish-source-sha }}
1206
- BUILDCHAIN_SOURCE_REPOSITORY: ${{ github.repository }}
1207
- GITHUB_TOKEN: ${{ github.token }}
1208
- ```
1209
-
1210
- Anchored/manual package release jobs should also make the Buildchain promotion
1211
- action validate that publication is entering through the same
1212
- `publish-gate/{alpha,release,major}` source-lock contract before any package
1213
- publish side effect:
1214
-
1215
- ```yaml
1216
- - name: Promote release ref and publish npm package set
1217
- uses: kungfu-systems/buildchain/actions/promote-buildchain-ref@v3
1218
- with:
1219
- sha: ${{ needs.build.outputs.publish-source-sha }}
1220
- target-ref: release/v22/v22.22
1221
- require-publish-source-lock: "true"
1222
- publish-source-ref: ${{ needs.build.outputs.publish-source-ref }}
1223
- publish-source-sha: ${{ needs.build.outputs.publish-source-sha }}
1224
- publish-source-locked: ${{ needs.build.outputs.publish-source-locked }}
1225
- ```
1226
-
1227
- `target-ref` stays the Buildchain channel promotion target, such as
1228
- `alpha/v22/v22.22`, `release/v22/v22.22`, or `publish-gate/major`.
1229
- `publish-source-ref` is the reviewed source-lock branch that authorized this
1230
- specific package publication. For alpha and release package publications, the
1231
- source-lock branch must point at the exact channel-line commit that promotion is
1232
- validating; it is not a replacement for `target-ref`.
1233
-
1234
- This keeps the version bump commit, publish authorization, and auditable publish
1235
- entrypoint on the Buildchain source-lock protocol. The CLI form
1236
- `buildchain publish-source validate-anchored-release --json` is still useful for
1237
- custom publish scripts, but the preferred GitHub Actions gate is the promotion
1238
- action input above. A publish job that still runs directly from `alpha/*` or
1239
- `release/*` channel branches fails this check because those refs are channel
1240
- state, not publish-gate decisions.
1241
-
1242
- ## Package-Set Publish Plan
1243
-
1244
- Projects that publish multiple packages should treat package publication as a
1245
- package-set operation. Buildchain's package-set planner uses these rules:
1246
-
1247
- - platform packages publish first;
1248
- - the main package publishes last;
1249
- - the dist-tag move happens only after the full package set is present;
1250
- - reruns accept already-published packages only when package name, version, and
1251
- integrity match;
1252
- - an existing package with different integrity is a hard failure.
1253
-
1254
- This keeps a consumer from observing a floating dist-tag that points to a main
1255
- package before all platform artifacts for the same source SHA are available.
1256
-
1257
- ## Command Sources
1258
-
1259
- The workflow runs `.buildchain/buildchain.toml` lifecycle stages by default:
1260
-
1261
- ```toml
1262
66
  [lifecycle.install]
1263
- command = "corepack yarn install --immutable"
67
+ command = "corepack pnpm install --frozen-lockfile"
1264
68
 
1265
69
  [lifecycle.build]
1266
- commands = [
1267
- "corepack yarn make",
1268
- "corepack yarn build",
1269
- ]
70
+ command = "corepack pnpm build"
1270
71
 
1271
72
  [lifecycle.verify]
1272
- command = "corepack yarn test"
1273
- ```
1274
-
1275
- Callers can override any stage for one invocation:
1276
-
1277
- ```yaml
1278
- with:
1279
- build-command: cmake --build build --config Release
1280
- verify-command: ctest --test-dir build --output-on-failure
1281
- ```
1282
-
1283
- Every native and container matrix job is bounded by
1284
- `lifecycle-timeout-minutes`, which defaults to 120 minutes. The same input is
1285
- the fallback deadline for each install, build, and verify action, so a hung
1286
- command fails with the lifecycle name and matrix platform before it can occupy
1287
- a self-hosted runner indefinitely. A stage-level `timeout_minutes` in
1288
- `buildchain.toml` remains the more specific override for that stage.
1289
-
1290
- ```yaml
1291
- with:
1292
- lifecycle-timeout-minutes: 90
1293
- ```
1294
-
1295
- The reusable build workflow samples the build lifecycle by default and carries
1296
- the generated summary into the final verify diagnostics. Callers can override
1297
- the sidecar path or disable sampling:
1298
-
1299
- ```yaml
1300
- with:
1301
- sample-process-tree: true
1302
- process-summary-path: .buildchain/diagnostics/process-summary.json
1303
- process-sample-interval-ms: 15000
1304
- requested-parallelism: 20
1305
- ```
1306
-
1307
- When `sample-process-tree` is true, Buildchain wraps either `build-command` or
1308
- the configured `lifecycle.build` stage with `buildchain sample process-tree`.
1309
- The path is relative to the checked-out workspace and is read again during the
1310
- final verify lifecycle. Custom workflows can still write their own sampler
1311
- summary and pass `process-summary-path`; Buildchain reads the file after the
1312
- lifecycle command finishes, so it may be produced during the same invocation.
1313
- When the build stage is optional, the reusable workflow treats the default
1314
- sampler path as optional during verify; an explicitly supplied
1315
- `process-summary-path` remains required.
1316
-
1317
- For custom workflows, use the action directly:
1318
-
1319
- ```yaml
1320
- - uses: kungfu-systems/buildchain/actions/run-lifecycle@v3
1321
- with:
1322
- stage: build
1323
- required: "true"
1324
- timeout-minutes: "90"
1325
- artifact-name: libnode-linux-x64-${{ github.sha }}
1326
- artifact-paths: |
1327
- dist
1328
- build/stage
1329
- ```
1330
-
1331
- ## Artifact Contract
1332
-
1333
- Each platform upload uses `artifact-name-template`. The default is:
1334
-
1335
- ```text
1336
- {artifact}-{platform}-{sha}
1337
- ```
1338
-
1339
- Supported placeholders are `{artifact}`, `{artifactName}`, `{platform}`,
1340
- `{platformId}`, `{platformName}`, `{sha}`, `{shortSha}`, `{ref}`, `{runId}`,
1341
- and `{runAttempt}`. Invalid GitHub artifact name characters are normalized to
1342
- `-`, so `{ref}` remains deterministic even for refs such as
1343
- `refs/heads/dev/v3/v3.0`.
1344
-
1345
- Each platform also writes and uploads:
1346
-
1347
- ```text
1348
- .buildchain/artifacts/<platform-id>/manifest.json
1349
- .buildchain/artifacts/<platform-id>/summary.json
1350
- ```
1351
-
1352
- The manifest schema is:
1353
-
1354
- ```json
1355
- {
1356
- "schemaVersion": 1,
1357
- "contract": "kungfu-buildchain-artifact",
1358
- "artifactName": "libnode-linux-x64-<sha>",
1359
- "platform": {
1360
- "id": "linux-x64",
1361
- "name": "Linux x64",
1362
- "os": "Linux",
1363
- "arch": "X64"
1364
- },
1365
- "git": {
1366
- "repository": "kungfu-systems/libnode",
1367
- "sha": "<sha>",
1368
- "ref": "<ref>",
1369
- "runId": "<run id>",
1370
- "runAttempt": "<attempt>"
1371
- },
1372
- "lifecycle": {
1373
- "stage": "verify",
1374
- "commandSource": "buildchain.toml",
1375
- "executed": true
1376
- },
1377
- "summary": {
1378
- "contract": "kungfu-buildchain-artifact-summary",
1379
- "artifactName": "libnode-linux-x64-<sha>",
1380
- "fileCount": 1,
1381
- "totalBytes": 1234,
1382
- "digest": "<hex>"
1383
- },
1384
- "expectedArtifacts": {
1385
- "ok": true,
1386
- "source": "expected-artifacts-json",
1387
- "checks": []
1388
- },
1389
- "files": [
1390
- {
1391
- "path": "dist/example.zip",
1392
- "size": 1234,
1393
- "sha256": "<hex>"
1394
- }
1395
- ]
1396
- }
1397
- ```
1398
-
1399
- Artifact names do not include actor names, timestamps, or retry counters. Reruns
1400
- produce a new GitHub Actions run but keep the same source SHA/platform contract.
1401
-
1402
- `expected-artifacts-json` fails the build before upload when the artifact does
1403
- not match the caller's declared contract. Supported checks are:
1404
-
1405
- | Field | Meaning |
1406
- | --------------- | ------------------------------------ |
1407
- | `minFiles` | Minimum number of manifest files |
1408
- | `maxFiles` | Maximum number of manifest files |
1409
- | `minTotalBytes` | Minimum total byte count |
1410
- | `requiredPaths` | Exact manifest paths that must exist |
1411
-
1412
- ## Trusted Event Gate
1413
-
1414
- The workflow has an explicit `trust-gate` job. By default, pull requests from
1415
- forks fail before any build job can reach self-hosted runners, secrets,
1416
- publishing credentials, or heavyweight build commands. Same-repository PRs,
1417
- workflow dispatches, and protected branch events can proceed.
1418
-
1419
- If a repository wants fork PRs to skip rather than fail, it can set:
1420
-
1421
- ```yaml
1422
- with:
1423
- untrusted-policy: skip
1424
- ```
1425
-
1426
- Do not set `require-trusted-event: false` for workflows that use self-hosted
1427
- runners or secrets.
1428
-
1429
- The build matrix and the workflow control plane are routed independently. The
1430
- matrix continues to use `runner-preset` and `platforms-json`. Consumers with a
1431
- governed runner may also move channel resolution, trust evaluation, contract
1432
- resolution, controller evidence, artifact transfer, and aggregation off the
1433
- default GitHub-hosted runner:
1434
-
1435
- ```yaml
1436
- with:
1437
- control-runner-json: '["self-hosted","agent-120"]'
1438
- runner-preset: custom
1439
- platforms-json: '[{"id":"linux-x64","name":"Linux x64","runner":"[\"self-hosted\",\"agent-120\"]"}]'
1440
- ```
1441
-
1442
- `control-runner-json` is additive and defaults to `["ubuntu-24.04"]`. Keep
1443
- `require-trusted-event: true` whenever either runner input selects
1444
- `self-hosted`; a self-hosted control plane must not be exposed to untrusted fork
1445
- events or arbitrary caller-controlled workflow code.
1446
-
1447
- Consumers that must verify platform-native properties of the final bytes can
1448
- set `artifact-finalization-command` and `artifact-finalization-on-platform:
1449
- true`. Buildchain then imports any signed result (or preserves the declared
1450
- unsigned artifact), runs the command on the matching GitHub-hosted platform,
1451
- and reseals the manifest before publishing the final deterministic artifact.
1452
- Platform-native finalization fails closed for self-hosted runners so signing
1453
- authority credentials and final bytes stay inside the trusted hosted boundary.
1454
-
1455
- `require-trusted-event` controls access to build runners. It does not override
1456
- the publish gate: pull requests remain non-publishing events.
1457
-
1458
- ## Fixture
1459
-
1460
- `fixtures/libnode-shaped` is the contract fixture. It has:
1461
-
1462
- - `package.json` version state;
1463
- - `.buildchain/buildchain.toml` with `install`, `build`, `verify`, and `publish`;
1464
- - cross-platform Node scripts that create small `dist/` outputs;
1465
- - `Build Surface Fixture` workflow coverage.
1466
-
1467
- The fixture proves the reusable surface without running the real libnode native
1468
- build.
73
+ command = "corepack pnpm test"
74
+
75
+ [build]
76
+ environment = "github-hosted"
77
+ timeout_minutes = 120
78
+ fail_fast = false
79
+
80
+ [build.tools]
81
+ node = "24"
82
+ rust = "stable"
83
+
84
+ [build.artifacts]
85
+ name = "my-library"
86
+ paths = ["dist"]
87
+ required_paths = ["dist/library.js"]
88
+ min_files = 1
89
+ release_candidate = true
90
+ retention_days = 14
91
+ compression_level = 0
92
+ ```
93
+
94
+ Commands remain exclusively in `[lifecycle]`. The backbone explicitly invokes
95
+ install, build and verify in that order on each platform. Build is required;
96
+ install and verify are required when declared. Unneeded stages require no
97
+ additional boolean setting. Per-stage lifecycle timeout declarations retain
98
+ precedence over the build timeout fallback.
99
+
100
+ Artifact paths and required file paths are relative to the selected project.
101
+ Platform artifact names derive from the configured base name,
102
+ platform and source SHA; callers do not supply naming templates. The final
103
+ manifest checks `min_files`, `max_files`, `min_total_bytes` and `required_paths`.
104
+
105
+ Other optional project sections are:
106
+
107
+ | TOML section | Responsibility |
108
+ | --- | --- |
109
+ | `build.diagnostics` | Process sampling and expected parallelism |
110
+ | `build.verification` | Consumer-produced verify substage evidence location |
111
+ | `build.finalization` | Verification of final signed bytes and platform placement |
112
+ | `build.transport_smoke` | Transport simulation scenario and artifact root |
113
+ | `build.attestation` | Subject path and attested platform |
114
+ | `build.macos_signing` | App path and sealed-input platform |
115
+ | `build.contract` | Compatibility and drift reporting policies |
116
+ | `build.evidence` | Repository files containing gate and candidate-family evidence |
117
+
118
+ Declarative signing targets continue to belong to `[signing.artifacts]`.
119
+ The formal signing authority remains `authority/v3/v3.0/artifact-signing`
120
+ with the protected `buildchain-artifact-signing` environment.
121
+ Credentials, authority profiles and signer identities cannot be supplied through
122
+ that declaration. See [Release Candidate](release-candidate.md)
123
+ and [GitHub Artifact Attestation](github-artifact-attestation.md).
124
+
125
+ ## Governed environments
126
+
127
+ `build.environment` selects a profile in
128
+ [`architecture/build-environments.json`](../architecture/build-environments.json)
129
+ from the exact Buildchain runtime. Infrastructure owners maintain runner
130
+ presets/platforms, Linux container images, mirrors, cache transport, artifact
131
+ relay settings and credential-island environments there. Adding a runner label
132
+ or provider role requires reviewing that profile; project TOML cannot redefine
133
+ it. Secrets still cross explicit workflow secret/environment boundaries.
134
+
135
+ The initial profiles are `github-hosted`, `github-hosted-container`,
136
+ `kungfu-v4-native` and `kungfu-v4-self-hosted`. The container profile uses the
137
+ existing digest-pinned `kungfu-verify` image. Native and container jobs remain
138
+ separate because GitHub selects `runs-on` and `container` before executing
139
+ steps. Their shared behavior is owned by these composite actions:
140
+
141
+ | Action | Owner responsibility |
142
+ | --- | --- |
143
+ | `build-lifecycle-stage` | Lifecycle manifests, sampling and diagnostics |
144
+ | `build-signing-request` | Unsigned requests and detached signing control inputs |
145
+ | `build-verification-evidence` | Independent substage verification and failure evidence |
146
+ | `build-attestation-policy` | Source-bound artifact attestation policy |
147
+ | `build-agent-hub-evidence` | Agent Hub conformance evidence |
148
+ | `build-artifact-transfer` | GitHub and S3 transport plus payload manifests |
149
+
150
+ The backbone still owns install → build → verify order, job dependencies,
151
+ permissions, signing control, credential-island placement and final aggregation.
152
+ A composite's internal receipt inputs transport already resolved facts; they
153
+ are not additional reusable-workflow configuration inputs.
154
+
155
+ ## Identity and admission
156
+
157
+ The configuration resolver reads the exact consumer source without executing
158
+ consumer commands. It emits a deterministic plan/root binding configuration
159
+ bytes, source SHA, called-workflow SHA, selected environment, toolchain and
160
+ cache roots. Trust checks run before project execution or privileged runners.
161
+
162
+ Tracked consumer workflows use `@v4` or `@v4-alpha` and commit both
163
+ `.buildchain/contract-lock.json` and `.buildchain/alpha-contract-lock.json`.
164
+ The called channel selects its matching lock. Source-persisted train/SHA
165
+ selectors and mismatched locks remain forbidden. Ordinary builds have no
166
+ runtime-override path; specialized release/recovery entry points keep their
167
+ separate, bounded admission contracts.
168
+
169
+ Bootstrap owns its admitted release and recovery capabilities. It is not an
170
+ alternate parameter envelope for ordinary builds and is not injected into the
171
+ build backbone. Release promotion consumes an already sealed candidate and
172
+ must verify the source, artifacts and receipts independently.
173
+
174
+ ## Evidence and failure behavior
175
+
176
+ The workflow retains runtime/source identity, build and diagnostics summaries,
177
+ Release Candidate Passport, signing/finalization and controller receipt outputs.
178
+ The public facade forwards those outputs from the single backbone execution.
179
+ Platform manifests include deterministic payload hashes; transport preserves
180
+ hidden artifacts and provenance. Substage failure evidence is collected even
181
+ when verify fails. Untrusted events cannot reach build runners.
182
+
183
+ Signing authority and credential-island jobs remain separate from ordinary
184
+ build execution. Imported signed bytes undergo consumer verification before
185
+ manifests are recomputed. Release builds produce candidates; registry publishing
186
+ belongs to the protected release path.
187
+
188
+ ## Maintaining the contract
189
+
190
+ Update the TOML parser and plan resolver, owner contracts, affected consumers
191
+ and tests together. Run `node scripts/generate-channel-build-workflow.mjs`,
192
+ `pnpm run generate:workflows`, generated reference/site checks and the full
193
+ `pnpm run check`. Breaking input removal is intentional: migrate old `with`
194
+ settings to TOML and remove them from calls; no compatibility forwarding exists.
195
+
196
+ The Buildchain fixture at `fixtures/libnode-shaped/buildchain.toml` exercises
197
+ native macOS/Windows and Linux-container builds through this public contract.