@kungfu-tech/buildchain 3.0.9-alpha.9 → 4.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/AGENTS.md +39 -0
  2. package/README.md +17 -13
  3. package/actions/promote-buildchain-ref/README.md +0 -10
  4. package/actions/release-tail/README.md +6 -10
  5. package/architecture/internal-capabilities.json +502 -0
  6. package/architecture/maintainability-baseline.json +59 -0
  7. package/architecture/maintainability-policy.json +1384 -0
  8. package/architecture/release-tail-contract-inventory.json +587 -0
  9. package/architecture/v3-core-mechanism-inventory.json +1043 -0
  10. package/architecture/v3-core-mechanism-inventory.md +67 -0
  11. package/architecture/v4-adopter-delivery-parity.json +47 -0
  12. package/architecture/v4-architecture-constitution.md +210 -0
  13. package/architecture/v4-bootstrap-authority.json +56 -0
  14. package/architecture/v4-canonical-contract-fixtures.json +105 -0
  15. package/architecture/v4-capability-state-machine-manifest.json +480 -0
  16. package/architecture/v4-capability-state-machine-manifest.schema.json +202 -0
  17. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.json +830 -0
  18. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.md +223 -0
  19. package/architecture/v4-delivery-warrant-shadow-bootstrap-plan.schema.json +318 -0
  20. package/architecture/v4-delivery-warrant-shadow-fixtures.json +227 -0
  21. package/architecture/v4-delivery-warrant-shadow-fixtures.schema.json +67 -0
  22. package/architecture/v4-exception-ledger.json +5 -0
  23. package/architecture/v4-exception-ledger.schema.json +56 -0
  24. package/architecture/v4-partial-mutation-recovery-qualification.json +65 -0
  25. package/architecture/v4-platform-stage-checkpoints.json +97 -0
  26. package/architecture/v4-provider-operation-journal-contract.json +113 -0
  27. package/architecture/v4-release-activation-shadow-domain.json +61 -0
  28. package/architecture/v4-release-train-parity.json +68 -0
  29. package/architecture/v4-rust-libnode-bridge-evaluation.json +107 -0
  30. package/architecture/v4-rust-libnode-bridge-spike.md +85 -0
  31. package/architecture/v4-stable-publication-fence.json +66 -0
  32. package/architecture/v4-stage-capsule-contract.json +90 -0
  33. package/architecture/v4-stage-capsule-qualification.json +99 -0
  34. package/architecture/v4-stage-capsule-resume-planner.json +52 -0
  35. package/architecture/v4-stage-capsule-store-contract.json +72 -0
  36. package/bin/buildchain.mjs +15 -10
  37. package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
  38. package/contracts/buildchain-v2-residuals-v1.json +2 -2
  39. package/contracts/fixtures/v4-delivery-warrant-trace-v1/golden.json +158 -0
  40. package/contracts/fixtures/v4-delivery-warrant-trace-v1/property.json +380 -0
  41. package/contracts/fixtures/v4-delivery-warrant-trace-v1/replay.json +163 -0
  42. package/contracts/fixtures/v4-partial-mutation-recovery-v1/shared.json +155 -0
  43. package/contracts/fixtures/v4-provider-operation-journal-v1/shared.json +457 -0
  44. package/contracts/fixtures/v4-provider-readback-idempotency-v1/shared.json +682 -0
  45. package/contracts/fixtures/v4-release-activation-shadow-v1/shared.json +53 -0
  46. package/contracts/fixtures/v4-stable-publication-fence-v1/shared.json +56 -0
  47. package/contracts/fixtures/v4-stage-capsule-resume-v1/late-platform-failure.json +176 -0
  48. package/contracts/fixtures/v4-stage-capsule-store-v1/shared.json +132 -0
  49. package/contracts/fixtures/v4-stage-capsule-v1/shared.json +111 -0
  50. package/contracts/v4-canonical-contracts-v1.schema.json +182 -0
  51. package/contracts/v4-delivery-warrant-semantic-diff-report-v1.schema.json +284 -0
  52. package/contracts/v4-delivery-warrant-trace-v1.schema.json +215 -0
  53. package/contracts/v4-host-contract-v1.schema.json +135 -0
  54. package/contracts/v4-partial-mutation-recovery-v1.schema.json +168 -0
  55. package/contracts/v4-provider-operation-journal-v1.schema.json +381 -0
  56. package/contracts/v4-release-activation-shadow-v1.schema.json +186 -0
  57. package/contracts/v4-stable-publication-fence-v1.schema.json +125 -0
  58. package/contracts/v4-stage-capsule-resume-v1.schema.json +225 -0
  59. package/contracts/v4-stage-capsule-store-v1.schema.json +154 -0
  60. package/contracts/v4-stage-capsule-v1.schema.json +178 -0
  61. package/crates/buildchain-v4-bridge/Cargo.lock +213 -0
  62. package/crates/buildchain-v4-bridge/Cargo.toml +12 -0
  63. package/crates/buildchain-v4-bridge/src/main.rs +495 -0
  64. package/dist/site/agent-index.json +0 -4
  65. package/dist/site/artifact-schemas.json +0 -8
  66. package/dist/site/buildchain-contract.json +103 -397
  67. package/dist/site/buildchain-site.json +301 -388
  68. package/dist/site/capability-registry.json +9 -11
  69. package/dist/site/cli-registry.json +8 -53
  70. package/dist/site/controller-registry.json +48 -8
  71. package/dist/site/kfd-claims.json +129 -167
  72. package/dist/site/kfd-upstream-aggregate.json +9 -9
  73. package/dist/site/manual-registry.json +18 -49
  74. package/dist/site/node-api-registry.json +4345 -7345
  75. package/dist/site/page-registry.json +278 -346
  76. package/dist/site/public-surface-audit.json +85 -404
  77. package/dist/site/publication-authority-registry.json +11 -1
  78. package/dist/site/publication-registry.json +3 -6
  79. package/dist/site/release-provenance.json +3 -9
  80. package/dist/site/schemas/kfd-support-projection-v1.schema.json +106 -0
  81. package/dist/site/site-manifest.json +21 -40
  82. package/dist/site/workflow-registry.json +78 -35
  83. package/docs/MAP.md +8 -5
  84. package/docs/auditable-demo.md +2 -2
  85. package/docs/aws-us-elastic-runner-burst-plane.md +7 -59
  86. package/docs/cli-reference.md +15 -213
  87. package/docs/cli.md +14 -14
  88. package/docs/dev-alpha-candidate-patrol.md +3 -11
  89. package/docs/dev-delivery-warrant.md +29 -194
  90. package/docs/engineering-housekeeper.md +12 -4
  91. package/docs/getting-started.md +1 -1
  92. package/docs/install.md +5 -5
  93. package/docs/kfd-support.md +2 -7
  94. package/docs/lifecycle-protocol.md +2 -4
  95. package/docs/node-api-reference.md +414 -554
  96. package/docs/release-flow.md +31 -31
  97. package/docs/release-governance.md +16 -28
  98. package/docs/release-passport.md +2 -15
  99. package/docs/release-tail-contract.md +13 -6
  100. package/docs/release-tail-provider-plane.md +13 -19
  101. package/docs/release-train.md +4 -4
  102. package/docs/reusable-build-surface.md +14 -27
  103. package/docs/site-bundle-contract.md +1 -5
  104. package/docs/stable-candidate-patrol.md +7 -7
  105. package/docs/v4-canonical-contracts.md +92 -0
  106. package/docs/v4-delivery-warrant-read-candidate.md +73 -0
  107. package/docs/v4-delivery-warrant-semantic-diff.md +75 -0
  108. package/docs/v4-production-release.md +63 -0
  109. package/docs/v4-stage-capsule.md +193 -0
  110. package/docs/versioning.md +3 -5
  111. package/package.json +19 -19
  112. package/packages/core/adopter-delivery-gate.js +576 -0
  113. package/packages/core/adopter-delivery-json.js +66 -0
  114. package/packages/core/artifact-signing.js +0 -61
  115. package/packages/core/artifact-verification-envelope.js +5 -5
  116. package/packages/core/build-facts.js +12 -4
  117. package/packages/core/buildchain-agent-manuals.js +0 -2
  118. package/packages/core/buildchain-channel-identity.js +3 -2
  119. package/packages/core/buildchain-config.js +4 -69
  120. package/packages/core/buildchain-contract.js +232 -46
  121. package/packages/core/buildchain-kfd-claims.js +1 -1
  122. package/packages/core/channel-candidate.js +0 -8
  123. package/packages/core/channel-promotion-baseline.js +0 -52
  124. package/packages/core/controller-evidence.js +3 -4
  125. package/packages/core/dev-alpha-candidate-selection.js +2 -10
  126. package/packages/core/dev-delivery-warrant-cancellation.js +0 -1
  127. package/packages/core/dev-delivery-warrant-settlement.js +20 -67
  128. package/packages/core/dev-delivery-warrant.js +50 -138
  129. package/packages/core/diagnostics.js +3 -8
  130. package/packages/core/github-governance-authority.js +3 -1
  131. package/packages/core/kfd-adopter-category-driver.js +167 -0
  132. package/packages/core/kfd-adopter-manifest.js +47 -46
  133. package/packages/core/kfd-gate.js +15 -45
  134. package/packages/core/paper-agent-entry.js +7 -16
  135. package/packages/core/paper-fleet.js +2 -2
  136. package/packages/core/paper-repository.js +2 -3
  137. package/packages/core/paper-scaffold-content.js +2 -23
  138. package/packages/core/paper.js +11 -43
  139. package/packages/core/publication-reproducibility.js +4 -4
  140. package/packages/core/release-line-bootstrap.js +21 -8
  141. package/packages/core/release-passport-contract.js +5 -5
  142. package/packages/core/release-passport.js +20 -131
  143. package/packages/core/self-dogfood-version.js +48 -35
  144. package/packages/core/spawn-command.js +232 -8
  145. package/packages/core/v4-adopter-delivery-parity.js +219 -0
  146. package/packages/core/v4-canonical-contracts.js +284 -0
  147. package/packages/core/v4-delivery-warrant-fixture-runner.js +371 -0
  148. package/packages/core/v4-delivery-warrant-read-candidate.js +279 -0
  149. package/packages/core/v4-delivery-warrant-semantic-diff-gate.js +352 -0
  150. package/packages/core/v4-delivery-warrant-shadow-adapter.js +447 -0
  151. package/packages/core/v4-partial-mutation-recovery-qualification.js +395 -0
  152. package/packages/core/v4-platform-stage-checkpoints.js +471 -0
  153. package/packages/core/v4-provider-operation-journal.js +531 -0
  154. package/packages/core/v4-provider-readback-idempotency.js +394 -0
  155. package/packages/core/v4-release-activation-shadow.js +519 -0
  156. package/packages/core/v4-stable-publication-fence.js +357 -0
  157. package/packages/core/v4-stage-capsule-local-store.js +426 -0
  158. package/packages/core/v4-stage-capsule-qualification-campaign.js +512 -0
  159. package/packages/core/v4-stage-capsule-qualification.js +456 -0
  160. package/packages/core/v4-stage-capsule-resume-planner.js +397 -0
  161. package/packages/core/v4-stage-capsule-store.js +401 -0
  162. package/packages/core/v4-stage-capsule.js +319 -0
  163. package/scripts/assemble-self-publication-admission.mjs +1 -1
  164. package/scripts/audit-publication-control-plane.mjs +5 -5
  165. package/scripts/auditable-demo-bundle-verification.mjs +3 -2
  166. package/scripts/auditable-demo-platform.mjs +2 -2
  167. package/scripts/auditable-demo-renditions.mjs +1 -1
  168. package/scripts/auditable-demo.mjs +2 -2
  169. package/scripts/aws-macos-jit-controller-core.mjs +13 -346
  170. package/scripts/aws-macos-jit-controller-runtime.mjs +2 -210
  171. package/scripts/aws-macos-jit-controller.mjs +37 -275
  172. package/scripts/aws-macos-jit-core.mjs +3 -41
  173. package/scripts/aws-macos-jit.mjs +0 -12
  174. package/scripts/aws-windows-jit-campaign.mjs +2 -2
  175. package/scripts/aws-windows-jit-controller-core.mjs +1 -1
  176. package/scripts/aws-windows-jit-controller.mjs +3 -3
  177. package/scripts/build-contract-core.mjs +3 -8
  178. package/scripts/build-standalone-binary.mjs +3 -14
  179. package/scripts/buildchain-channel-router.mjs +4 -0
  180. package/scripts/buildchain-cli-help.mjs +7 -16
  181. package/scripts/buildchain-contract-lock.mjs +0 -6
  182. package/scripts/check-action-bundles.mjs +3 -4
  183. package/scripts/check-internal-architecture.mjs +7 -6
  184. package/scripts/check-inventory.mjs +30 -25
  185. package/scripts/check-v4-public-dogfood-contract.mjs +270 -0
  186. package/scripts/create-release-bundle.mjs +8 -4
  187. package/scripts/dev-alpha-candidate-patrol.mjs +1 -22
  188. package/scripts/dev-delivery-proof.mjs +2 -64
  189. package/scripts/dev-delivery-warrant.mjs +61 -86
  190. package/scripts/dev-pr-auto-merge.mjs +4 -30
  191. package/scripts/dev-pr-delivery-warrant.mjs +0 -55
  192. package/scripts/dispatch-artifact-signing-authority.mjs +7 -4
  193. package/scripts/generate-buildchain-kfd-witnesses.mjs +94 -88
  194. package/scripts/generate-channel-build-workflow.mjs +54 -24
  195. package/scripts/generate-channel-promotion-workflow.mjs +15 -6
  196. package/scripts/generate-site-bundle.mjs +8 -27
  197. package/scripts/git-fetch-process-tree.mjs +34 -1
  198. package/scripts/init-repo.mjs +64 -159
  199. package/scripts/inspect-artifact-signing-requests.mjs +0 -6
  200. package/scripts/locked-source-checkout.mjs +3 -4
  201. package/scripts/maintainability-metrics.mjs +8 -2
  202. package/scripts/npm-publish-dry-run.mjs +2 -2
  203. package/scripts/npm-publish-transaction.mjs +2 -2
  204. package/scripts/publication-commit-evidence.mjs +23 -69
  205. package/scripts/release-candidate-resolver.mjs +11 -17
  206. package/scripts/release-tail.mjs +3 -49
  207. package/scripts/resume-from-candidate-run.mjs +9 -123
  208. package/scripts/run-lifecycle-core.mjs +12 -13
  209. package/scripts/seal-artifact-signing-requests.mjs +17 -21
  210. package/scripts/site-capability-metadata.mjs +3 -20
  211. package/scripts/stable-candidate-qualification.mjs +10 -10
  212. package/scripts/v4-architecture.mjs +12 -2
  213. package/scripts/v4-bridge-bootstrap.mjs +141 -0
  214. package/scripts/v4-bridge-evidence.mjs +141 -0
  215. package/scripts/v4-host-adapter.mjs +182 -0
  216. package/scripts/v4-platform-stage-checkpoint-rehearsal.mjs +207 -0
  217. package/scripts/v4-stage-capsule-qualification.mjs +528 -0
  218. package/scripts/v4-stage-capsule-resume-rehearsal.mjs +57 -0
  219. package/scripts/v4-warrant-shadow-plan.mjs +601 -0
  220. package/scripts/verify-golden-path.mjs +5 -5
  221. package/scripts/web-surface-cloudfront-rewrite.mjs +15 -15
  222. package/scripts/web-surface-core.mjs +2 -8
  223. package/scripts/workflow-call-contract.mjs +5 -184
  224. package/contracts/dev-delivery-authority-v2.schema.json +0 -253
  225. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-perspective.json +0 -16
  226. package/contracts/fixtures/kfd-adopter-release-v1/kfd-4-replay.json +0 -57
  227. package/contracts/fixtures/next-development-transition-v1/anchored-manual-waiting.json +0 -47
  228. package/contracts/fixtures/next-development-transition-v1/semver-auto-planned.json +0 -47
  229. package/contracts/fixtures/next-development-transition-v1/version-model-cases.json +0 -40
  230. package/contracts/next-development-request-v1.schema.json +0 -66
  231. package/contracts/next-development-transition-v1.schema.json +0 -292
  232. package/contracts/publication-rehearsal-capsule-v1.schema.json +0 -173
  233. package/dist/site/schemas/dev-delivery-authority-v2.schema.json +0 -442
  234. package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +0 -269
  235. package/dist/site/schemas/release-tail-capabilities-v1.schema.json +0 -353
  236. package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +0 -94
  237. package/docs/dev-delivery-qualification-landing-adr.md +0 -137
  238. package/docs/next-development-transition.md +0 -119
  239. package/docs/publication-rehearsal.md +0 -94
  240. package/packages/core/buildchain-compatibility-proof.js +0 -631
  241. package/packages/core/dev-delivery-authority.js +0 -996
  242. package/packages/core/dev-delivery-native-proof.js +0 -418
  243. package/packages/core/dev-delivery-warrant-shadow.js +0 -502
  244. package/packages/core/next-development-candidate-reservation.js +0 -186
  245. package/packages/core/next-development-controller.js +0 -726
  246. package/packages/core/next-development-projection.js +0 -288
  247. package/packages/core/next-development-transition.js +0 -738
  248. package/packages/core/publication-rehearsal-projection.js +0 -173
  249. package/packages/core/publication-rehearsal-runtime.js +0 -921
  250. package/scripts/aws-macos-jit-instance-rehydrate.mjs +0 -260
  251. package/scripts/aws-macos-jit-source-rebind.mjs +0 -724
  252. package/scripts/dev-delivery-authority.mjs +0 -368
  253. package/scripts/dev-delivery-native-run.mjs +0 -235
  254. package/scripts/dev-delivery-two-phase-resume.mjs +0 -90
  255. package/scripts/dev-delivery-two-phase.mjs +0 -577
  256. package/scripts/generate-next-development-guidance.mjs +0 -49
  257. package/scripts/materialize-self-release-candidate-version.mjs +0 -137
  258. package/scripts/next-development-self-dogfood-harness.mjs +0 -409
  259. package/scripts/next-development-self-dogfood.mjs +0 -532
  260. package/scripts/next-development-transition.mjs +0 -47
  261. package/scripts/release-candidate-tail-reseal.mjs +0 -426
  262. package/templates/native-dev-delivery.yml +0 -86
@@ -0,0 +1,495 @@
1
+ use std::env;
2
+ use std::io::{self, Read, Write};
3
+ use std::process::{Command, Stdio};
4
+ use std::sync::Arc;
5
+ use std::sync::atomic::{AtomicBool, Ordering};
6
+ use std::thread;
7
+ use std::time::{Duration, Instant};
8
+
9
+ use base64::Engine;
10
+ use base64::engine::general_purpose::STANDARD as BASE64;
11
+ use serde::{Deserialize, Serialize};
12
+ use serde_json::Value;
13
+
14
+ const REQUEST_CONTRACT: &str = "kungfu-buildchain-v4-host-request";
15
+ const RESPONSE_CONTRACT: &str = "kungfu-buildchain-v4-host-response";
16
+ const PROTOCOL_VERSION: &str = "1.0";
17
+ const MAX_REQUEST_BYTES: u64 = 4 * 1024 * 1024;
18
+ const MAX_RESPONSE_BYTES: u64 = 8 * 1024 * 1024;
19
+ const SOFTWARE_EXIT: i32 = 70;
20
+ const DATA_EXIT: i32 = 65;
21
+
22
+ #[derive(Debug, Deserialize, Serialize, Clone)]
23
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
24
+ struct EncodedBytes {
25
+ encoding: String,
26
+ bytes: String,
27
+ }
28
+
29
+ impl EncodedBytes {
30
+ fn empty() -> Self {
31
+ Self {
32
+ encoding: "base64".to_owned(),
33
+ bytes: String::new(),
34
+ }
35
+ }
36
+
37
+ fn from_bytes(value: &[u8]) -> Self {
38
+ Self {
39
+ encoding: "base64".to_owned(),
40
+ bytes: BASE64.encode(value),
41
+ }
42
+ }
43
+
44
+ fn decode(&self) -> Result<Vec<u8>, String> {
45
+ if self.encoding != "base64" {
46
+ return Err(format!("unsupported byte encoding: {}", self.encoding));
47
+ }
48
+ BASE64
49
+ .decode(&self.bytes)
50
+ .map_err(|error| format!("invalid base64 bytes: {error}"))
51
+ }
52
+ }
53
+
54
+ #[derive(Debug, Deserialize, Serialize, Clone)]
55
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
56
+ struct HostCommand {
57
+ id: String,
58
+ arguments: Vec<String>,
59
+ }
60
+
61
+ #[derive(Debug, Deserialize, Serialize)]
62
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
63
+ struct HostRequest {
64
+ schema_version: u8,
65
+ contract: String,
66
+ protocol_version: String,
67
+ request_id: String,
68
+ command: HostCommand,
69
+ input: EncodedBytes,
70
+ required_capabilities: Vec<String>,
71
+ timeout_ms: u64,
72
+ }
73
+
74
+ #[derive(Debug, Deserialize, Serialize)]
75
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
76
+ struct HostIdentity {
77
+ kind: String,
78
+ implementation: String,
79
+ capabilities: Vec<String>,
80
+ }
81
+
82
+ #[derive(Debug, Deserialize, Serialize)]
83
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
84
+ struct HostOutput {
85
+ stdout: EncodedBytes,
86
+ stderr: EncodedBytes,
87
+ }
88
+
89
+ #[derive(Debug, Deserialize, Serialize)]
90
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
91
+ struct Diagnostic {
92
+ code: String,
93
+ message: String,
94
+ retryable: bool,
95
+ }
96
+
97
+ #[derive(Debug, Deserialize, Serialize)]
98
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
99
+ struct ExitSemantics {
100
+ code: u8,
101
+ signal: Option<String>,
102
+ }
103
+
104
+ #[derive(Debug, Deserialize, Serialize)]
105
+ #[serde(rename_all = "camelCase", deny_unknown_fields)]
106
+ struct HostResponse {
107
+ schema_version: u8,
108
+ contract: String,
109
+ protocol_version: String,
110
+ request_id: String,
111
+ status: String,
112
+ host: HostIdentity,
113
+ command: HostCommand,
114
+ output: HostOutput,
115
+ structured_result: Value,
116
+ diagnostics: Vec<Diagnostic>,
117
+ exit: ExitSemantics,
118
+ }
119
+
120
+ impl HostResponse {
121
+ fn bridge_failure(request: &HostRequest, code: &str, message: String, exit: u8) -> Self {
122
+ Self {
123
+ schema_version: 1,
124
+ contract: RESPONSE_CONTRACT.to_owned(),
125
+ protocol_version: PROTOCOL_VERSION.to_owned(),
126
+ request_id: request.request_id.clone(),
127
+ status: "failed".to_owned(),
128
+ host: HostIdentity {
129
+ kind: "node-subprocess".to_owned(),
130
+ implementation: "unavailable".to_owned(),
131
+ capabilities: Vec::new(),
132
+ },
133
+ command: request.command.clone(),
134
+ output: HostOutput {
135
+ stdout: EncodedBytes::empty(),
136
+ stderr: EncodedBytes::from_bytes(message.as_bytes()),
137
+ },
138
+ structured_result: Value::Null,
139
+ diagnostics: vec![Diagnostic {
140
+ code: code.to_owned(),
141
+ message,
142
+ retryable: false,
143
+ }],
144
+ exit: ExitSemantics {
145
+ code: exit,
146
+ signal: None,
147
+ },
148
+ }
149
+ }
150
+
151
+ fn cancelled(request: &HostRequest, reason: &str, exit: u8, signal: Option<&str>) -> Self {
152
+ Self {
153
+ schema_version: 1,
154
+ contract: RESPONSE_CONTRACT.to_owned(),
155
+ protocol_version: PROTOCOL_VERSION.to_owned(),
156
+ request_id: request.request_id.clone(),
157
+ status: "cancelled".to_owned(),
158
+ host: HostIdentity {
159
+ kind: "node-subprocess".to_owned(),
160
+ implementation: "buildchain-v4-mjs-adapter".to_owned(),
161
+ capabilities: Vec::new(),
162
+ },
163
+ command: request.command.clone(),
164
+ output: HostOutput {
165
+ stdout: EncodedBytes::empty(),
166
+ stderr: EncodedBytes::empty(),
167
+ },
168
+ structured_result: Value::Null,
169
+ diagnostics: vec![Diagnostic {
170
+ code: reason.to_owned(),
171
+ message: "the bridge terminated and reaped the host process".to_owned(),
172
+ retryable: true,
173
+ }],
174
+ exit: ExitSemantics {
175
+ code: exit,
176
+ signal: signal.map(str::to_owned),
177
+ },
178
+ }
179
+ }
180
+ }
181
+
182
+ fn validate_request(request: &HostRequest) -> Result<(), String> {
183
+ if request.schema_version != 1 || request.contract != REQUEST_CONTRACT {
184
+ return Err("unsupported request contract".to_owned());
185
+ }
186
+ if request.protocol_version != PROTOCOL_VERSION {
187
+ return Err(format!(
188
+ "unsupported protocol version: {}",
189
+ request.protocol_version
190
+ ));
191
+ }
192
+ if request.request_id.trim().is_empty() || request.command.id.trim().is_empty() {
193
+ return Err("requestId and command.id must be non-empty".to_owned());
194
+ }
195
+ if !(1..=30_000).contains(&request.timeout_ms) {
196
+ return Err("timeoutMs must be between 1 and 30000".to_owned());
197
+ }
198
+ let input = request.input.decode()?;
199
+ if input.len() as u64 > MAX_REQUEST_BYTES {
200
+ return Err(format!("canonical input exceeds {MAX_REQUEST_BYTES} bytes"));
201
+ }
202
+ Ok(())
203
+ }
204
+
205
+ fn read_bounded(
206
+ mut reader: impl Read + Send + 'static,
207
+ limit: u64,
208
+ ) -> thread::JoinHandle<io::Result<Vec<u8>>> {
209
+ thread::spawn(move || {
210
+ let mut value = Vec::new();
211
+ reader.by_ref().take(limit + 1).read_to_end(&mut value)?;
212
+ if value.len() as u64 > limit {
213
+ return Err(io::Error::new(
214
+ io::ErrorKind::InvalidData,
215
+ format!("host output exceeds {limit} bytes"),
216
+ ));
217
+ }
218
+ Ok(value)
219
+ })
220
+ }
221
+
222
+ fn execute(request: &HostRequest, serialized_request: &[u8]) -> HostResponse {
223
+ let command = env::var("BUILDCHAIN_V4_HOST_COMMAND").unwrap_or_else(|_| "node".to_owned());
224
+ let script = env::var("BUILDCHAIN_V4_HOST_SCRIPT")
225
+ .unwrap_or_else(|_| "scripts/v4-host-adapter.mjs".to_owned());
226
+ let mut child = match Command::new(&command)
227
+ .arg(&script)
228
+ .stdin(Stdio::piped())
229
+ .stdout(Stdio::piped())
230
+ .stderr(Stdio::piped())
231
+ .spawn()
232
+ {
233
+ Ok(child) => child,
234
+ Err(error) => {
235
+ return HostResponse::bridge_failure(
236
+ request,
237
+ "host-spawn-failed",
238
+ format!("failed to start host command {command}: {error}"),
239
+ SOFTWARE_EXIT as u8,
240
+ );
241
+ }
242
+ };
243
+
244
+ let stdout = read_bounded(
245
+ child.stdout.take().expect("piped stdout"),
246
+ MAX_RESPONSE_BYTES,
247
+ );
248
+ let stderr = read_bounded(
249
+ child.stderr.take().expect("piped stderr"),
250
+ MAX_RESPONSE_BYTES,
251
+ );
252
+ if let Some(mut stdin) = child.stdin.take()
253
+ && let Err(error) = stdin.write_all(serialized_request)
254
+ {
255
+ let _ = child.kill();
256
+ let _ = child.wait();
257
+ return HostResponse::bridge_failure(
258
+ request,
259
+ "host-input-failed",
260
+ format!("failed to send canonical request to host: {error}"),
261
+ SOFTWARE_EXIT as u8,
262
+ );
263
+ }
264
+
265
+ let cancelled = Arc::new(AtomicBool::new(false));
266
+ let signal_seen = Arc::clone(&cancelled);
267
+ if let Err(error) = ctrlc::set_handler(move || signal_seen.store(true, Ordering::SeqCst)) {
268
+ let _ = child.kill();
269
+ let _ = child.wait();
270
+ return HostResponse::bridge_failure(
271
+ request,
272
+ "signal-handler-failed",
273
+ format!("failed to install cancellation handler: {error}"),
274
+ SOFTWARE_EXIT as u8,
275
+ );
276
+ }
277
+
278
+ let started = Instant::now();
279
+ let timeout = Duration::from_millis(request.timeout_ms);
280
+ let status = loop {
281
+ if cancelled.load(Ordering::SeqCst) {
282
+ let _ = child.kill();
283
+ let _ = child.wait();
284
+ let _ = stdout.join();
285
+ let _ = stderr.join();
286
+ return HostResponse::cancelled(request, "host-cancelled", 130, Some("SIGINT"));
287
+ }
288
+ if started.elapsed() >= timeout {
289
+ let _ = child.kill();
290
+ let _ = child.wait();
291
+ let _ = stdout.join();
292
+ let _ = stderr.join();
293
+ return HostResponse::cancelled(request, "host-timeout", 124, None);
294
+ }
295
+ match child.try_wait() {
296
+ Ok(Some(status)) => break status,
297
+ Ok(None) => thread::sleep(Duration::from_millis(5)),
298
+ Err(error) => {
299
+ let _ = child.kill();
300
+ let _ = child.wait();
301
+ return HostResponse::bridge_failure(
302
+ request,
303
+ "host-wait-failed",
304
+ format!("failed while waiting for host: {error}"),
305
+ SOFTWARE_EXIT as u8,
306
+ );
307
+ }
308
+ }
309
+ };
310
+
311
+ let stdout = match stdout.join() {
312
+ Ok(Ok(value)) => value,
313
+ Ok(Err(error)) => {
314
+ return HostResponse::bridge_failure(
315
+ request,
316
+ "host-output-invalid",
317
+ error.to_string(),
318
+ SOFTWARE_EXIT as u8,
319
+ );
320
+ }
321
+ Err(_) => {
322
+ return HostResponse::bridge_failure(
323
+ request,
324
+ "host-output-reader-panicked",
325
+ "host stdout reader panicked".to_owned(),
326
+ SOFTWARE_EXIT as u8,
327
+ );
328
+ }
329
+ };
330
+ let stderr = match stderr.join() {
331
+ Ok(Ok(value)) => value,
332
+ Ok(Err(error)) => {
333
+ return HostResponse::bridge_failure(
334
+ request,
335
+ "host-diagnostics-invalid",
336
+ error.to_string(),
337
+ SOFTWARE_EXIT as u8,
338
+ );
339
+ }
340
+ Err(_) => {
341
+ return HostResponse::bridge_failure(
342
+ request,
343
+ "host-diagnostics-reader-panicked",
344
+ "host stderr reader panicked".to_owned(),
345
+ SOFTWARE_EXIT as u8,
346
+ );
347
+ }
348
+ };
349
+
350
+ if !status.success() {
351
+ let code = status.code().unwrap_or(SOFTWARE_EXIT).clamp(1, 255) as u8;
352
+ let detail = String::from_utf8_lossy(&stderr).trim().to_owned();
353
+ return HostResponse::bridge_failure(
354
+ request,
355
+ "host-crashed",
356
+ if detail.is_empty() {
357
+ format!("host exited before returning a response: {status}")
358
+ } else {
359
+ format!("host exited before returning a response: {status}: {detail}")
360
+ },
361
+ code,
362
+ );
363
+ }
364
+
365
+ let mut response: HostResponse = match serde_json::from_slice(&stdout) {
366
+ Ok(value) => value,
367
+ Err(error) => {
368
+ return HostResponse::bridge_failure(
369
+ request,
370
+ "host-response-invalid",
371
+ format!("host returned invalid response JSON: {error}"),
372
+ SOFTWARE_EXIT as u8,
373
+ );
374
+ }
375
+ };
376
+ if response.schema_version != 1
377
+ || response.contract != RESPONSE_CONTRACT
378
+ || response.protocol_version != PROTOCOL_VERSION
379
+ || response.request_id != request.request_id
380
+ || response.command.id != request.command.id
381
+ {
382
+ return HostResponse::bridge_failure(
383
+ request,
384
+ "host-response-mismatch",
385
+ "host response does not match the request contract and correlation identity".to_owned(),
386
+ SOFTWARE_EXIT as u8,
387
+ );
388
+ }
389
+ if let Err(error) = response.output.stdout.decode() {
390
+ return HostResponse::bridge_failure(
391
+ request,
392
+ "host-stdout-invalid",
393
+ error,
394
+ SOFTWARE_EXIT as u8,
395
+ );
396
+ }
397
+ if let Err(error) = response.output.stderr.decode() {
398
+ return HostResponse::bridge_failure(
399
+ request,
400
+ "host-stderr-invalid",
401
+ error,
402
+ SOFTWARE_EXIT as u8,
403
+ );
404
+ }
405
+ if !stderr.is_empty() {
406
+ response.diagnostics.push(Diagnostic {
407
+ code: "host-transport-diagnostic".to_owned(),
408
+ message: String::from_utf8_lossy(&stderr).trim().to_owned(),
409
+ retryable: false,
410
+ });
411
+ }
412
+ response
413
+ }
414
+
415
+ fn run() -> Result<i32, String> {
416
+ let mode = env::args().nth(1).unwrap_or_else(|| "exchange".to_owned());
417
+ if !["exchange", "compat"].contains(&mode.as_str()) {
418
+ return Err("usage: buildchain-v4-bridge [exchange|compat]".to_owned());
419
+ }
420
+ let mut serialized = Vec::new();
421
+ io::stdin()
422
+ .take(MAX_REQUEST_BYTES * 2 + 1)
423
+ .read_to_end(&mut serialized)
424
+ .map_err(|error| format!("failed to read request: {error}"))?;
425
+ if serialized.len() as u64 > MAX_REQUEST_BYTES * 2 {
426
+ return Err("serialized request exceeds the bounded transport limit".to_owned());
427
+ }
428
+ let request: HostRequest = serde_json::from_slice(&serialized)
429
+ .map_err(|error| format!("invalid host request: {error}"))?;
430
+ validate_request(&request)?;
431
+ let response = execute(&request, &serialized);
432
+ if mode == "exchange" {
433
+ serde_json::to_writer(io::stdout().lock(), &response)
434
+ .map_err(|error| format!("failed to write response: {error}"))?;
435
+ println!();
436
+ return Ok(0);
437
+ }
438
+ io::stdout()
439
+ .write_all(&response.output.stdout.decode()?)
440
+ .map_err(|error| format!("failed to write command stdout: {error}"))?;
441
+ io::stderr()
442
+ .write_all(&response.output.stderr.decode()?)
443
+ .map_err(|error| format!("failed to write command stderr: {error}"))?;
444
+ Ok(i32::from(response.exit.code))
445
+ }
446
+
447
+ fn main() {
448
+ match run() {
449
+ Ok(code) => std::process::exit(code),
450
+ Err(error) => {
451
+ eprintln!("buildchain-v4-bridge: {error}");
452
+ std::process::exit(DATA_EXIT);
453
+ }
454
+ }
455
+ }
456
+
457
+ #[cfg(test)]
458
+ mod tests {
459
+ use super::*;
460
+
461
+ #[test]
462
+ fn rejects_private_transport_fields() {
463
+ let value = serde_json::json!({
464
+ "schemaVersion": 1,
465
+ "contract": REQUEST_CONTRACT,
466
+ "protocolVersion": PROTOCOL_VERSION,
467
+ "requestId": "fixture",
468
+ "command": { "id": "fixture.echo", "arguments": [] },
469
+ "input": { "encoding": "base64", "bytes": "" },
470
+ "requiredCapabilities": [],
471
+ "timeoutMs": 1000,
472
+ "credentials": { "token": "must-not-cross-contract" }
473
+ });
474
+ assert!(serde_json::from_value::<HostRequest>(value).is_err());
475
+ }
476
+
477
+ #[test]
478
+ fn validates_canonical_input_bytes() {
479
+ let request = HostRequest {
480
+ schema_version: 1,
481
+ contract: REQUEST_CONTRACT.to_owned(),
482
+ protocol_version: PROTOCOL_VERSION.to_owned(),
483
+ request_id: "fixture".to_owned(),
484
+ command: HostCommand {
485
+ id: "fixture.echo".to_owned(),
486
+ arguments: Vec::new(),
487
+ },
488
+ input: EncodedBytes::from_bytes(b"hello"),
489
+ required_capabilities: Vec::new(),
490
+ timeout_ms: 1000,
491
+ };
492
+ assert_eq!(request.input.decode().unwrap(), b"hello");
493
+ assert!(validate_request(&request).is_ok());
494
+ }
495
+ }
@@ -17,12 +17,8 @@
17
17
  "artifact-schemas.json",
18
18
  "release-passport-check-manifest.json",
19
19
  "schemas/release-passport-v1.schema.json",
20
- "schemas/publication-rehearsal-capsule-v1.schema.json",
21
- "schemas/release-tail-capabilities-v1.schema.json",
22
- "schemas/release-tail-provider-bindings-v1.schema.json",
23
20
  "schemas/kfd-agent-hub-adoption.schema.json",
24
21
  "schemas/kfd-product-gate-input-v1.schema.json",
25
- "schemas/dev-delivery-authority-v2.schema.json",
26
22
  "buildchain-contract.json",
27
23
  "kfd-upstream-aggregate.json",
28
24
  "kfd-claims.json",
@@ -5,12 +5,8 @@
5
5
  "buildchain.release.json",
6
6
  "release-passport-check-manifest.json",
7
7
  "schemas/release-passport-v1.schema.json",
8
- "schemas/publication-rehearsal-capsule-v1.schema.json",
9
- "schemas/release-tail-capabilities-v1.schema.json",
10
- "schemas/release-tail-provider-bindings-v1.schema.json",
11
8
  "schemas/kfd-agent-hub-adoption.schema.json",
12
9
  "schemas/kfd-product-gate-input-v1.schema.json",
13
- "schemas/dev-delivery-authority-v2.schema.json",
14
10
  "kfd-support.json",
15
11
  "artifact-evidence.json",
16
12
  "product-mechanism.json",
@@ -43,12 +39,8 @@
43
39
  "artifact-schemas.json",
44
40
  "release-passport-check-manifest.json",
45
41
  "schemas/release-passport-v1.schema.json",
46
- "schemas/publication-rehearsal-capsule-v1.schema.json",
47
- "schemas/release-tail-capabilities-v1.schema.json",
48
- "schemas/release-tail-provider-bindings-v1.schema.json",
49
42
  "schemas/kfd-agent-hub-adoption.schema.json",
50
43
  "schemas/kfd-product-gate-input-v1.schema.json",
51
- "schemas/dev-delivery-authority-v2.schema.json",
52
44
  "buildchain-contract.json",
53
45
  "kfd-claims.json",
54
46
  "product-mechanism.json",