rcf-lite 0.23.0 → 0.25.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 (666) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/blueprints/application-account-settings/docs/topics.md +10 -1
  3. package/blueprints/application-admin-console/CHANGELOG.md +12 -0
  4. package/blueprints/application-admin-console/blueprint.json +34 -1
  5. package/blueprints/application-admin-console/contributions/adrs/adr-2214-application-admin-console-consume-zero-trust-gate.json +25 -0
  6. package/blueprints/application-admin-console/contributions/requirements/application-admin-console-req-014.json +17 -0
  7. package/blueprints/application-admin-console/contributions/tacs/tac-2214-application-admin-console-access-gated-signin-surface.json +32 -0
  8. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21815.json +27 -0
  9. package/blueprints/application-admin-console/contributions/user-stories/application-admin-console-us-21816.json +27 -0
  10. package/blueprints/application-admin-console/docs/topics.md +10 -1
  11. package/blueprints/application-admin-console/guide/application-admin-console.md +24 -0
  12. package/blueprints/application-admin-console/probe-packs/application-admin-console.pack.mjs +26 -1
  13. package/blueprints/application-api-rest/docs/topics.md +10 -1
  14. package/blueprints/application-charts/docs/topics.md +10 -1
  15. package/blueprints/application-dashboard/docs/topics.md +10 -1
  16. package/blueprints/application-datatable/docs/topics.md +10 -1
  17. package/blueprints/application-empty-error-states/docs/topics.md +10 -1
  18. package/blueprints/application-error-handling/docs/topics.md +10 -1
  19. package/blueprints/application-file-upload/docs/topics.md +10 -1
  20. package/blueprints/application-forms-wizard/docs/topics.md +10 -1
  21. package/blueprints/application-notifications-in-app/docs/topics.md +10 -1
  22. package/blueprints/application-onboarding-tour/docs/topics.md +10 -1
  23. package/blueprints/application-spa/docs/topics.md +10 -1
  24. package/blueprints/delivery-ci-workflows/docs/topics.md +10 -1
  25. package/blueprints/deploy-cloudflare-workers/docs/topics.md +10 -1
  26. package/blueprints/deploy-hetzner-server/CHANGELOG.md +34 -0
  27. package/blueprints/deploy-hetzner-server/README.md +202 -0
  28. package/blueprints/deploy-hetzner-server/blueprint.json +233 -0
  29. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +14 -0
  30. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +14 -0
  31. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +20 -0
  32. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +14 -0
  33. package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +64 -0
  34. package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +116 -0
  35. package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +180 -0
  36. package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +106 -0
  37. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +63 -0
  38. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-snapshot-on-demand.mjs +60 -0
  39. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-throwaway-server-provision.mjs +58 -0
  40. package/blueprints/deploy-hetzner-server/contributions/probes/run-cloud-init-render-lint.mjs +9 -0
  41. package/blueprints/deploy-hetzner-server/contributions/probes/run-hcloud-dry-run-mock.mjs +9 -0
  42. package/blueprints/deploy-hetzner-server/contributions/probes/run-manifest-schema-validate.mjs +9 -0
  43. package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-cloud-init-hardened.mjs +9 -0
  44. package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-snapshot-on-demand.mjs +9 -0
  45. package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-throwaway-server-provision.mjs +9 -0
  46. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-001.json +18 -0
  47. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-002.json +18 -0
  48. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-003.json +18 -0
  49. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-004.json +18 -0
  50. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-005.json +18 -0
  51. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-006.json +18 -0
  52. package/blueprints/deploy-hetzner-server/contributions/schemas/hetzner-server.schema.json +108 -0
  53. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3801-deploy-hetzner-server-provisioner.json +27 -0
  54. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +26 -0
  55. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +26 -0
  56. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +25 -0
  57. package/blueprints/deploy-hetzner-server/contributions/templates/cloud-init.yaml.tmpl +93 -0
  58. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37101.json +24 -0
  59. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37102.json +24 -0
  60. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37103.json +25 -0
  61. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37104.json +24 -0
  62. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37105.json +24 -0
  63. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37106.json +25 -0
  64. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37107.json +24 -0
  65. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37108.json +24 -0
  66. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37109.json +24 -0
  67. package/blueprints/deploy-hetzner-server/docs/topics.md +48 -0
  68. package/blueprints/deploy-hetzner-server/guide/deploy-hetzner-server.md +180 -0
  69. package/blueprints/edge-cloudflare-access/CHANGELOG.md +23 -0
  70. package/blueprints/edge-cloudflare-access/README.md +141 -0
  71. package/blueprints/edge-cloudflare-access/blueprint.json +197 -0
  72. package/blueprints/edge-cloudflare-access/contributions/adrs/adr-3501-edge-cloudflare-access-jwt-gate.json +14 -0
  73. package/blueprints/edge-cloudflare-access/contributions/adrs/adr-3502-edge-cloudflare-access-identity-provider.json +25 -0
  74. package/blueprints/edge-cloudflare-access/contributions/adrs/adr-3503-edge-cloudflare-access-policy-scope.json +12 -0
  75. package/blueprints/edge-cloudflare-access/contributions/adrs/adr-3504-edge-cloudflare-access-audit-retention.json +12 -0
  76. package/blueprints/edge-cloudflare-access/contributions/probes/admin-console-gate-surface.mjs +100 -0
  77. package/blueprints/edge-cloudflare-access/contributions/probes/audit-event-secrecy.mjs +70 -0
  78. package/blueprints/edge-cloudflare-access/contributions/probes/jwt-validator-fixture.mjs +63 -0
  79. package/blueprints/edge-cloudflare-access/contributions/probes/jwt-validator-reject.mjs +95 -0
  80. package/blueprints/edge-cloudflare-access/contributions/probes/probe-utils.mjs +90 -0
  81. package/blueprints/edge-cloudflare-access/contributions/probes/real-account-gated-url.mjs +65 -0
  82. package/blueprints/edge-cloudflare-access/contributions/probes/run-admin-console-gate-surface.mjs +5 -0
  83. package/blueprints/edge-cloudflare-access/contributions/probes/run-audit-event-secrecy.mjs +5 -0
  84. package/blueprints/edge-cloudflare-access/contributions/probes/run-jwt-validator-fixture.mjs +5 -0
  85. package/blueprints/edge-cloudflare-access/contributions/probes/run-jwt-validator-reject.mjs +5 -0
  86. package/blueprints/edge-cloudflare-access/contributions/probes/run-real-account-gated-url.mjs +5 -0
  87. package/blueprints/edge-cloudflare-access/contributions/probes/run-wrangler-seam.mjs +5 -0
  88. package/blueprints/edge-cloudflare-access/contributions/probes/wrangler-seam.mjs +203 -0
  89. package/blueprints/edge-cloudflare-access/contributions/requirements/edge-cloudflare-access-req-001.json +17 -0
  90. package/blueprints/edge-cloudflare-access/contributions/requirements/edge-cloudflare-access-req-002.json +17 -0
  91. package/blueprints/edge-cloudflare-access/contributions/requirements/edge-cloudflare-access-req-003.json +17 -0
  92. package/blueprints/edge-cloudflare-access/contributions/requirements/edge-cloudflare-access-req-004.json +17 -0
  93. package/blueprints/edge-cloudflare-access/contributions/requirements/edge-cloudflare-access-req-005.json +17 -0
  94. package/blueprints/edge-cloudflare-access/contributions/requirements/edge-cloudflare-access-req-006.json +18 -0
  95. package/blueprints/edge-cloudflare-access/contributions/tacs/tac-3501-edge-cloudflare-access-jwt-validator.json +31 -0
  96. package/blueprints/edge-cloudflare-access/contributions/tacs/tac-3502-edge-cloudflare-access-application-declaration.json +33 -0
  97. package/blueprints/edge-cloudflare-access/contributions/tacs/tac-3503-edge-cloudflare-access-policy-shape.json +25 -0
  98. package/blueprints/edge-cloudflare-access/contributions/tacs/tac-3504-edge-cloudflare-access-audit-sink.json +25 -0
  99. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34101.json +27 -0
  100. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34102.json +28 -0
  101. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34103.json +27 -0
  102. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34104.json +27 -0
  103. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34105.json +27 -0
  104. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34106.json +27 -0
  105. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34107.json +27 -0
  106. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34108.json +27 -0
  107. package/blueprints/edge-cloudflare-access/contributions/user-stories/edge-cloudflare-access-us-34109.json +27 -0
  108. package/blueprints/edge-cloudflare-access/docs/topics.md +71 -0
  109. package/blueprints/edge-cloudflare-access/guide/edge-cloudflare-access.md +180 -0
  110. package/blueprints/edge-cloudflare-rate-limiting/CHANGELOG.md +10 -0
  111. package/blueprints/edge-cloudflare-rate-limiting/README.md +73 -0
  112. package/blueprints/edge-cloudflare-rate-limiting/blueprint.json +174 -0
  113. package/blueprints/edge-cloudflare-rate-limiting/contributions/adrs/adr-3701-edge-cloudflare-rate-limiting-throttle-contract.json +14 -0
  114. package/blueprints/edge-cloudflare-rate-limiting/contributions/adrs/adr-3702-edge-cloudflare-rate-limiting-layering.json +14 -0
  115. package/blueprints/edge-cloudflare-rate-limiting/contributions/adrs/adr-3703-edge-cloudflare-rate-limiting-manifest-shape.json +14 -0
  116. package/blueprints/edge-cloudflare-rate-limiting/contributions/adrs/adr-3704-edge-cloudflare-rate-limiting-drift-audit-cadence.json +14 -0
  117. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/event-secrecy.mjs +104 -0
  118. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/manifest-presence.mjs +95 -0
  119. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/manifest-schema-validate.mjs +130 -0
  120. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/probe-utils.mjs +111 -0
  121. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/real-account-burst-and-429.mjs +104 -0
  122. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/run-event-secrecy.mjs +5 -0
  123. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/run-manifest-presence.mjs +5 -0
  124. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/run-manifest-schema-validate.mjs +5 -0
  125. package/blueprints/edge-cloudflare-rate-limiting/contributions/probes/run-real-account-burst-and-429.mjs +5 -0
  126. package/blueprints/edge-cloudflare-rate-limiting/contributions/requirements/edge-cloudflare-rate-limiting-req-001.json +17 -0
  127. package/blueprints/edge-cloudflare-rate-limiting/contributions/requirements/edge-cloudflare-rate-limiting-req-002.json +17 -0
  128. package/blueprints/edge-cloudflare-rate-limiting/contributions/requirements/edge-cloudflare-rate-limiting-req-003.json +17 -0
  129. package/blueprints/edge-cloudflare-rate-limiting/contributions/requirements/edge-cloudflare-rate-limiting-req-004.json +17 -0
  130. package/blueprints/edge-cloudflare-rate-limiting/contributions/requirements/edge-cloudflare-rate-limiting-req-005.json +17 -0
  131. package/blueprints/edge-cloudflare-rate-limiting/contributions/schemas/rate-limit-rule.schema.json +67 -0
  132. package/blueprints/edge-cloudflare-rate-limiting/contributions/tacs/tac-3701-edge-cloudflare-rate-limiting-manifest-schema.json +31 -0
  133. package/blueprints/edge-cloudflare-rate-limiting/contributions/tacs/tac-3702-edge-cloudflare-rate-limiting-management-surface-doc.json +25 -0
  134. package/blueprints/edge-cloudflare-rate-limiting/contributions/tacs/tac-3703-edge-cloudflare-rate-limiting-drift-audit-runner.json +26 -0
  135. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36101.json +27 -0
  136. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36102.json +27 -0
  137. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36103.json +27 -0
  138. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36104.json +27 -0
  139. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36105.json +27 -0
  140. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36106.json +27 -0
  141. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36107.json +27 -0
  142. package/blueprints/edge-cloudflare-rate-limiting/contributions/user-stories/edge-cloudflare-rate-limiting-us-36108.json +27 -0
  143. package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +71 -0
  144. package/blueprints/edge-cloudflare-rate-limiting/guide/edge-cloudflare-rate-limiting.md +88 -0
  145. package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +13 -0
  146. package/blueprints/edge-cloudflare-tunnel/README.md +257 -0
  147. package/blueprints/edge-cloudflare-tunnel/blueprint.json +168 -0
  148. package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +23 -0
  149. package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +19 -0
  150. package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +19 -0
  151. package/blueprints/edge-cloudflare-tunnel/contributions/probes/aud-presence-check.mjs +114 -0
  152. package/blueprints/edge-cloudflare-tunnel/contributions/probes/cloudflared-config-lint.mjs +80 -0
  153. package/blueprints/edge-cloudflare-tunnel/contributions/probes/manifest-schema-validate.mjs +234 -0
  154. package/blueprints/edge-cloudflare-tunnel/contributions/probes/probe-utils.mjs +231 -0
  155. package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-connector-healthy.mjs +54 -0
  156. package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-tunnel-hostname-routes.mjs +73 -0
  157. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-aud-presence-check.mjs +4 -0
  158. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-cloudflared-config-lint.mjs +4 -0
  159. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-manifest-schema-validate.mjs +4 -0
  160. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-real-account-connector-healthy.mjs +4 -0
  161. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-real-account-tunnel-hostname-routes.mjs +4 -0
  162. package/blueprints/edge-cloudflare-tunnel/contributions/probes/tunnel-manifest.schema.json +55 -0
  163. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-001.json +18 -0
  164. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-002.json +18 -0
  165. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-003.json +18 -0
  166. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-004.json +18 -0
  167. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-005.json +18 -0
  168. package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +26 -0
  169. package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +26 -0
  170. package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +25 -0
  171. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39101.json +24 -0
  172. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39102.json +24 -0
  173. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39103.json +24 -0
  174. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39104.json +24 -0
  175. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39105.json +24 -0
  176. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39106.json +24 -0
  177. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39107.json +24 -0
  178. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39108.json +24 -0
  179. package/blueprints/edge-cloudflare-tunnel/docs/topics.md +72 -0
  180. package/blueprints/edge-cloudflare-tunnel/guide/edge-cloudflare-tunnel.md +204 -0
  181. package/blueprints/edge-cloudflare-turnstile/CHANGELOG.md +20 -0
  182. package/blueprints/edge-cloudflare-turnstile/README.md +95 -0
  183. package/blueprints/edge-cloudflare-turnstile/blueprint.json +161 -0
  184. package/blueprints/edge-cloudflare-turnstile/contributions/adrs/adr-3601-edge-cloudflare-turnstile-verifier-contract.json +14 -0
  185. package/blueprints/edge-cloudflare-turnstile/contributions/adrs/adr-3602-edge-cloudflare-turnstile-widget-mode.json +14 -0
  186. package/blueprints/edge-cloudflare-turnstile/contributions/adrs/adr-3603-edge-cloudflare-turnstile-refuse-shape.json +14 -0
  187. package/blueprints/edge-cloudflare-turnstile/contributions/probes/event-secrecy.mjs +51 -0
  188. package/blueprints/edge-cloudflare-turnstile/contributions/probes/guard-shape-scan.mjs +48 -0
  189. package/blueprints/edge-cloudflare-turnstile/contributions/probes/probe-utils.mjs +139 -0
  190. package/blueprints/edge-cloudflare-turnstile/contributions/probes/run-event-secrecy.mjs +5 -0
  191. package/blueprints/edge-cloudflare-turnstile/contributions/probes/run-guard-shape-scan.mjs +5 -0
  192. package/blueprints/edge-cloudflare-turnstile/contributions/probes/run-secret-shape-scan.mjs +5 -0
  193. package/blueprints/edge-cloudflare-turnstile/contributions/probes/run-siteverify-fixture.mjs +5 -0
  194. package/blueprints/edge-cloudflare-turnstile/contributions/probes/secret-shape-scan.mjs +77 -0
  195. package/blueprints/edge-cloudflare-turnstile/contributions/probes/siteverify-fixture.mjs +53 -0
  196. package/blueprints/edge-cloudflare-turnstile/contributions/requirements/edge-cloudflare-turnstile-req-001.json +17 -0
  197. package/blueprints/edge-cloudflare-turnstile/contributions/requirements/edge-cloudflare-turnstile-req-002.json +17 -0
  198. package/blueprints/edge-cloudflare-turnstile/contributions/requirements/edge-cloudflare-turnstile-req-003.json +17 -0
  199. package/blueprints/edge-cloudflare-turnstile/contributions/requirements/edge-cloudflare-turnstile-req-004.json +17 -0
  200. package/blueprints/edge-cloudflare-turnstile/contributions/requirements/edge-cloudflare-turnstile-req-005.json +17 -0
  201. package/blueprints/edge-cloudflare-turnstile/contributions/tacs/tac-3601-edge-cloudflare-turnstile-widget-mount.json +26 -0
  202. package/blueprints/edge-cloudflare-turnstile/contributions/tacs/tac-3602-edge-cloudflare-turnstile-server-verifier.json +36 -0
  203. package/blueprints/edge-cloudflare-turnstile/contributions/tacs/tac-3603-edge-cloudflare-turnstile-magic-link-hook.json +25 -0
  204. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35101.json +27 -0
  205. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35102.json +27 -0
  206. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35103.json +27 -0
  207. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35104.json +27 -0
  208. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35105.json +36 -0
  209. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35106.json +27 -0
  210. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35107.json +27 -0
  211. package/blueprints/edge-cloudflare-turnstile/contributions/user-stories/edge-cloudflare-turnstile-us-35108.json +27 -0
  212. package/blueprints/edge-cloudflare-turnstile/docs/topics.md +71 -0
  213. package/blueprints/edge-cloudflare-turnstile/guide/edge-cloudflare-turnstile.md +104 -0
  214. package/blueprints/edge-cloudflare-turnstile/probe-packs/edge-cloudflare-turnstile.pack.mjs +146 -0
  215. package/blueprints/email-smtp-resend/docs/topics.md +10 -1
  216. package/blueprints/jobs-background/docs/topics.md +10 -1
  217. package/blueprints/messaging-queue-cloudflare/docs/topics.md +10 -1
  218. package/blueprints/object-storage-s3/docs/topics.md +10 -1
  219. package/blueprints/observability-essentials/docs/topics.md +10 -1
  220. package/blueprints/observability-logging/docs/topics.md +10 -1
  221. package/blueprints/observability-probe-endpoints/docs/topics.md +10 -1
  222. package/blueprints/persistence-data-d1/docs/topics.md +10 -1
  223. package/blueprints/persistence-data-postgres/docs/topics.md +10 -1
  224. package/blueprints/persistence-data-sqlite/docs/topics.md +10 -1
  225. package/blueprints/platform-cloudflare-cron-triggers/CHANGELOG.md +22 -0
  226. package/blueprints/platform-cloudflare-cron-triggers/README.md +117 -0
  227. package/blueprints/platform-cloudflare-cron-triggers/blueprint.json +85 -0
  228. package/blueprints/platform-cloudflare-cron-triggers/contributions/adrs/adr-3301-platform-cloudflare-cron-triggers-scheduled-trigger-contract.json +14 -0
  229. package/blueprints/platform-cloudflare-cron-triggers/contributions/adrs/adr-3302-platform-cloudflare-cron-triggers-dispatcher-mode.json +14 -0
  230. package/blueprints/platform-cloudflare-cron-triggers/contributions/adrs/adr-3303-platform-cloudflare-cron-triggers-skew-tolerance-default.json +14 -0
  231. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/dispatcher-routing.mjs +64 -0
  232. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/event-secrecy.mjs +100 -0
  233. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/probe-utils.mjs +70 -0
  234. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/real-account-scheduled-smoke.mjs +100 -0
  235. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/run-dispatcher-routing.mjs +5 -0
  236. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/run-event-secrecy.mjs +5 -0
  237. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/run-real-account-scheduled-smoke.mjs +5 -0
  238. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/run-skew-tolerance.mjs +5 -0
  239. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/run-wrangler-test-scheduled.mjs +5 -0
  240. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/skew-tolerance.mjs +130 -0
  241. package/blueprints/platform-cloudflare-cron-triggers/contributions/probes/wrangler-test-scheduled.mjs +129 -0
  242. package/blueprints/platform-cloudflare-cron-triggers/contributions/requirements/platform-cloudflare-cron-triggers-req-001.json +17 -0
  243. package/blueprints/platform-cloudflare-cron-triggers/contributions/requirements/platform-cloudflare-cron-triggers-req-002.json +17 -0
  244. package/blueprints/platform-cloudflare-cron-triggers/contributions/requirements/platform-cloudflare-cron-triggers-req-003.json +17 -0
  245. package/blueprints/platform-cloudflare-cron-triggers/contributions/requirements/platform-cloudflare-cron-triggers-req-004.json +17 -0
  246. package/blueprints/platform-cloudflare-cron-triggers/contributions/tacs/tac-3301-platform-cloudflare-cron-triggers-scheduled-handler.json +22 -0
  247. package/blueprints/platform-cloudflare-cron-triggers/contributions/tacs/tac-3302-platform-cloudflare-cron-triggers-expression-router.json +22 -0
  248. package/blueprints/platform-cloudflare-cron-triggers/contributions/tacs/tac-3303-platform-cloudflare-cron-triggers-event-sink.json +21 -0
  249. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32101.json +25 -0
  250. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32102.json +34 -0
  251. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32103.json +34 -0
  252. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32104.json +34 -0
  253. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32105.json +25 -0
  254. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32106.json +25 -0
  255. package/blueprints/platform-cloudflare-cron-triggers/contributions/user-stories/platform-cloudflare-cron-triggers-us-32107.json +25 -0
  256. package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +74 -0
  257. package/blueprints/platform-cloudflare-cron-triggers/guide/platform-cloudflare-cron-triggers.md +119 -0
  258. package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +23 -0
  259. package/blueprints/platform-cloudflare-durable-objects/README.md +200 -0
  260. package/blueprints/platform-cloudflare-durable-objects/blueprint.json +243 -0
  261. package/blueprints/platform-cloudflare-durable-objects/contributions/adrs/adr-3401-platform-cloudflare-durable-objects-single-cell-contract.json +14 -0
  262. package/blueprints/platform-cloudflare-durable-objects/contributions/adrs/adr-3402-platform-cloudflare-durable-objects-websocket-hub-contract.json +14 -0
  263. package/blueprints/platform-cloudflare-durable-objects/contributions/adrs/adr-3403-platform-cloudflare-durable-objects-storage-backend.json +14 -0
  264. package/blueprints/platform-cloudflare-durable-objects/contributions/adrs/adr-3404-platform-cloudflare-durable-objects-migrations-shape.json +14 -0
  265. package/blueprints/platform-cloudflare-durable-objects/contributions/adrs/adr-3405-platform-cloudflare-durable-objects-hibernation-posture.json +14 -0
  266. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/alarm-fires-once.mjs +63 -0
  267. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/namespace-facade-ready.mjs +60 -0
  268. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/probe-utils.mjs +105 -0
  269. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/real-account-storage-smoke.mjs +58 -0
  270. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-alarm-fires-once.mjs +5 -0
  271. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-namespace-facade-ready.mjs +5 -0
  272. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-real-account-storage-smoke.mjs +5 -0
  273. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-single-cell-concurrent-increment.mjs +5 -0
  274. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-sole-reader-scan.mjs +5 -0
  275. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-storage-round-trip.mjs +5 -0
  276. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-websocket-hub-broadcast.mjs +5 -0
  277. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/run-wrangler-seam.mjs +5 -0
  278. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/single-cell-concurrent-increment.mjs +56 -0
  279. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/sole-reader-scan.mjs +117 -0
  280. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/storage-round-trip.mjs +87 -0
  281. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/websocket-hub-broadcast.mjs +112 -0
  282. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/wrangler-seam.mjs +257 -0
  283. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-001.json +17 -0
  284. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-002.json +17 -0
  285. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-003.json +17 -0
  286. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-004.json +17 -0
  287. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-005.json +17 -0
  288. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-006.json +17 -0
  289. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-007.json +17 -0
  290. package/blueprints/platform-cloudflare-durable-objects/contributions/requirements/platform-cloudflare-durable-objects-req-008.json +17 -0
  291. package/blueprints/platform-cloudflare-durable-objects/contributions/tacs/tac-3401-platform-cloudflare-durable-objects-namespace-facade.json +31 -0
  292. package/blueprints/platform-cloudflare-durable-objects/contributions/tacs/tac-3402-platform-cloudflare-durable-objects-single-cell-shape.json +30 -0
  293. package/blueprints/platform-cloudflare-durable-objects/contributions/tacs/tac-3403-platform-cloudflare-durable-objects-alarm-handler.json +29 -0
  294. package/blueprints/platform-cloudflare-durable-objects/contributions/tacs/tac-3404-platform-cloudflare-durable-objects-websocket-hub.json +31 -0
  295. package/blueprints/platform-cloudflare-durable-objects/contributions/tacs/tac-3405-platform-cloudflare-durable-objects-event-sink.json +24 -0
  296. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33101.json +27 -0
  297. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33102.json +27 -0
  298. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33103.json +27 -0
  299. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33104.json +27 -0
  300. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33105.json +27 -0
  301. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33106.json +27 -0
  302. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33107.json +27 -0
  303. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33108.json +27 -0
  304. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33109.json +27 -0
  305. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33110.json +27 -0
  306. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33111.json +27 -0
  307. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33112.json +27 -0
  308. package/blueprints/platform-cloudflare-durable-objects/contributions/user-stories/platform-cloudflare-durable-objects-us-33113.json +25 -0
  309. package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +76 -0
  310. package/blueprints/platform-cloudflare-durable-objects/guide/platform-cloudflare-durable-objects.md +237 -0
  311. package/blueprints/platform-cloudflare-kv/CHANGELOG.md +21 -0
  312. package/blueprints/platform-cloudflare-kv/README.md +159 -0
  313. package/blueprints/platform-cloudflare-kv/blueprint.json +87 -0
  314. package/blueprints/platform-cloudflare-kv/contributions/adrs/adr-3201-platform-cloudflare-kv-key-value-store-contract.json +14 -0
  315. package/blueprints/platform-cloudflare-kv/contributions/adrs/adr-3202-platform-cloudflare-kv-default-cache-ttl.json +14 -0
  316. package/blueprints/platform-cloudflare-kv/contributions/adrs/adr-3203-platform-cloudflare-kv-key-naming-convention.json +14 -0
  317. package/blueprints/platform-cloudflare-kv/contributions/probes/cache-aside-hit-then-miss.mjs +82 -0
  318. package/blueprints/platform-cloudflare-kv/contributions/probes/event-secrecy.mjs +102 -0
  319. package/blueprints/platform-cloudflare-kv/contributions/probes/facade-round-trip.mjs +92 -0
  320. package/blueprints/platform-cloudflare-kv/contributions/probes/list-with-prefix.mjs +52 -0
  321. package/blueprints/platform-cloudflare-kv/contributions/probes/probe-utils.mjs +73 -0
  322. package/blueprints/platform-cloudflare-kv/contributions/probes/real-account-eventual-consistency-smoke.mjs +102 -0
  323. package/blueprints/platform-cloudflare-kv/contributions/probes/run-cache-aside-hit-then-miss.mjs +5 -0
  324. package/blueprints/platform-cloudflare-kv/contributions/probes/run-event-secrecy.mjs +5 -0
  325. package/blueprints/platform-cloudflare-kv/contributions/probes/run-facade-round-trip.mjs +5 -0
  326. package/blueprints/platform-cloudflare-kv/contributions/probes/run-list-with-prefix.mjs +5 -0
  327. package/blueprints/platform-cloudflare-kv/contributions/probes/run-real-account-eventual-consistency-smoke.mjs +5 -0
  328. package/blueprints/platform-cloudflare-kv/contributions/requirements/platform-cloudflare-kv-req-001.json +18 -0
  329. package/blueprints/platform-cloudflare-kv/contributions/requirements/platform-cloudflare-kv-req-002.json +18 -0
  330. package/blueprints/platform-cloudflare-kv/contributions/requirements/platform-cloudflare-kv-req-003.json +18 -0
  331. package/blueprints/platform-cloudflare-kv/contributions/requirements/platform-cloudflare-kv-req-004.json +18 -0
  332. package/blueprints/platform-cloudflare-kv/contributions/requirements/platform-cloudflare-kv-req-005.json +18 -0
  333. package/blueprints/platform-cloudflare-kv/contributions/tacs/tac-3201-platform-cloudflare-kv-facade.json +27 -0
  334. package/blueprints/platform-cloudflare-kv/contributions/tacs/tac-3202-platform-cloudflare-kv-cache-aside.json +26 -0
  335. package/blueprints/platform-cloudflare-kv/contributions/tacs/tac-3203-platform-cloudflare-kv-event-sink.json +25 -0
  336. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31101.json +27 -0
  337. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31102.json +27 -0
  338. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31103.json +36 -0
  339. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31104.json +27 -0
  340. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31105.json +27 -0
  341. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31106.json +27 -0
  342. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31107.json +27 -0
  343. package/blueprints/platform-cloudflare-kv/contributions/user-stories/platform-cloudflare-kv-us-31108.json +27 -0
  344. package/blueprints/platform-cloudflare-kv/docs/topics.md +74 -0
  345. package/blueprints/platform-cloudflare-kv/guide/platform-cloudflare-kv.md +138 -0
  346. package/blueprints/platform-docker-compose-host/CHANGELOG.md +12 -0
  347. package/blueprints/platform-docker-compose-host/README.md +76 -0
  348. package/blueprints/platform-docker-compose-host/blueprint.json +209 -0
  349. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3901-platform-docker-compose-host-contract.json +14 -0
  350. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +14 -0
  351. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +14 -0
  352. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3904-platform-docker-compose-host-log-driver.json +14 -0
  353. package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +108 -0
  354. package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +231 -0
  355. package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +185 -0
  356. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +41 -0
  357. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +36 -0
  358. package/blueprints/platform-docker-compose-host/contributions/probes/run-caddyfile-validate.mjs +5 -0
  359. package/blueprints/platform-docker-compose-host/contributions/probes/run-compose-config-lint.mjs +5 -0
  360. package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-minimal-stack-up.mjs +5 -0
  361. package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-reload-burst.mjs +5 -0
  362. package/blueprints/platform-docker-compose-host/contributions/probes/run-secrets-as-files-scan.mjs +5 -0
  363. package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +116 -0
  364. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-001.json +18 -0
  365. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-002.json +18 -0
  366. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-003.json +18 -0
  367. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-004.json +18 -0
  368. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-005.json +18 -0
  369. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-006.json +18 -0
  370. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +26 -0
  371. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +26 -0
  372. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +26 -0
  373. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +26 -0
  374. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38101.json +24 -0
  375. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38102.json +24 -0
  376. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38103.json +24 -0
  377. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38104.json +24 -0
  378. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38105.json +25 -0
  379. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38106.json +24 -0
  380. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38107.json +24 -0
  381. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38108.json +24 -0
  382. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38109.json +24 -0
  383. package/blueprints/platform-docker-compose-host/docs/topics.md +71 -0
  384. package/blueprints/platform-docker-compose-host/guide/platform-docker-compose-host.md +94 -0
  385. package/blueprints/security-auth-clerk/docs/topics.md +10 -1
  386. package/blueprints/security-auth-keycloak/docs/topics.md +10 -1
  387. package/blueprints/security-auth-magic-link/docs/topics.md +10 -1
  388. package/blueprints/security-auth-oauth2/docs/topics.md +10 -1
  389. package/blueprints/security-secrets-management/docs/topics.md +10 -1
  390. package/fixtures/canary-manifest.json +9 -9
  391. package/package.json +1 -1
  392. package/rcf/adrs/adr-2214-application-admin-console-consume-zero-trust-gate.json +13 -0
  393. package/rcf/adrs/adr-3501-edge-cloudflare-access-jwt-gate.json +13 -0
  394. package/rcf/adrs/adr-3502-edge-cloudflare-access-identity-provider.json +13 -0
  395. package/rcf/adrs/adr-3503-edge-cloudflare-access-policy-scope.json +13 -0
  396. package/rcf/adrs/adr-3504-edge-cloudflare-access-audit-retention.json +13 -0
  397. package/rcf/adrs/adr-3601-edge-cloudflare-turnstile-verifier-contract.json +13 -0
  398. package/rcf/adrs/adr-3602-edge-cloudflare-turnstile-widget-mode.json +13 -0
  399. package/rcf/adrs/adr-3603-edge-cloudflare-turnstile-refuse-shape.json +13 -0
  400. package/rcf/adrs/adr-3701-edge-cloudflare-rate-limiting-throttle-contract.json +13 -0
  401. package/rcf/adrs/adr-3702-edge-cloudflare-rate-limiting-layering.json +13 -0
  402. package/rcf/adrs/adr-3703-edge-cloudflare-rate-limiting-manifest-shape.json +13 -0
  403. package/rcf/adrs/adr-3704-edge-cloudflare-rate-limiting-drift-audit-cadence.json +13 -0
  404. package/rcf/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +13 -0
  405. package/rcf/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +13 -0
  406. package/rcf/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +13 -0
  407. package/rcf/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +13 -0
  408. package/rcf/adrs/adr-3901-platform-docker-compose-host-contract.json +13 -0
  409. package/rcf/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +13 -0
  410. package/rcf/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +13 -0
  411. package/rcf/adrs/adr-3904-platform-docker-compose-host-log-driver.json +13 -0
  412. package/rcf/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +13 -0
  413. package/rcf/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +13 -0
  414. package/rcf/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +13 -0
  415. package/rcf/code-nodes/cn-230.json +13 -0
  416. package/rcf/code-nodes/cn-231.json +14 -0
  417. package/rcf/code-nodes/cn-232.json +13 -0
  418. package/rcf/code-nodes/cn-233.json +14 -0
  419. package/rcf/code-nodes/cn-234.json +13 -0
  420. package/rcf/code-nodes/cn-260.json +21 -0
  421. package/rcf/code-nodes/cn-261.json +14 -0
  422. package/rcf/code-nodes/cn-262.json +18 -0
  423. package/rcf/code-nodes/cn-263.json +12 -0
  424. package/rcf/code-nodes/cn-264.json +13 -0
  425. package/rcf/code-nodes/cn-265.json +15 -0
  426. package/rcf/code-nodes/cn-266.json +12 -0
  427. package/rcf/code-nodes/cn-267.json +12 -0
  428. package/rcf/code-nodes/cn-290.json +21 -0
  429. package/rcf/code-nodes/cn-291.json +12 -0
  430. package/rcf/code-nodes/cn-292.json +13 -0
  431. package/rcf/code-nodes/cn-293.json +13 -0
  432. package/rcf/code-nodes/cn-294.json +13 -0
  433. package/rcf/code-nodes/cn-295.json +12 -0
  434. package/rcf/code-nodes/cn-296.json +12 -0
  435. package/rcf/code-nodes/cn-297.json +12 -0
  436. package/rcf/code-nodes/cn-298.json +12 -0
  437. package/rcf/code-nodes/cn-299.json +12 -0
  438. package/rcf/code-nodes/cn-300.json +12 -0
  439. package/rcf/code-nodes/cn-301.json +14 -0
  440. package/rcf/code-nodes/cn-302.json +12 -0
  441. package/rcf/code-nodes/cn-303.json +12 -0
  442. package/rcf/code-nodes/cn-304.json +21 -0
  443. package/rcf/code-nodes/cn-320.json +20 -0
  444. package/rcf/code-nodes/cn-321.json +14 -0
  445. package/rcf/code-nodes/cn-350.json +19 -0
  446. package/rcf/code-nodes/cn-380.json +19 -0
  447. package/rcf/code-nodes/cn-410.json +20 -0
  448. package/rcf/code-nodes/cn-440.json +20 -0
  449. package/rcf/code-nodes/cn-470.json +19 -0
  450. package/rcf/fbs/fbs-070.json +24 -0
  451. package/rcf/fbs/fbs-080.json +24 -0
  452. package/rcf/fbs/fbs-090.json +24 -0
  453. package/rcf/fbs/fbs-100.json +23 -0
  454. package/rcf/fbs/fbs-101.json +16 -0
  455. package/rcf/fbs/fbs-110.json +23 -0
  456. package/rcf/fbs/fbs-120.json +22 -0
  457. package/rcf/fbs/fbs-130.json +23 -0
  458. package/rcf/fbs/fbs-140.json +23 -0
  459. package/rcf/fbs/fbs-150.json +22 -0
  460. package/rcf/requirements/req-050.json +18 -0
  461. package/rcf/requirements/req-051.json +18 -0
  462. package/rcf/requirements/req-052.json +18 -0
  463. package/rcf/requirements/req-053.json +18 -0
  464. package/rcf/requirements/req-054.json +18 -0
  465. package/rcf/requirements/req-060.json +20 -0
  466. package/rcf/requirements/req-061.json +18 -0
  467. package/rcf/requirements/req-062.json +20 -0
  468. package/rcf/requirements/req-063.json +20 -0
  469. package/rcf/requirements/req-070.json +18 -0
  470. package/rcf/requirements/req-071.json +18 -0
  471. package/rcf/requirements/req-072.json +18 -0
  472. package/rcf/requirements/req-073.json +18 -0
  473. package/rcf/requirements/req-074.json +18 -0
  474. package/rcf/requirements/req-075.json +18 -0
  475. package/rcf/requirements/req-076.json +18 -0
  476. package/rcf/requirements/req-077.json +18 -0
  477. package/rcf/requirements/req-080.json +18 -0
  478. package/rcf/requirements/req-081.json +18 -0
  479. package/rcf/requirements/req-082.json +18 -0
  480. package/rcf/requirements/req-083.json +18 -0
  481. package/rcf/requirements/req-084.json +18 -0
  482. package/rcf/requirements/req-085.json +18 -0
  483. package/rcf/requirements/req-086.json +18 -0
  484. package/rcf/requirements/req-090.json +44 -0
  485. package/rcf/requirements/req-091.json +53 -0
  486. package/rcf/requirements/req-092.json +32 -0
  487. package/rcf/requirements/req-093.json +52 -0
  488. package/rcf/requirements/req-094.json +32 -0
  489. package/rcf/requirements/req-100.json +13 -0
  490. package/rcf/requirements/req-101.json +13 -0
  491. package/rcf/requirements/req-102.json +13 -0
  492. package/rcf/requirements/req-103.json +13 -0
  493. package/rcf/requirements/req-104.json +13 -0
  494. package/rcf/requirements/req-110.json +58 -0
  495. package/rcf/requirements/req-111.json +32 -0
  496. package/rcf/requirements/req-112.json +42 -0
  497. package/rcf/requirements/req-113.json +33 -0
  498. package/rcf/requirements/req-114.json +20 -0
  499. package/rcf/requirements/req-115.json +38 -0
  500. package/rcf/requirements/req-120.json +20 -0
  501. package/rcf/requirements/req-121.json +20 -0
  502. package/rcf/requirements/req-122.json +20 -0
  503. package/rcf/requirements/req-123.json +42 -0
  504. package/rcf/requirements/req-124.json +27 -0
  505. package/rcf/requirements/req-125.json +20 -0
  506. package/rcf/requirements/req-130.json +32 -0
  507. package/rcf/requirements/req-131.json +54 -0
  508. package/rcf/requirements/req-132.json +27 -0
  509. package/rcf/requirements/req-133.json +20 -0
  510. package/rcf/requirements/req-134.json +42 -0
  511. package/rcf/tacs/tac-2214-application-admin-console-access-gated-signin-surface.json +15 -0
  512. package/rcf/tacs/tac-3501-edge-cloudflare-access-jwt-validator.json +15 -0
  513. package/rcf/tacs/tac-3502-edge-cloudflare-access-application-declaration.json +15 -0
  514. package/rcf/tacs/tac-3503-edge-cloudflare-access-policy-shape.json +15 -0
  515. package/rcf/tacs/tac-3504-edge-cloudflare-access-audit-sink.json +15 -0
  516. package/rcf/tacs/tac-3601-edge-cloudflare-turnstile-widget-mount.json +15 -0
  517. package/rcf/tacs/tac-3602-edge-cloudflare-turnstile-server-verifier.json +15 -0
  518. package/rcf/tacs/tac-3603-edge-cloudflare-turnstile-magic-link-hook.json +15 -0
  519. package/rcf/tacs/tac-3701-edge-cloudflare-rate-limiting-manifest-schema.json +16 -0
  520. package/rcf/tacs/tac-3702-edge-cloudflare-rate-limiting-management-surface-doc.json +15 -0
  521. package/rcf/tacs/tac-3703-edge-cloudflare-rate-limiting-drift-audit-runner.json +16 -0
  522. package/rcf/tacs/tac-3801-deploy-hetzner-server-provisioner.json +17 -0
  523. package/rcf/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +17 -0
  524. package/rcf/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +17 -0
  525. package/rcf/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +16 -0
  526. package/rcf/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +17 -0
  527. package/rcf/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +17 -0
  528. package/rcf/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +17 -0
  529. package/rcf/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +17 -0
  530. package/rcf/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +17 -0
  531. package/rcf/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +17 -0
  532. package/rcf/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +16 -0
  533. package/rcf/test-suites/ts-052.json +58 -8
  534. package/rcf/test-suites/ts-080.json +22 -0
  535. package/rcf/test-suites/ts-081.json +22 -0
  536. package/rcf/test-suites/ts-082.json +30 -0
  537. package/rcf/test-suites/ts-083.json +22 -0
  538. package/rcf/test-suites/ts-084.json +30 -0
  539. package/rcf/test-suites/ts-085.json +22 -0
  540. package/rcf/test-suites/ts-086.json +22 -0
  541. package/rcf/test-suites/ts-087.json +22 -0
  542. package/rcf/test-suites/ts-090.json +22 -0
  543. package/rcf/test-suites/ts-091.json +22 -0
  544. package/rcf/test-suites/ts-092.json +22 -0
  545. package/rcf/test-suites/ts-093.json +30 -0
  546. package/rcf/test-suites/ts-094.json +30 -0
  547. package/rcf/test-suites/ts-095.json +30 -0
  548. package/rcf/test-suites/ts-096.json +22 -0
  549. package/rcf/test-suites/ts-100.json +22 -0
  550. package/rcf/test-suites/ts-101.json +22 -0
  551. package/rcf/test-suites/ts-102.json +22 -0
  552. package/rcf/test-suites/ts-103.json +22 -0
  553. package/rcf/test-suites/ts-104.json +22 -0
  554. package/rcf/test-suites/ts-105.json +22 -0
  555. package/rcf/test-suites/ts-106.json +22 -0
  556. package/rcf/test-suites/ts-107.json +22 -0
  557. package/rcf/test-suites/ts-108.json +22 -0
  558. package/rcf/test-suites/ts-109.json +22 -0
  559. package/rcf/test-suites/ts-110.json +22 -0
  560. package/rcf/test-suites/ts-111.json +22 -0
  561. package/rcf/test-suites/ts-112.json +22 -0
  562. package/rcf/test-suites/ts-113.json +22 -0
  563. package/rcf/test-suites/ts-114.json +22 -0
  564. package/rcf/test-suites/ts-115.json +22 -0
  565. package/rcf/test-suites/ts-116.json +22 -0
  566. package/rcf/test-suites/ts-117.json +22 -0
  567. package/rcf/test-suites/ts-118.json +22 -0
  568. package/rcf/test-suites/ts-119.json +30 -0
  569. package/rcf/test-suites/ts-120.json +22 -0
  570. package/rcf/test-suites/ts-121.json +22 -0
  571. package/rcf/test-suites/ts-122.json +22 -0
  572. package/rcf/test-suites/ts-123.json +22 -0
  573. package/rcf/test-suites/ts-124.json +22 -0
  574. package/rcf/test-suites/ts-125.json +22 -0
  575. package/rcf/test-suites/ts-126.json +22 -0
  576. package/rcf/test-suites/ts-127.json +22 -0
  577. package/rcf/test-suites/ts-130.json +22 -0
  578. package/rcf/test-suites/ts-131.json +22 -0
  579. package/rcf/test-suites/ts-132.json +22 -0
  580. package/rcf/test-suites/ts-133.json +22 -0
  581. package/rcf/test-suites/ts-134.json +22 -0
  582. package/rcf/test-suites/ts-135.json +22 -0
  583. package/rcf/test-suites/ts-136.json +22 -0
  584. package/rcf/test-suites/ts-137.json +22 -0
  585. package/rcf/test-suites/ts-140.json +86 -0
  586. package/rcf/test-suites/ts-150.json +86 -0
  587. package/rcf/test-suites/ts-160.json +78 -0
  588. package/rcf/user-stories/us-10001.json +23 -0
  589. package/rcf/user-stories/us-10101.json +23 -0
  590. package/rcf/user-stories/us-10201.json +23 -0
  591. package/rcf/user-stories/us-10301.json +23 -0
  592. package/rcf/user-stories/us-10401.json +23 -0
  593. package/rcf/user-stories/us-10601.json +23 -0
  594. package/rcf/user-stories/us-10701.json +23 -0
  595. package/rcf/user-stories/us-10801.json +23 -0
  596. package/rcf/user-stories/us-11001.json +23 -0
  597. package/rcf/user-stories/us-11101.json +23 -0
  598. package/rcf/user-stories/us-11102.json +24 -0
  599. package/rcf/user-stories/us-11201.json +23 -0
  600. package/rcf/user-stories/us-11202.json +23 -0
  601. package/rcf/user-stories/us-11301.json +24 -0
  602. package/rcf/user-stories/us-11401.json +23 -0
  603. package/rcf/user-stories/us-11402.json +23 -0
  604. package/rcf/user-stories/us-11501.json +23 -0
  605. package/rcf/user-stories/us-12001.json +23 -0
  606. package/rcf/user-stories/us-12101.json +23 -0
  607. package/rcf/user-stories/us-12102.json +23 -0
  608. package/rcf/user-stories/us-12201.json +23 -0
  609. package/rcf/user-stories/us-12202.json +24 -0
  610. package/rcf/user-stories/us-12301.json +23 -0
  611. package/rcf/user-stories/us-12401.json +23 -0
  612. package/rcf/user-stories/us-12402.json +23 -0
  613. package/rcf/user-stories/us-12501.json +23 -0
  614. package/rcf/user-stories/us-13001.json +23 -0
  615. package/rcf/user-stories/us-13002.json +23 -0
  616. package/rcf/user-stories/us-13101.json +23 -0
  617. package/rcf/user-stories/us-13102.json +23 -0
  618. package/rcf/user-stories/us-13201.json +23 -0
  619. package/rcf/user-stories/us-13301.json +23 -0
  620. package/rcf/user-stories/us-13302.json +23 -0
  621. package/rcf/user-stories/us-13401.json +23 -0
  622. package/rcf/user-stories/us-5001.json +27 -0
  623. package/rcf/user-stories/us-5002.json +27 -0
  624. package/rcf/user-stories/us-5101.json +36 -0
  625. package/rcf/user-stories/us-5102.json +27 -0
  626. package/rcf/user-stories/us-5201.json +36 -0
  627. package/rcf/user-stories/us-5301.json +27 -0
  628. package/rcf/user-stories/us-5302.json +27 -0
  629. package/rcf/user-stories/us-5401.json +27 -0
  630. package/rcf/user-stories/us-6001.json +27 -0
  631. package/rcf/user-stories/us-6002.json +27 -0
  632. package/rcf/user-stories/us-6003.json +27 -0
  633. package/rcf/user-stories/us-6101.json +36 -0
  634. package/rcf/user-stories/us-6201.json +36 -0
  635. package/rcf/user-stories/us-6301.json +36 -0
  636. package/rcf/user-stories/us-6302.json +27 -0
  637. package/rcf/user-stories/us-7001.json +27 -0
  638. package/rcf/user-stories/us-7002.json +27 -0
  639. package/rcf/user-stories/us-7101.json +27 -0
  640. package/rcf/user-stories/us-7201.json +27 -0
  641. package/rcf/user-stories/us-7301.json +27 -0
  642. package/rcf/user-stories/us-7401.json +27 -0
  643. package/rcf/user-stories/us-7402.json +27 -0
  644. package/rcf/user-stories/us-7501.json +27 -0
  645. package/rcf/user-stories/us-7601.json +27 -0
  646. package/rcf/user-stories/us-7701.json +27 -0
  647. package/rcf/user-stories/us-8001.json +24 -0
  648. package/rcf/user-stories/us-8002.json +24 -0
  649. package/rcf/user-stories/us-8003.json +24 -0
  650. package/rcf/user-stories/us-8101.json +24 -0
  651. package/rcf/user-stories/us-8201.json +24 -0
  652. package/rcf/user-stories/us-8301.json +24 -0
  653. package/rcf/user-stories/us-8401.json +24 -0
  654. package/rcf/user-stories/us-8501.json +24 -0
  655. package/rcf/user-stories/us-8502.json +24 -0
  656. package/rcf/user-stories/us-8601.json +24 -0
  657. package/rcf/user-stories/us-8602.json +24 -0
  658. package/rcf/user-stories/us-9001.json +27 -0
  659. package/rcf/user-stories/us-9002.json +27 -0
  660. package/rcf/user-stories/us-9101.json +27 -0
  661. package/rcf/user-stories/us-9102.json +27 -0
  662. package/rcf/user-stories/us-9201.json +27 -0
  663. package/rcf/user-stories/us-9301.json +27 -0
  664. package/rcf/user-stories/us-9302.json +27 -0
  665. package/rcf/user-stories/us-9401.json +27 -0
  666. package/releases/releases.yaml +21 -1
@@ -0,0 +1,20 @@
1
+ {
2
+ "adrId": "ADR-3803-deploy-hetzner-server-provisioning-tool",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Provisioning tool: hcloud CLI default, raw Hetzner Cloud API alternative, Terraform rejected",
8
+ "context": "An applying project needs a documented answer to how the provisioner shells out to Hetzner Cloud. Options considered: hcloud CLI (small, self-documenting, JSON output on --output json, per https://github.com/hetznercloud/cli); the raw Hetzner Cloud REST API (no CLI dependency, useful when the target host cannot install a binary, per https://docs.hetzner.cloud/); Terraform (declarative, adds a second toolchain and a state file). Baz ruling 2026-09-07 09:50Z on decision 18 of the round-7 summary: hcloud not Terraform. Terraform adds a second toolchain and a state file that nobody in the estate needs.",
9
+ "decision": "The shipped v1.0.0 default is the hcloud CLI as the provisioning tool. Alternative elicited: raw-api (the provisioner fetches the Hetzner Cloud REST API directly). Terraform is a rejected alternative recorded in the blueprint README section 9 rejected-alternatives. elicited: true. recommendedDefault: hcloud. Standards trace clause: Hetzner Cloud CLI documented resource verbs per hetzner-round-7-spec-2026-09-07.md.",
10
+ "consequences": "The applying project installs the hcloud binary on the operator laptop and every CI runner that provisions. The raw-api fork sits behind the same provisioner facade so no consumer code diverges. The provisioner-facade sole-reader guarantee holds on both paths.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "id": "terraform",
14
+ "summary": "Terraform declaring the Hetzner resources with the hetznercloud provider.",
15
+ "rejectedBecause": "Adds a second toolchain and a state file. The state file becomes a new source of truth to reconcile with git and with the live account. Baz ruling 2026-09-07 09:50Z."
16
+ }
17
+ ],
18
+ "createdAt": "2026-09-08T00:00:00.000Z",
19
+ "updatedAt": "2026-09-08T00:00:00.000Z"
20
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "adrId": "ADR-3804-deploy-hetzner-server-hardening-baseline",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Hardening baseline: SSH key-only, root disabled, UFW default-deny, DOCKER-USER chain, fail2ban, unattended-upgrades",
8
+ "context": "Every Hetzner server exposed to the public internet needs a minimum hardening set. Cloud-init landing the set at first boot is the operator-friendly shape: declarative, reproducible, kept in git per https://cloudinit.readthedocs.io/en/latest/. The Debian unattended-upgrades wiki (https://wiki.debian.org/UnattendedUpgrades) documents the auto-update mechanism the baseline turns on; the estate baseline at operator/knowledge/systems/environments/hetzner-cloud/overview.md Security posture baseline names the exact block set. The Hetzner community basic-cloud-config tutorial (https://community.hetzner.com/tutorials/basic-cloud-config) shows the vendor-recommended shape.",
9
+ "decision": "The shipped v1.0.0 hardening baseline is the six blocks: SSH key-only (PasswordAuthentication no), root disabled (PermitRootLogin no), UFW default-deny incoming with 22, 80 and 443 permitted per the manifest firewall shape, DOCKER-USER iptables chain wired to drop non-conforming egress, fail2ban SSH jail, unattended-upgrades on for security updates. The render-lint probe (cloud-init-render-lint.mjs) asserts every block appears in the rendered cloud-init YAML; the SIMULATE_HARDENING_DRIFT mutation removes a block and the probe FAILS naming the missing line. recommendedDefault: true. Standards trace clause: Debian unattended-upgrades wiki AND the estate baseline at operator/knowledge/systems/environments/hetzner-cloud/overview.md Security posture baseline per hetzner-round-7-spec-2026-09-07.md.",
10
+ "consequences": "The applying project inherits the six-block baseline on every provisioned server. Deviation costs a fresh ADR at the applying project level. The baseline is text plus a rendered template; extension (extra ssh key, extra fail2ban jail) sits on top without rewriting.",
11
+ "alternativesConsidered": [],
12
+ "createdAt": "2026-09-08T00:00:00.000Z",
13
+ "updatedAt": "2026-09-08T00:00:00.000Z"
14
+ }
@@ -0,0 +1,64 @@
1
+ // Probe: cloud-init render lint.
2
+ //
3
+ // anchorAcId: AC-37104-1. accountBound: false.
4
+ //
5
+ // Renders the shipped cloud-init template against the fixture manifest at
6
+ // packages/rcf-lite/test/fixtures/hetzner-throwaway-server/hetzner/servers/ci-throwaway.json
7
+ // via the fixture's src/cloud-init-renderer.mjs. Asserts the six hardening
8
+ // baseline blocks appear in the rendered YAML.
9
+ //
10
+ // SIMULATE_HARDENING_DRIFT=true removes the unattended-upgrades write-file
11
+ // block from the rendered YAML; the probe FAILS naming the missing line
12
+ // per hetzner-round-7-spec-2026-09-07.md section 3.4 lesson 4.
13
+
14
+ import { readFile } from 'node:fs/promises';
15
+ import { resolve } from 'node:path';
16
+ import { FIXTURE_DIR } from './probe-utils.mjs';
17
+
18
+ export const anchorAcId = 'AC-37104-1';
19
+ export const accountBound = false;
20
+
21
+ export default async function runProbe() {
22
+ const rendererPath = resolve(FIXTURE_DIR, 'src/cloud-init-renderer.mjs');
23
+ const manifestPath = resolve(FIXTURE_DIR, 'hetzner/servers/ci-throwaway.json');
24
+ const { renderCloudInit, BASELINE_BLOCKS } = await import(rendererPath);
25
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
26
+ const rendered = await renderCloudInit(manifest);
27
+ const missing = [];
28
+ const present = [];
29
+ for (const block of BASELINE_BLOCKS) {
30
+ const ok = block.markers.every((m) => rendered.includes(m));
31
+ if (ok) {
32
+ present.push(block.id);
33
+ } else {
34
+ const missingMarkers = block.markers.filter((m) => !rendered.includes(m));
35
+ missing.push({ id: block.id, label: block.label, missingMarkers });
36
+ }
37
+ }
38
+ const results = [];
39
+ if (missing.length > 0) {
40
+ for (const m of missing) {
41
+ results.push({
42
+ anchorAcId,
43
+ verdict: 'fail',
44
+ detail: `cloud-init hardening baseline block "${m.label}" is missing from the rendered YAML (missing markers: ${m.missingMarkers.join(', ')}).`,
45
+ });
46
+ }
47
+ } else {
48
+ results.push({
49
+ anchorAcId,
50
+ verdict: 'pass',
51
+ detail: `all ${BASELINE_BLOCKS.length} baseline blocks present in the rendered YAML: ${present.join(', ')}.`,
52
+ });
53
+ }
54
+ return {
55
+ results,
56
+ extra: {
57
+ manifestName: manifest.name,
58
+ renderedByteLength: rendered.length,
59
+ presentBlocks: present,
60
+ missingBlocks: missing.map((m) => m.id),
61
+ mutationActive: process.env.SIMULATE_HARDENING_DRIFT === 'true',
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,116 @@
1
+ // Probe: hcloud dry-run mock.
2
+ //
3
+ // anchorAcId: AC-37101-1 (provisioner facade sole reader + provisionerReady
4
+ // on boot; also covers AC-37109-1 event-secrecy across the lifecycle).
5
+ // accountBound: false.
6
+ //
7
+ // Drives the provisioner facade with the fixture's ci-throwaway manifest
8
+ // against the mocked hcloud shim (src/hcloud-mock.mjs). Asserts:
9
+ // - the facade opens on ready() and fires provisionerReady with a
10
+ // metadata-only payload {tool, apiHost}.
11
+ // - createServer parses the mocked JSON stdout and emits
12
+ // hetznerServerProvisioned with id, primaryIpv4, location=fsn1,
13
+ // serverType=cx23.
14
+ // - takeSnapshot fires hetznerSnapshotTaken with snapshot id and time.
15
+ // - destroyServer fires hetznerServerDestroyed with the id.
16
+ // - no event body across the run carries the token, an ssh private-key
17
+ // marker, or the rendered user-data bytes (event-secrecy check).
18
+ //
19
+ // Mutations:
20
+ // SIMULATE_JSON_PARSE_STRIP=true has hcloud-mock.mjs return non-JSON
21
+ // stdout so JSON.parse throws and the probe FAILS.
22
+ // SIMULATE_EVENT_SECRECY_LEAK=true has provisioner-facade.mjs inject
23
+ // the token into the hetznerServerProvisioned payload; the event-
24
+ // secrecy scan FAILS with a defensive-fake finding.
25
+
26
+ import { readFile } from 'node:fs/promises';
27
+ import { resolve } from 'node:path';
28
+ import { FIXTURE_DIR } from './probe-utils.mjs';
29
+
30
+ export const anchorAcId = 'AC-37101-1';
31
+ export const accountBound = false;
32
+
33
+ const TOKEN = 'test-fixture-token-000000000000000000000000000000000000000000';
34
+ const USER_DATA_MARKER = 'unattended-upgrades on for security updates';
35
+ const PRIVATE_KEY_MARKER = 'BEGIN OPENSSH PRIVATE KEY';
36
+
37
+ export default async function runProbe() {
38
+ const facadePath = resolve(FIXTURE_DIR, 'src/provisioner-facade.mjs');
39
+ const manifestPath = resolve(FIXTURE_DIR, 'hetzner/servers/ci-throwaway.json');
40
+ const { createProvisionerFacade, createEventLog } = await import(facadePath);
41
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
42
+ const { events, eventSink } = createEventLog();
43
+ const facade = createProvisionerFacade({
44
+ token: TOKEN,
45
+ tool: 'hcloud',
46
+ apiHost: 'https://api.hetzner.cloud/v1',
47
+ eventSink,
48
+ });
49
+ const results = [];
50
+ try {
51
+ await facade.ready();
52
+ const server = await facade.createServer(manifest);
53
+ const snapshot = await facade.takeSnapshot({ id: server.id, name: manifest.name }, `${manifest.name}-${new Date().toISOString()}`);
54
+ await facade.destroyServer(server);
55
+ const provisionerReady = events.find((e) => e.event === 'provisionerReady');
56
+ const provisioned = events.find((e) => e.event === 'hetznerServerProvisioned');
57
+ const snapped = events.find((e) => e.event === 'hetznerSnapshotTaken');
58
+ const destroyed = events.find((e) => e.event === 'hetznerServerDestroyed');
59
+ if (!provisionerReady) {
60
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'fail', detail: 'provisionerReady event did not fire on ready().' });
61
+ } else if (provisionerReady.tool !== 'hcloud' || provisionerReady.apiHost !== 'https://api.hetzner.cloud/v1') {
62
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'fail', detail: `provisionerReady payload shape wrong: ${JSON.stringify(provisionerReady)}` });
63
+ } else {
64
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'pass', detail: 'provisionerReady fired with metadata-only {tool, apiHost} payload.' });
65
+ }
66
+ if (!provisioned || !provisioned.id || !provisioned.primaryIpv4 || provisioned.location !== 'fsn1' || provisioned.serverType !== 'cx23') {
67
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'fail', detail: `hetznerServerProvisioned event missing or malformed: ${JSON.stringify(provisioned)}` });
68
+ } else {
69
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'pass', detail: `hetznerServerProvisioned fired with id=${provisioned.id} primaryIpv4=${provisioned.primaryIpv4} location=fsn1 serverType=cx23.` });
70
+ }
71
+ if (!snapped || !snapped.snapshotId || !snapped.wallClockTime) {
72
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'fail', detail: `hetznerSnapshotTaken event missing or malformed: ${JSON.stringify(snapped)}` });
73
+ }
74
+ if (!destroyed || !destroyed.id) {
75
+ results.push({ anchorAcId: 'AC-37101-1', verdict: 'fail', detail: `hetznerServerDestroyed event missing or malformed: ${JSON.stringify(destroyed)}` });
76
+ }
77
+ // Event-secrecy scan across every event body.
78
+ const leaks = [];
79
+ for (const e of events) {
80
+ const body = JSON.stringify(e);
81
+ if (body.includes(TOKEN)) leaks.push({ event: e.event, marker: 'HETZNER_ACCOUNT_API_KEY value' });
82
+ if (body.includes(PRIVATE_KEY_MARKER)) leaks.push({ event: e.event, marker: 'ssh private key' });
83
+ if (body.includes(USER_DATA_MARKER)) leaks.push({ event: e.event, marker: 'rendered user-data bytes' });
84
+ }
85
+ if (leaks.length > 0) {
86
+ results.push({
87
+ anchorAcId: 'AC-37109-1',
88
+ verdict: 'fail',
89
+ detail: `event-secrecy leak: ${leaks.map((l) => `${l.event} carries ${l.marker}`).join('; ')} (defensive-fake finding; check for a SIMULATE_EVENT_SECRECY_LEAK mutation).`,
90
+ });
91
+ } else {
92
+ results.push({
93
+ anchorAcId: 'AC-37109-1',
94
+ verdict: 'pass',
95
+ detail: `event-secrecy scan across ${events.length} event bodies found no leak of the token, ssh private key, or user-data content.`,
96
+ });
97
+ }
98
+ } catch (err) {
99
+ results.push({
100
+ anchorAcId: 'AC-37101-1',
101
+ verdict: 'fail',
102
+ detail: `provisioner facade lifecycle threw: ${err.message} (probable cause: mocked hcloud stdout is not JSON; check for a SIMULATE_JSON_PARSE_STRIP mutation).`,
103
+ });
104
+ }
105
+ return {
106
+ results,
107
+ extra: {
108
+ eventCount: events.length,
109
+ events: events.map((e) => ({ event: e.event, keys: Object.keys(e).sort() })),
110
+ mutations: {
111
+ jsonParseStrip: process.env.SIMULATE_JSON_PARSE_STRIP === 'true',
112
+ eventSecrecyLeak: process.env.SIMULATE_EVENT_SECRECY_LEAK === 'true',
113
+ },
114
+ },
115
+ };
116
+ }
@@ -0,0 +1,180 @@
1
+ // Probe: manifest schema validate.
2
+ //
3
+ // anchorAcId: AC-37102-1 (also covers AC-37106-1 firewall shape and
4
+ // AC-37107-1 snapshot cadence enum via the same schema). accountBound: false.
5
+ //
6
+ // Validates every hetzner/servers/*.json under the shared throwaway-server
7
+ // fixture against blueprints/deploy-hetzner-server/contributions/schemas/
8
+ // hetzner-server.schema.json. A lightweight in-process draft-07 subset
9
+ // validator ships with the probe so the shelf gains no runtime dependency
10
+ // on ajv or similar. See validate() at the bottom.
11
+ //
12
+ // SIMULATE_MANIFEST_INVALID=true replaces the manifest's location with an
13
+ // unknown token and the probe FAILS naming the offending field per
14
+ // hetzner-round-7-spec-2026-09-07.md section 3.4 lesson 4.
15
+
16
+ import { readFile } from 'node:fs/promises';
17
+ import { readManifestFiles, SCHEMA_PATH } from './probe-utils.mjs';
18
+
19
+ export const anchorAcId = 'AC-37102-1';
20
+ export const accountBound = false;
21
+
22
+ export default async function runProbe() {
23
+ const schema = JSON.parse(await readFile(SCHEMA_PATH, 'utf8'));
24
+ const { present, files } = await readManifestFiles();
25
+ if (!present) {
26
+ return {
27
+ results: [{
28
+ anchorAcId,
29
+ verdict: 'fail',
30
+ detail: 'fixture hetzner/servers/ holds zero JSON files; ship at least ci-throwaway.json.',
31
+ }],
32
+ };
33
+ }
34
+ const results = [];
35
+ const mutationActive = process.env.SIMULATE_MANIFEST_INVALID === 'true';
36
+ for (const f of files) {
37
+ let doc;
38
+ try {
39
+ doc = JSON.parse(f.text);
40
+ } catch (err) {
41
+ results.push({
42
+ anchorAcId,
43
+ verdict: 'fail',
44
+ detail: `manifest ${f.name} does not parse: ${err.message}`,
45
+ });
46
+ continue;
47
+ }
48
+ if (mutationActive) doc = { ...doc, location: 'mars1' };
49
+ const errors = validate(schema, doc, `#/${f.name}`);
50
+ if (errors.length > 0) {
51
+ for (const e of errors) {
52
+ const anchor = anchorForError(e);
53
+ results.push({
54
+ anchorAcId: anchor,
55
+ verdict: 'fail',
56
+ detail: `manifest ${f.name} schema violation at ${e.path}: ${e.message}`,
57
+ });
58
+ }
59
+ } else {
60
+ results.push({
61
+ anchorAcId,
62
+ verdict: 'pass',
63
+ detail: `manifest ${f.name} validates against hetzner-server.schema.json (nine required fields, firewall rule shape, snapshotCadence enum all satisfied).`,
64
+ });
65
+ }
66
+ }
67
+ return {
68
+ results,
69
+ extra: {
70
+ manifestCount: files.length,
71
+ manifestNames: files.map((f) => f.name),
72
+ mutationActive,
73
+ },
74
+ };
75
+ }
76
+
77
+ function anchorForError(err) {
78
+ if (err.field === 'firewallRules' || (err.path || '').includes('firewallRules')) {
79
+ return 'AC-37106-1';
80
+ }
81
+ if (err.field === 'snapshotCadence' || (err.path || '').includes('snapshotCadence')) {
82
+ return 'AC-37107-1';
83
+ }
84
+ return 'AC-37102-1';
85
+ }
86
+
87
+ // Draft-07 subset validator: required, type, enum, minLength, minItems,
88
+ // additionalProperties, items, properties, required-on-array-items.
89
+ // Extra rule: on firewallRules the ssh rule sourceIps must NOT include
90
+ // 0.0.0.0/0 (per TAC-3804); handled after schema validation.
91
+ export function validate(schema, doc, pathPrefix = '#') {
92
+ const errors = [];
93
+ walk(schema, doc, pathPrefix, errors);
94
+ if (Array.isArray(doc.firewallRules)) {
95
+ const ssh = doc.firewallRules.find((r) => r && r.name === 'ssh');
96
+ if (ssh && Array.isArray(ssh.sourceIps) && ssh.sourceIps.some((s) => s === '0.0.0.0/0' || s === '::/0')) {
97
+ errors.push({
98
+ path: `${pathPrefix}/firewallRules/ssh/sourceIps`,
99
+ field: 'firewallRules',
100
+ message: 'ssh rule sourceIps must not include 0.0.0.0/0 or ::/0; use an operator-nominated set per TAC-3804.',
101
+ });
102
+ }
103
+ const need = ['ssh', 'http', 'https'];
104
+ for (const n of need) {
105
+ if (!doc.firewallRules.some((r) => r && r.name === n)) {
106
+ errors.push({
107
+ path: `${pathPrefix}/firewallRules`,
108
+ field: 'firewallRules',
109
+ message: `firewallRules is missing the required ${n} rule per TAC-3804.`,
110
+ });
111
+ }
112
+ }
113
+ }
114
+ return errors;
115
+ }
116
+
117
+ function walk(schema, value, path, errors) {
118
+ if (!schema || typeof schema !== 'object') return;
119
+ if (Array.isArray(schema.required) && typeof value === 'object' && value !== null && !Array.isArray(value)) {
120
+ for (const k of schema.required) {
121
+ if (!(k in value)) {
122
+ errors.push({ path: `${path}/${k}`, field: k, message: `missing required field ${k}` });
123
+ }
124
+ }
125
+ }
126
+ if (schema.type) {
127
+ const typeOk = checkType(schema.type, value);
128
+ if (!typeOk) {
129
+ errors.push({ path, message: `expected type ${JSON.stringify(schema.type)}, got ${jsType(value)}` });
130
+ return;
131
+ }
132
+ }
133
+ if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
134
+ errors.push({ path, message: `value ${JSON.stringify(value)} is not in enum ${JSON.stringify(schema.enum)}` });
135
+ }
136
+ if (typeof schema.minLength === 'number' && typeof value === 'string' && value.length < schema.minLength) {
137
+ errors.push({ path, message: `string shorter than minLength ${schema.minLength}` });
138
+ }
139
+ if (typeof schema.minItems === 'number' && Array.isArray(value) && value.length < schema.minItems) {
140
+ errors.push({ path, message: `array shorter than minItems ${schema.minItems}` });
141
+ }
142
+ if (schema.type === 'object' && schema.additionalProperties === false && value && typeof value === 'object') {
143
+ const known = new Set(Object.keys(schema.properties || {}));
144
+ for (const k of Object.keys(value)) {
145
+ if (!known.has(k)) {
146
+ errors.push({ path: `${path}/${k}`, field: k, message: `unknown field ${k} (additionalProperties false)` });
147
+ }
148
+ }
149
+ }
150
+ if (schema.properties && value && typeof value === 'object' && !Array.isArray(value)) {
151
+ for (const [k, sub] of Object.entries(schema.properties)) {
152
+ if (k in value) walk(sub, value[k], `${path}/${k}`, errors);
153
+ }
154
+ }
155
+ if (schema.items && Array.isArray(value)) {
156
+ value.forEach((v, i) => walk(schema.items, v, `${path}/${i}`, errors));
157
+ }
158
+ }
159
+
160
+ function checkType(type, value) {
161
+ const types = Array.isArray(type) ? type : [type];
162
+ return types.some((t) => matchType(t, value));
163
+ }
164
+
165
+ function matchType(t, value) {
166
+ if (t === 'null') return value === null;
167
+ if (t === 'array') return Array.isArray(value);
168
+ if (t === 'object') return typeof value === 'object' && value !== null && !Array.isArray(value);
169
+ if (t === 'integer') return Number.isInteger(value);
170
+ if (t === 'number') return typeof value === 'number';
171
+ if (t === 'string') return typeof value === 'string';
172
+ if (t === 'boolean') return typeof value === 'boolean';
173
+ return false;
174
+ }
175
+
176
+ function jsType(v) {
177
+ if (v === null) return 'null';
178
+ if (Array.isArray(v)) return 'array';
179
+ return typeof v;
180
+ }
@@ -0,0 +1,106 @@
1
+ // Shared helpers for deploy-hetzner-server probes.
2
+ //
3
+ // Runtime-dependency posture:
4
+ // - cloud-init-render-lint, manifest-schema-validate and
5
+ // hcloud-dry-run-mock run in-process against the shared throwaway-
6
+ // server fixture at packages/rcf-lite/test/fixtures/hetzner-throwaway-
7
+ // server/. No real API call fires; hcloud is mocked via
8
+ // src/hcloud-mock.mjs so the shim never crosses the process boundary.
9
+ // - real-account-* probes call the fixture's provision.mjs / destroy.mjs
10
+ // / snapshot verbs. Without CI_HAS_HETZNER_ACCOUNT they record
11
+ // accountBoundSkipped: true and the aggregate flips to pass per
12
+ // hetzner-round-7-spec-2026-09-07.md section 3.5.
13
+
14
+ import { mkdir, writeFile, readFile, readdir } from 'node:fs/promises';
15
+ import { dirname, resolve, join } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const HERE = dirname(fileURLToPath(import.meta.url));
19
+ export const PROJECT_ROOT = resolve(HERE, '..', '..', '..', '..');
20
+ export const FIXTURE_DIR = resolve(
21
+ PROJECT_ROOT,
22
+ 'packages/rcf-lite/test/fixtures/hetzner-throwaway-server',
23
+ );
24
+ export const MANIFEST_DIR = resolve(FIXTURE_DIR, 'hetzner/servers');
25
+ export const SCHEMA_PATH = resolve(HERE, '..', 'schemas', 'hetzner-server.schema.json');
26
+ export const REPORT_DIR = resolve(
27
+ PROJECT_ROOT,
28
+ '.rcf/reports/blueprints/deploy-hetzner-server',
29
+ );
30
+
31
+ export function aggregate(results) {
32
+ if (results.some((r) => r.verdict === 'fail')) return 'fail';
33
+ if (results.some((r) => r.verdict === 'warn')) return 'warn';
34
+ return 'pass';
35
+ }
36
+
37
+ export function isSkipped(results) {
38
+ return results.length > 0 && results.every((r) => r.accountBoundSkipped === true);
39
+ }
40
+
41
+ export async function writeReport({ probeName, engine, results, extra }) {
42
+ await mkdir(REPORT_DIR, { recursive: true });
43
+ const rawVerdict = aggregate(results);
44
+ const aggregateVerdict = isSkipped(results) ? 'pass' : rawVerdict;
45
+ const report = {
46
+ slug: 'deploy-hetzner-server',
47
+ probeName,
48
+ runAt: new Date().toISOString(),
49
+ engine,
50
+ results,
51
+ aggregateVerdict,
52
+ ...(extra ?? {}),
53
+ };
54
+ const path = resolve(REPORT_DIR, `${probeName}.json`);
55
+ await writeFile(path, JSON.stringify(report, null, 2) + '\n', 'utf8');
56
+ return { report, path };
57
+ }
58
+
59
+ export async function runShim(probeName, engine, mainFn) {
60
+ try {
61
+ const outcome = (await mainFn()) ?? { results: [] };
62
+ const { results, extra } = outcome;
63
+ const { report, path } = await writeReport({ probeName, engine, results, extra });
64
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
65
+ process.stdout.write(`report written to ${path}\n`);
66
+ if (report.aggregateVerdict === 'fail') process.exitCode = 1;
67
+ } catch (err) {
68
+ const results = [{
69
+ anchorAcId: 'unknown',
70
+ verdict: 'fail',
71
+ detail: `probe threw: ${err && err.message ? err.message : String(err)}`,
72
+ }];
73
+ const { report, path } = await writeReport({ probeName, engine, results });
74
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
75
+ process.stderr.write(`probe error: ${err && err.stack ? err.stack : String(err)}\n`);
76
+ process.stderr.write(`report written to ${path}\n`);
77
+ process.exitCode = 1;
78
+ }
79
+ }
80
+
81
+ export async function readManifestFiles(dir = MANIFEST_DIR) {
82
+ let entries;
83
+ try {
84
+ entries = (await readdir(dir))
85
+ .filter((f) => f.endsWith('.json'))
86
+ .sort();
87
+ } catch (err) {
88
+ return { present: false, entries: [], files: [], error: err.message };
89
+ }
90
+ const files = [];
91
+ for (const name of entries) {
92
+ const p = join(dir, name);
93
+ const text = await readFile(p, 'utf8');
94
+ files.push({ name, path: p, text });
95
+ }
96
+ return { present: entries.length > 0, entries, files };
97
+ }
98
+
99
+ export function accountBoundSkippedResult(anchorAcId, note) {
100
+ return {
101
+ anchorAcId,
102
+ verdict: 'skipped',
103
+ accountBoundSkipped: true,
104
+ detail: `accountBound: CI_HAS_HETZNER_ACCOUNT unset; ${note}`,
105
+ };
106
+ }
@@ -0,0 +1,63 @@
1
+ // Probe: real-account cloud-init hardened.
2
+ //
3
+ // anchorAcId: AC-37105-1. accountBound: true.
4
+ //
5
+ // Without CI_HAS_HETZNER_ACCOUNT the probe records
6
+ // accountBoundSkipped: true and the aggregate flips to pass.
7
+ //
8
+ // With the env var set the probe:
9
+ // - provisions the throwaway server via the fixture provision.mjs;
10
+ // - shells cloud-init status --wait over ssh to the throwaway server;
11
+ // - runs the six baseline checks over ssh (grep for the marker per
12
+ // baseline block); FAILS naming the first missing block on any
13
+ // defect.
14
+ // - always() calls destroy.mjs regardless of verdict.
15
+
16
+ import { accountBoundSkippedResult, FIXTURE_DIR } from './probe-utils.mjs';
17
+ import { resolve } from 'node:path';
18
+
19
+ export const anchorAcId = 'AC-37105-1';
20
+ export const accountBound = true;
21
+
22
+ export default async function runProbe() {
23
+ if (process.env.CI_HAS_HETZNER_ACCOUNT !== 'true') {
24
+ return {
25
+ results: [accountBoundSkippedResult(anchorAcId, 'real-account cloud-init hardened check skipped; run with CI_HAS_HETZNER_ACCOUNT=true to exercise the six ssh baseline checks against a throwaway cx23.')],
26
+ };
27
+ }
28
+ const provisionPath = resolve(FIXTURE_DIR, 'provision.mjs');
29
+ const destroyPath = resolve(FIXTURE_DIR, 'destroy.mjs');
30
+ const sshCheckPath = resolve(FIXTURE_DIR, 'src/ssh-baseline-check.mjs');
31
+ const { provisionThrowawayServer } = await import(provisionPath);
32
+ const { destroyThrowawayServer } = await import(destroyPath);
33
+ const { runSshBaselineChecks } = await import(sshCheckPath);
34
+ let provisioned;
35
+ try {
36
+ provisioned = await provisionThrowawayServer({ runId: process.env.GITHUB_RUN_ID ?? 'local' });
37
+ const report = await runSshBaselineChecks(provisioned);
38
+ const failed = report.checks.filter((c) => c.verdict === 'fail');
39
+ if (failed.length > 0) {
40
+ return {
41
+ results: failed.map((c) => ({
42
+ anchorAcId,
43
+ verdict: 'fail',
44
+ detail: `cloud-init baseline block "${c.label}" failed on server ${provisioned.id}: ${c.detail}`,
45
+ })),
46
+ };
47
+ }
48
+ return {
49
+ results: [{
50
+ anchorAcId,
51
+ verdict: 'pass',
52
+ detail: `cloud-init status --wait completed on server ${provisioned.id}; all six ssh baseline checks passed.`,
53
+ }],
54
+ extra: report,
55
+ };
56
+ } catch (err) {
57
+ return { results: [{ anchorAcId, verdict: 'fail', detail: `check failed: ${err.message}` }] };
58
+ } finally {
59
+ if (provisioned && provisioned.id) {
60
+ try { await destroyThrowawayServer(provisioned); } catch (_) { /* swept by orphan cron */ }
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,60 @@
1
+ // Probe: real-account snapshot on demand.
2
+ //
3
+ // anchorAcId: AC-37108-1. accountBound: true.
4
+ //
5
+ // Without CI_HAS_HETZNER_ACCOUNT the probe records
6
+ // accountBoundSkipped: true and the aggregate flips to pass.
7
+ //
8
+ // With the env var set the probe:
9
+ // - provisions the throwaway server via provision.mjs;
10
+ // - fires the snapshot verb on the provisioner facade;
11
+ // - asserts hcloud image list --type=snapshot --output json shows a
12
+ // snapshot with the server name and current wall-clock time in the
13
+ // labels;
14
+ // - always() calls destroy.mjs regardless of verdict; the snapshot is
15
+ // deleted as part of destroy.mjs so the run leaves no orphaned
16
+ // image behind.
17
+
18
+ import { accountBoundSkippedResult, FIXTURE_DIR } from './probe-utils.mjs';
19
+ import { resolve } from 'node:path';
20
+
21
+ export const anchorAcId = 'AC-37108-1';
22
+ export const accountBound = true;
23
+
24
+ export default async function runProbe() {
25
+ if (process.env.CI_HAS_HETZNER_ACCOUNT !== 'true') {
26
+ return {
27
+ results: [accountBoundSkippedResult(anchorAcId, 'real-account snapshot on-demand skipped; run with CI_HAS_HETZNER_ACCOUNT=true to exercise the hcloud image create-image path.')],
28
+ };
29
+ }
30
+ const provisionPath = resolve(FIXTURE_DIR, 'provision.mjs');
31
+ const destroyPath = resolve(FIXTURE_DIR, 'destroy.mjs');
32
+ const snapshotPath = resolve(FIXTURE_DIR, 'src/snapshot-verb.mjs');
33
+ const { provisionThrowawayServer } = await import(provisionPath);
34
+ const { destroyThrowawayServer } = await import(destroyPath);
35
+ const { takeAndVerifySnapshot } = await import(snapshotPath);
36
+ let provisioned;
37
+ try {
38
+ provisioned = await provisionThrowawayServer({ runId: process.env.GITHUB_RUN_ID ?? 'local' });
39
+ const outcome = await takeAndVerifySnapshot(provisioned);
40
+ if (!outcome.snapshotId) {
41
+ return {
42
+ results: [{ anchorAcId, verdict: 'fail', detail: `snapshot verb ran but no matching snapshot appeared in hcloud image list.` }],
43
+ };
44
+ }
45
+ return {
46
+ results: [{
47
+ anchorAcId,
48
+ verdict: 'pass',
49
+ detail: `snapshot ${outcome.snapshotId} tagged with server name ${provisioned.name} and time ${outcome.wallClockTime} on server ${provisioned.id}.`,
50
+ }],
51
+ extra: outcome,
52
+ };
53
+ } catch (err) {
54
+ return { results: [{ anchorAcId, verdict: 'fail', detail: `snapshot verb threw: ${err.message}` }] };
55
+ } finally {
56
+ if (provisioned && provisioned.id) {
57
+ try { await destroyThrowawayServer(provisioned); } catch (_) { /* swept by orphan cron */ }
58
+ }
59
+ }
60
+ }