rcf-lite 0.24.0 → 0.26.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 (313) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/blueprints/application-account-settings/docs/topics.md +4 -1
  3. package/blueprints/application-admin-console/docs/topics.md +4 -1
  4. package/blueprints/application-api-rest/docs/topics.md +4 -1
  5. package/blueprints/application-charts/docs/topics.md +4 -1
  6. package/blueprints/application-dashboard/docs/topics.md +4 -1
  7. package/blueprints/application-datatable/docs/topics.md +4 -1
  8. package/blueprints/application-empty-error-states/docs/topics.md +4 -1
  9. package/blueprints/application-error-handling/docs/topics.md +4 -1
  10. package/blueprints/application-file-upload/docs/topics.md +4 -1
  11. package/blueprints/application-forms-wizard/docs/topics.md +4 -1
  12. package/blueprints/application-notifications-in-app/docs/topics.md +4 -1
  13. package/blueprints/application-onboarding-tour/docs/topics.md +4 -1
  14. package/blueprints/application-spa/docs/topics.md +4 -1
  15. package/blueprints/delivery-ci-workflows/docs/topics.md +4 -1
  16. package/blueprints/deploy-cloudflare-workers/docs/topics.md +4 -1
  17. package/blueprints/deploy-hetzner-server/CHANGELOG.md +55 -0
  18. package/blueprints/deploy-hetzner-server/README.md +202 -0
  19. package/blueprints/deploy-hetzner-server/blueprint.json +233 -0
  20. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +14 -0
  21. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +14 -0
  22. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +20 -0
  23. package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +14 -0
  24. package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +71 -0
  25. package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +164 -0
  26. package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +182 -0
  27. package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +112 -0
  28. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +64 -0
  29. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-snapshot-on-demand.mjs +60 -0
  30. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-throwaway-server-provision.mjs +58 -0
  31. package/blueprints/deploy-hetzner-server/contributions/probes/run-cloud-init-render-lint.mjs +9 -0
  32. package/blueprints/deploy-hetzner-server/contributions/probes/run-hcloud-dry-run-mock.mjs +9 -0
  33. package/blueprints/deploy-hetzner-server/contributions/probes/run-manifest-schema-validate.mjs +9 -0
  34. package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-cloud-init-hardened.mjs +9 -0
  35. package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-snapshot-on-demand.mjs +9 -0
  36. package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-throwaway-server-provision.mjs +9 -0
  37. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-001.json +18 -0
  38. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-002.json +18 -0
  39. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-003.json +18 -0
  40. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-004.json +18 -0
  41. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-005.json +18 -0
  42. package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-006.json +18 -0
  43. package/blueprints/deploy-hetzner-server/contributions/schemas/hetzner-server.schema.json +108 -0
  44. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3801-deploy-hetzner-server-provisioner.json +27 -0
  45. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +26 -0
  46. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +26 -0
  47. package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +25 -0
  48. package/blueprints/deploy-hetzner-server/contributions/templates/cloud-init.yaml.tmpl +113 -0
  49. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37101.json +24 -0
  50. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37102.json +24 -0
  51. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37103.json +25 -0
  52. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37104.json +24 -0
  53. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37105.json +24 -0
  54. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37106.json +25 -0
  55. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37107.json +24 -0
  56. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37108.json +24 -0
  57. package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37109.json +24 -0
  58. package/blueprints/deploy-hetzner-server/docs/topics.md +48 -0
  59. package/blueprints/deploy-hetzner-server/guide/deploy-hetzner-server.md +180 -0
  60. package/blueprints/edge-cloudflare-access/docs/topics.md +4 -1
  61. package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +4 -1
  62. package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +23 -0
  63. package/blueprints/edge-cloudflare-tunnel/README.md +257 -0
  64. package/blueprints/edge-cloudflare-tunnel/blueprint.json +168 -0
  65. package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +23 -0
  66. package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +19 -0
  67. package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +19 -0
  68. package/blueprints/edge-cloudflare-tunnel/contributions/probes/aud-presence-check.mjs +114 -0
  69. package/blueprints/edge-cloudflare-tunnel/contributions/probes/cloudflared-config-lint.mjs +80 -0
  70. package/blueprints/edge-cloudflare-tunnel/contributions/probes/manifest-schema-validate.mjs +237 -0
  71. package/blueprints/edge-cloudflare-tunnel/contributions/probes/probe-utils.mjs +231 -0
  72. package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-connector-healthy.mjs +143 -0
  73. package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-tunnel-hostname-routes.mjs +166 -0
  74. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-aud-presence-check.mjs +4 -0
  75. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-cloudflared-config-lint.mjs +4 -0
  76. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-manifest-schema-validate.mjs +4 -0
  77. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-real-account-connector-healthy.mjs +4 -0
  78. package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-real-account-tunnel-hostname-routes.mjs +4 -0
  79. package/blueprints/edge-cloudflare-tunnel/contributions/probes/tunnel-manifest.schema.json +55 -0
  80. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-001.json +18 -0
  81. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-002.json +18 -0
  82. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-003.json +18 -0
  83. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-004.json +18 -0
  84. package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-005.json +18 -0
  85. package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +26 -0
  86. package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +26 -0
  87. package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +25 -0
  88. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39101.json +24 -0
  89. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39102.json +24 -0
  90. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39103.json +24 -0
  91. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39104.json +24 -0
  92. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39105.json +24 -0
  93. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39106.json +24 -0
  94. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39107.json +24 -0
  95. package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39108.json +24 -0
  96. package/blueprints/edge-cloudflare-tunnel/docs/topics.md +72 -0
  97. package/blueprints/edge-cloudflare-tunnel/guide/edge-cloudflare-tunnel.md +204 -0
  98. package/blueprints/edge-cloudflare-turnstile/docs/topics.md +4 -1
  99. package/blueprints/email-smtp-resend/docs/topics.md +4 -1
  100. package/blueprints/jobs-background/docs/topics.md +4 -1
  101. package/blueprints/messaging-queue-cloudflare/CHANGELOG.md +19 -0
  102. package/blueprints/messaging-queue-cloudflare/README.md +2 -2
  103. package/blueprints/messaging-queue-cloudflare/blueprint.json +1 -1
  104. package/blueprints/messaging-queue-cloudflare/contributions/probes/real-account-concurrency-smoke.mjs +192 -37
  105. package/blueprints/messaging-queue-cloudflare/docs/topics.md +4 -1
  106. package/blueprints/object-storage-s3/CHANGELOG.md +11 -0
  107. package/blueprints/object-storage-s3/README.md +14 -1
  108. package/blueprints/object-storage-s3/blueprint.json +12 -1
  109. package/blueprints/object-storage-s3/contributions/adrs/adr-2905-object-storage-s3-hetzner-object-storage-provider.json +30 -0
  110. package/blueprints/object-storage-s3/contributions/probes/hetzner-object-storage-round-trip.mjs +136 -0
  111. package/blueprints/object-storage-s3/contributions/probes/run-hetzner-object-storage-round-trip.mjs +11 -0
  112. package/blueprints/object-storage-s3/contributions/requirements/object-storage-s3-req-101.json +18 -0
  113. package/blueprints/object-storage-s3/contributions/tacs/tac-2904-object-storage-s3-hetzner-endpoint-helper.json +34 -0
  114. package/blueprints/object-storage-s3/contributions/user-stories/object-storage-s3-us-28110.json +25 -0
  115. package/blueprints/object-storage-s3/docs/topics.md +4 -1
  116. package/blueprints/object-storage-s3/guide/object-storage-s3.md +36 -0
  117. package/blueprints/observability-essentials/docs/topics.md +4 -1
  118. package/blueprints/observability-logging/docs/topics.md +4 -1
  119. package/blueprints/observability-probe-endpoints/docs/topics.md +4 -1
  120. package/blueprints/persistence-data-d1/docs/topics.md +4 -1
  121. package/blueprints/persistence-data-postgres/docs/topics.md +4 -1
  122. package/blueprints/persistence-data-sqlite/docs/topics.md +4 -1
  123. package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +4 -1
  124. package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +15 -0
  125. package/blueprints/platform-cloudflare-durable-objects/blueprint.json +1 -1
  126. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/namespace-facade-ready.mjs +3 -3
  127. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/probe-utils.mjs +28 -14
  128. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/real-account-storage-smoke.mjs +132 -26
  129. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/sole-reader-scan.mjs +40 -39
  130. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/storage-round-trip.mjs +26 -16
  131. package/blueprints/platform-cloudflare-durable-objects/contributions/probes/websocket-hub-broadcast.mjs +31 -34
  132. package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +4 -1
  133. package/blueprints/platform-cloudflare-kv/CHANGELOG.md +40 -1
  134. package/blueprints/platform-cloudflare-kv/README.md +41 -13
  135. package/blueprints/platform-cloudflare-kv/blueprint.json +1 -1
  136. package/blueprints/platform-cloudflare-kv/contributions/probes/cache-aside-hit-then-miss.mjs +26 -26
  137. package/blueprints/platform-cloudflare-kv/contributions/probes/event-secrecy.mjs +49 -31
  138. package/blueprints/platform-cloudflare-kv/contributions/probes/facade-round-trip.mjs +58 -16
  139. package/blueprints/platform-cloudflare-kv/contributions/probes/list-with-prefix.mjs +6 -5
  140. package/blueprints/platform-cloudflare-kv/contributions/probes/real-account-eventual-consistency-smoke.mjs +131 -67
  141. package/blueprints/platform-cloudflare-kv/docs/topics.md +4 -1
  142. package/blueprints/platform-docker-compose-host/CHANGELOG.md +12 -0
  143. package/blueprints/platform-docker-compose-host/README.md +76 -0
  144. package/blueprints/platform-docker-compose-host/blueprint.json +209 -0
  145. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3901-platform-docker-compose-host-contract.json +14 -0
  146. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +14 -0
  147. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +14 -0
  148. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3904-platform-docker-compose-host-log-driver.json +14 -0
  149. package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +108 -0
  150. package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +231 -0
  151. package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +185 -0
  152. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +41 -0
  153. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +36 -0
  154. package/blueprints/platform-docker-compose-host/contributions/probes/run-caddyfile-validate.mjs +5 -0
  155. package/blueprints/platform-docker-compose-host/contributions/probes/run-compose-config-lint.mjs +5 -0
  156. package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-minimal-stack-up.mjs +5 -0
  157. package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-reload-burst.mjs +5 -0
  158. package/blueprints/platform-docker-compose-host/contributions/probes/run-secrets-as-files-scan.mjs +5 -0
  159. package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +116 -0
  160. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-001.json +18 -0
  161. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-002.json +18 -0
  162. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-003.json +18 -0
  163. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-004.json +18 -0
  164. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-005.json +18 -0
  165. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-006.json +18 -0
  166. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +26 -0
  167. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +26 -0
  168. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +26 -0
  169. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +26 -0
  170. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38101.json +24 -0
  171. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38102.json +24 -0
  172. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38103.json +24 -0
  173. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38104.json +24 -0
  174. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38105.json +25 -0
  175. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38106.json +24 -0
  176. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38107.json +24 -0
  177. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38108.json +24 -0
  178. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38109.json +24 -0
  179. package/blueprints/platform-docker-compose-host/docs/topics.md +71 -0
  180. package/blueprints/platform-docker-compose-host/guide/platform-docker-compose-host.md +94 -0
  181. package/blueprints/security-auth-clerk/docs/topics.md +4 -1
  182. package/blueprints/security-auth-keycloak/docs/topics.md +4 -1
  183. package/blueprints/security-auth-magic-link/docs/topics.md +4 -1
  184. package/blueprints/security-auth-oauth2/docs/topics.md +4 -1
  185. package/blueprints/security-secrets-management/docs/topics.md +4 -1
  186. package/fixtures/canary-manifest.json +9 -9
  187. package/guidance/harness-template.md +67 -0
  188. package/guidance/managed/agent-instructions-block.hash +1 -1
  189. package/guidance/managed/agent-instructions-block.md +67 -0
  190. package/package.json +2 -2
  191. package/rcf/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +13 -0
  192. package/rcf/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +13 -0
  193. package/rcf/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +13 -0
  194. package/rcf/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +13 -0
  195. package/rcf/adrs/adr-3901-platform-docker-compose-host-contract.json +13 -0
  196. package/rcf/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +13 -0
  197. package/rcf/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +13 -0
  198. package/rcf/adrs/adr-3904-platform-docker-compose-host-log-driver.json +13 -0
  199. package/rcf/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +13 -0
  200. package/rcf/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +13 -0
  201. package/rcf/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +13 -0
  202. package/rcf/code-nodes/cn-410.json +20 -0
  203. package/rcf/code-nodes/cn-440.json +20 -0
  204. package/rcf/code-nodes/cn-470.json +19 -0
  205. package/rcf/code-nodes/cn-500.json +12 -0
  206. package/rcf/code-nodes/cn-510.json +12 -0
  207. package/rcf/code-nodes/cn-520.json +14 -0
  208. package/rcf/code-nodes/cn-521.json +12 -0
  209. package/rcf/code-nodes/cn-522.json +13 -0
  210. package/rcf/code-nodes/cn-523.json +12 -0
  211. package/rcf/code-nodes/cn-524.json +14 -0
  212. package/rcf/code-nodes/cn-525.json +12 -0
  213. package/rcf/code-nodes/cn-526.json +14 -0
  214. package/rcf/fbs/fbs-130.json +23 -0
  215. package/rcf/fbs/fbs-140.json +23 -0
  216. package/rcf/fbs/fbs-150.json +22 -0
  217. package/rcf/fbs/fbs-160.json +15 -0
  218. package/rcf/fbs/fbs-165.json +15 -0
  219. package/rcf/fbs/fbs-170.json +16 -0
  220. package/rcf/fbs/fbs-171.json +16 -0
  221. package/rcf/fbs/fbs-172.json +15 -0
  222. package/rcf/fbs/fbs-173.json +17 -0
  223. package/rcf/fbs/fbs-174.json +17 -0
  224. package/rcf/requirements/req-110.json +58 -0
  225. package/rcf/requirements/req-111.json +32 -0
  226. package/rcf/requirements/req-112.json +42 -0
  227. package/rcf/requirements/req-113.json +33 -0
  228. package/rcf/requirements/req-114.json +20 -0
  229. package/rcf/requirements/req-115.json +38 -0
  230. package/rcf/requirements/req-120.json +20 -0
  231. package/rcf/requirements/req-121.json +20 -0
  232. package/rcf/requirements/req-122.json +20 -0
  233. package/rcf/requirements/req-123.json +42 -0
  234. package/rcf/requirements/req-124.json +27 -0
  235. package/rcf/requirements/req-125.json +20 -0
  236. package/rcf/requirements/req-130.json +32 -0
  237. package/rcf/requirements/req-131.json +54 -0
  238. package/rcf/requirements/req-132.json +27 -0
  239. package/rcf/requirements/req-133.json +20 -0
  240. package/rcf/requirements/req-134.json +42 -0
  241. package/rcf/requirements/req-140.json +149 -0
  242. package/rcf/requirements/req-145.json +53 -0
  243. package/rcf/requirements/req-150.json +20 -0
  244. package/rcf/requirements/req-151.json +85 -0
  245. package/rcf/requirements/req-152.json +20 -0
  246. package/rcf/requirements/req-153.json +59 -0
  247. package/rcf/requirements/req-154.json +44 -0
  248. package/rcf/tacs/tac-3801-deploy-hetzner-server-provisioner.json +17 -0
  249. package/rcf/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +17 -0
  250. package/rcf/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +17 -0
  251. package/rcf/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +16 -0
  252. package/rcf/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +17 -0
  253. package/rcf/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +17 -0
  254. package/rcf/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +17 -0
  255. package/rcf/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +17 -0
  256. package/rcf/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +17 -0
  257. package/rcf/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +17 -0
  258. package/rcf/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +16 -0
  259. package/rcf/test-suites/ts-071.json +2 -2
  260. package/rcf/test-suites/ts-086.json +2 -2
  261. package/rcf/test-suites/ts-140.json +86 -0
  262. package/rcf/test-suites/ts-150.json +86 -0
  263. package/rcf/test-suites/ts-160.json +78 -0
  264. package/rcf/test-suites/ts-170.json +22 -0
  265. package/rcf/test-suites/ts-175.json +22 -0
  266. package/rcf/test-suites/ts-180.json +30 -0
  267. package/rcf/test-suites/ts-181.json +30 -0
  268. package/rcf/test-suites/ts-182.json +22 -0
  269. package/rcf/test-suites/ts-183.json +38 -0
  270. package/rcf/test-suites/ts-184.json +38 -0
  271. package/rcf/user-stories/us-11001.json +23 -0
  272. package/rcf/user-stories/us-11101.json +23 -0
  273. package/rcf/user-stories/us-11102.json +24 -0
  274. package/rcf/user-stories/us-11201.json +23 -0
  275. package/rcf/user-stories/us-11202.json +23 -0
  276. package/rcf/user-stories/us-11301.json +24 -0
  277. package/rcf/user-stories/us-11401.json +23 -0
  278. package/rcf/user-stories/us-11402.json +23 -0
  279. package/rcf/user-stories/us-11501.json +23 -0
  280. package/rcf/user-stories/us-12001.json +23 -0
  281. package/rcf/user-stories/us-12101.json +23 -0
  282. package/rcf/user-stories/us-12102.json +23 -0
  283. package/rcf/user-stories/us-12201.json +23 -0
  284. package/rcf/user-stories/us-12202.json +24 -0
  285. package/rcf/user-stories/us-12301.json +23 -0
  286. package/rcf/user-stories/us-12401.json +23 -0
  287. package/rcf/user-stories/us-12402.json +23 -0
  288. package/rcf/user-stories/us-12501.json +23 -0
  289. package/rcf/user-stories/us-13001.json +23 -0
  290. package/rcf/user-stories/us-13002.json +23 -0
  291. package/rcf/user-stories/us-13101.json +23 -0
  292. package/rcf/user-stories/us-13102.json +23 -0
  293. package/rcf/user-stories/us-13201.json +23 -0
  294. package/rcf/user-stories/us-13301.json +23 -0
  295. package/rcf/user-stories/us-13302.json +23 -0
  296. package/rcf/user-stories/us-13401.json +23 -0
  297. package/rcf/user-stories/us-14001.json +21 -0
  298. package/rcf/user-stories/us-14501.json +24 -0
  299. package/rcf/user-stories/us-15001.json +26 -0
  300. package/rcf/user-stories/us-15101.json +26 -0
  301. package/rcf/user-stories/us-15201.json +21 -0
  302. package/rcf/user-stories/us-15301.json +31 -0
  303. package/rcf/user-stories/us-15401.json +31 -0
  304. package/releases/releases.yaml +21 -1
  305. package/src/blueprint/apply.js +64 -0
  306. package/src/blueprint/consistency-lint.js +432 -0
  307. package/src/blueprint/disposition-ledger.js +224 -0
  308. package/src/blueprint/dispositions.js +44 -0
  309. package/src/blueprint/index.js +19 -0
  310. package/src/browser-verify/manifest-writer.js +53 -2
  311. package/src/browser-verify/pack-runner.js +18 -0
  312. package/src/cli/blueprint.js +103 -0
  313. package/src/setup/identity-seed.js +18 -0
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "deploy-hetzner-server-REQ-006",
3
+ "prdId": "PRD-001",
4
+ "title": "Four lifecycle events fire with metadata-only payloads and no secret ever crosses the sink boundary",
5
+ "description": "The provisioner facade emits four lifecycle events on the injected event sink: provisionerReady (tool, apiHost), hetznerServerProvisioned (id, primaryIpv4, location, serverType, labels), hetznerSnapshotTaken (serverId, snapshotId, wallClockTime), hetznerServerDestroyed (id, wallClockTime). Every payload carries only metadata; no event body carries the HETZNER_ACCOUNT_API_KEY value, an ssh private key, or a rendered user-data body. The event-secrecy check inside the hcloud-dry-run-mock probe accumulates every emitted event body across a full lifecycle run and asserts the token, an ssh private-key marker (BEGIN OPENSSH PRIVATE KEY), and the elicited user-data bytes appear in NO event body; a mutation SIMULATE_EVENT_SECRECY_LEAK=true injects the token into the hetznerServerProvisioned payload and the probe FAILS with a defensive-fake finding naming the leaked field.",
6
+ "category": "functional",
7
+ "domain": "platform",
8
+ "priority": "must",
9
+ "rationale": "Ratified in the Hetzner round 7 spec (2026-09-07 section 5.1 REQ-006).",
10
+ "tags": [
11
+ "blueprint:deploy-hetzner-server",
12
+ "capability:cloudHost"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-09-08T00:00:00.000Z",
17
+ "updatedAt": "2026-09-08T00:00:00.000Z"
18
+ }
@@ -0,0 +1,108 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://stravica.ai/rcf-lite/schemas/hetzner-server.schema.json",
4
+ "title": "Hetzner Cloud server manifest file",
5
+ "description": "One server per file under the applying project's hetzner/servers/ directory. Fields track the Hetzner Cloud API server resource shape at https://docs.hetzner.cloud/.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "name",
10
+ "serverType",
11
+ "location",
12
+ "image",
13
+ "sshKeyIds",
14
+ "firewallId",
15
+ "cloudInitPath",
16
+ "labels",
17
+ "firewallRules",
18
+ "snapshotCadence"
19
+ ],
20
+ "properties": {
21
+ "name": {
22
+ "type": "string",
23
+ "minLength": 1,
24
+ "description": "Operator-facing stable server name (kebab-case). Used as the Hetzner server name and prefixed onto snapshot names."
25
+ },
26
+ "serverType": {
27
+ "type": "string",
28
+ "enum": ["cx23", "cx33", "cx43", "cpx11", "cpx21", "cpx31", "cpx41", "cax11", "cax21", "cax31"],
29
+ "description": "Hetzner Cloud shared-vCPU server type. cx23 is the shipped default per the round-7 spec section 5.1."
30
+ },
31
+ "location": {
32
+ "type": "string",
33
+ "enum": ["fsn1", "nbg1", "hel1", "ash", "hil"],
34
+ "description": "Hetzner data centre location. fsn1 is the shipped default per Baz ruling 2026-09-07 09:50Z on the Q2 ratification."
35
+ },
36
+ "image": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "description": "Hetzner Cloud base image identifier. ubuntu-24.04 is the shipped default."
40
+ },
41
+ "sshKeyIds": {
42
+ "type": "array",
43
+ "minItems": 1,
44
+ "items": {
45
+ "type": "string",
46
+ "minLength": 1
47
+ },
48
+ "description": "Nonempty array of Hetzner ssh-key references. Delegated to security-secrets-management for the actual key material."
49
+ },
50
+ "networkId": {
51
+ "type": ["string", "null"],
52
+ "description": "Optional Hetzner Cloud private network id. null for no attached private network."
53
+ },
54
+ "firewallId": {
55
+ "type": ["string", "null"],
56
+ "description": "Existing Hetzner Cloud firewall id to reuse; when null the provisioner creates one from firewallRules."
57
+ },
58
+ "cloudInitPath": {
59
+ "type": "string",
60
+ "minLength": 1,
61
+ "description": "Relative path to the rendered cloud-init user-data file. The renderer at src/cloud-init-renderer.mjs consumes contributions/templates/cloud-init.yaml.tmpl and writes the output here."
62
+ },
63
+ "labels": {
64
+ "type": "object",
65
+ "additionalProperties": {
66
+ "type": "string"
67
+ },
68
+ "required": [
69
+ "role",
70
+ "blueprint"
71
+ ],
72
+ "description": "Hetzner tag map. Must include role and blueprint. Under the shared throwaway-server fixture role=rcf-lite-ci-throwaway is required per section 3.3 of the round-7 spec."
73
+ },
74
+ "firewallRules": {
75
+ "type": "array",
76
+ "minItems": 3,
77
+ "description": "The rule shape from TAC-3804. Must include an ssh rule (protocol tcp, port 22, direction in, sourceIps nonempty and NOT containing 0.0.0.0/0), an http rule (protocol tcp, port 80, direction in, sourceIps [\"0.0.0.0/0\", \"::/0\"]) and an https rule (protocol tcp, port 443, direction in, sourceIps [\"0.0.0.0/0\", \"::/0\"]).",
78
+ "items": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": ["name", "protocol", "port", "direction", "sourceIps"],
82
+ "properties": {
83
+ "name": { "type": "string", "enum": ["ssh", "http", "https"] },
84
+ "protocol": { "type": "string", "enum": ["tcp", "udp", "icmp"] },
85
+ "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
86
+ "direction": { "type": "string", "enum": ["in", "out"] },
87
+ "sourceIps": {
88
+ "type": "array",
89
+ "minItems": 1,
90
+ "items": {
91
+ "type": "string",
92
+ "minLength": 1
93
+ }
94
+ }
95
+ }
96
+ }
97
+ },
98
+ "snapshotCadence": {
99
+ "type": "string",
100
+ "enum": ["weekly", "daily", "off"],
101
+ "description": "Snapshot cadence per ADR-3802. weekly is the recommended default."
102
+ },
103
+ "hetznerManagedBackups": {
104
+ "type": "boolean",
105
+ "description": "Optional paid opt-in for Hetzner-managed backups per ADR-3802 (approximately 20 percent of monthly server price)."
106
+ }
107
+ }
108
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "tacId": "TAC-3801-deploy-hetzner-server-provisioner",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Deploy-hetzner-server provisioner facade",
8
+ "purpose": "The single project-side module that dereferences HETZNER_ACCOUNT_API_KEY, wraps typed Hetzner Cloud verbs (createServer, applyFirewall, takeSnapshot, listSnapshots, destroyServer), and emits four lifecycle events to the injected event sink. Under provisioningTool=hcloud the module shells to hcloud CLI with --output json; under raw-api it fetches the equivalent Hetzner Cloud REST endpoints. Consumers never dereference the token; the facade is the one place vendor credentials live.",
9
+ "responsibilities": [
10
+ "Open the facade on boot via a bounded ready-check; be the ONLY module that reads the HETZNER_ACCOUNT_API_KEY value, and export no symbol that leaks it (AC-37101-1).",
11
+ "Expose typed verbs createServer, applyFirewall, takeSnapshot, listSnapshots and destroyServer as returned methods on the factory result; refuse a raw-token passthrough on the public surface (AC-37101-1, AC-37103-1, AC-37108-1).",
12
+ "Emit provisionerReady, hetznerServerProvisioned, hetznerSnapshotTaken and hetznerServerDestroyed on the injected event sink; every payload is metadata-only per REQ-006 (AC-37101-1, AC-37103-1, AC-37108-1, AC-37109-1).",
13
+ "Shell path (hcloud): call hcloud <verb> --output json, JSON.parse the stdout, project the documented fields (id, primaryIpv4, location, serverType, snapshotId) into the returned record (AC-37103-1, AC-37108-1).",
14
+ "Raw-api path: fetch the documented REST endpoints (POST /servers, POST /firewalls, POST /servers/{id}/actions/create_image, DELETE /servers/{id}) with the token in Authorization: Bearer and project the same records (AC-37103-1)."
15
+ ],
16
+ "internalStructure": "One module exporting createProvisionerFacade({ token, tool, apiHost, eventSink, hcloudPath?, fetch? }) returning a ProvisionerFacade whose methods are the typed verbs. The token capture lives at the top of the factory; the shell path passes the token via HCLOUD_TOKEN in the child environment (never on the argv); the raw-api path passes it in the Authorization header. Consumers call the returned methods for the process lifetime; the raw token never crosses the module boundary again.",
17
+ "interfaces": [
18
+ {
19
+ "name": "default export",
20
+ "kind": "factory",
21
+ "summary": "Provisioner facade factory exported from the applied fixture module."
22
+ }
23
+ ],
24
+ "dependencies": [],
25
+ "createdAt": "2026-09-08T00:00:00.000Z",
26
+ "updatedAt": "2026-09-08T00:00:00.000Z"
27
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "tacId": "TAC-3802-deploy-hetzner-server-manifest-schema",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Hetzner server manifest schema",
8
+ "purpose": "One draft-07 JSON Schema declaring the shape of hetzner/servers/name.json in the applying project. Names required fields (name, serverType, location, image, sshKeyIds, firewallId, cloudInitPath, labels, firewallRules, snapshotCadence) and enforces enums (location, serverType, snapshotCadence) and rule-shape invariants (see TAC-3804). Manifest-schema-validate.mjs uses the schema to accept or refuse every fixture manifest.",
9
+ "responsibilities": [
10
+ "Ship a draft-07 JSON Schema for the hetzner-server manifest at contributions/schemas/hetzner-server.schema.json (AC-37102-1).",
11
+ "Refuse on missing required fields with an error message that names the offending field (AC-37102-1).",
12
+ "Refuse on firewall-rule-shape violations (22 opened to 0.0.0.0/0, or 80/443 missing) with an error message that names the offending rule (AC-37106-1).",
13
+ "Enforce snapshotCadence enum {weekly, daily, off} and refuse otherwise (AC-37107-1)."
14
+ ],
15
+ "internalStructure": "One JSON Schema file at blueprints/deploy-hetzner-server/contributions/schemas/hetzner-server.schema.json; one shipped example manifest under the shared throwaway-server fixture at packages/rcf-lite/test/fixtures/hetzner-throwaway-server/hetzner/servers/ci-throwaway.json.",
16
+ "interfaces": [
17
+ {
18
+ "name": "hetzner-server.schema.json",
19
+ "kind": "jsonSchema",
20
+ "summary": "draft-07 JSON Schema for the Hetzner server manifest."
21
+ }
22
+ ],
23
+ "dependencies": [],
24
+ "createdAt": "2026-09-08T00:00:00.000Z",
25
+ "updatedAt": "2026-09-08T00:00:00.000Z"
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "tacId": "TAC-3803-deploy-hetzner-server-cloud-init-template",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Cloud-init template and render lint",
8
+ "purpose": "One cloud-init user-data template that renders YAML landing the six hardening baseline blocks per ADR-3804 on every provisioned Hetzner server: SSH key-only, root disabled, UFW default-deny incoming, DOCKER-USER iptables chain, fail2ban SSH jail, unattended-upgrades. A local render-lint probe asserts every block appears in the rendered YAML; the SIMULATE_HARDENING_DRIFT mutation removes the unattended-upgrades block and the probe FAILS naming the missing line.",
9
+ "responsibilities": [
10
+ "Ship a cloud-init template at contributions/templates/cloud-init.yaml.tmpl that renders the six baseline blocks (AC-37104-1).",
11
+ "Render deterministically against a manifest via the fixture's cloud-init-renderer.mjs (AC-37104-1).",
12
+ "Cover the SIMULATE_HARDENING_DRIFT mutation with a probe FAILURE that names the missing line (AC-37104-1).",
13
+ "Document the runtime ssh baseline check that the account-bound probe drives against the throwaway server (AC-37105-1)."
14
+ ],
15
+ "internalStructure": "One template file at blueprints/deploy-hetzner-server/contributions/templates/cloud-init.yaml.tmpl using {{token}} placeholders substituted by the renderer; one renderer module at packages/rcf-lite/test/fixtures/hetzner-throwaway-server/src/cloud-init-renderer.mjs; one probe cloud-init-render-lint.mjs asserts baseline lines.",
16
+ "interfaces": [
17
+ {
18
+ "name": "cloud-init.yaml.tmpl",
19
+ "kind": "template",
20
+ "summary": "Cloud-init user-data template with the six baseline blocks and templated substitutions."
21
+ }
22
+ ],
23
+ "dependencies": [],
24
+ "createdAt": "2026-09-08T00:00:00.000Z",
25
+ "updatedAt": "2026-09-08T00:00:00.000Z"
26
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "tacId": "TAC-3804-deploy-hetzner-server-firewall-shape",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Hetzner Cloud firewall rule shape",
8
+ "purpose": "One canonical firewall rule shape carried inside the server manifest. Rule set: 22 permitted only from a nonempty list of source-prefix ranges (the operator-nominated set; the schema refuses on 0.0.0.0/0 in this list); 80 and 443 permitted from 0.0.0.0/0 for the reverse proxy; all other inbound denied. The provisioner applies the rule set to the server at provision time via hcloud firewall create + apply-to-resource (or the raw-api equivalent).",
9
+ "responsibilities": [
10
+ "Define the firewall rule shape as a sub-schema in hetzner-server.schema.json (AC-37106-1).",
11
+ "Refuse via manifest-schema-validate on a manifest that opens 22 to 0.0.0.0/0 or drops 80/443 (AC-37106-1).",
12
+ "Enforce that the ssh rule's sourceIps is a nonempty array (AC-37106-1)."
13
+ ],
14
+ "internalStructure": "Rule shape lives inside the same hetzner-server.schema.json used by TAC-3802 (properties.firewallRules array items with protocol, port, direction, sourceIps). The applyFirewall verb on the provisioner facade consumes the parsed rule set.",
15
+ "interfaces": [
16
+ {
17
+ "name": "firewallRules sub-schema",
18
+ "kind": "jsonSchemaFragment",
19
+ "summary": "Rule-shape invariants inside hetzner-server.schema.json."
20
+ }
21
+ ],
22
+ "dependencies": [],
23
+ "createdAt": "2026-09-08T00:00:00.000Z",
24
+ "updatedAt": "2026-09-08T00:00:00.000Z"
25
+ }
@@ -0,0 +1,113 @@
1
+ #cloud-config
2
+ # deploy-hetzner-server v1.0.0 cloud-init user-data template.
3
+ # Six-block hardening baseline per ADR-3804 (Debian unattended-upgrades wiki
4
+ # and the estate baseline at
5
+ # operator/knowledge/systems/environments/hetzner-cloud/overview.md
6
+ # Security posture baseline). Placeholders in {{double-brace}} form are
7
+ # substituted by the fixture's cloud-init-renderer.mjs against the applying
8
+ # manifest.
9
+
10
+ hostname: {{name}}
11
+
12
+ # Block 1: SSH key-only. Password auth disabled at the sshd level too.
13
+ users:
14
+ - name: deploy
15
+ groups: [sudo]
16
+ shell: /bin/bash
17
+ lock_passwd: true
18
+ ssh_authorized_keys:
19
+ {{sshAuthorizedKeysBlock}}
20
+
21
+ # Block 2: root disabled (PermitRootLogin no).
22
+ # Block 1 continued: PasswordAuthentication no.
23
+ write_files:
24
+ # Block 1 continued: NOPASSWD sudo for the deploy user (mode 0440,
25
+ # visudo-shaped fragment). Baseline hardening requires the deploy
26
+ # user to run sudoed baseline checks without a tty; the write_files
27
+ # entry ships the fragment in the same posture as the sshd config.
28
+ - path: /etc/sudoers.d/90-deploy-nopasswd
29
+ permissions: '0440'
30
+ content: |
31
+ # deploy-hetzner-server v1.0.1 hardening: passwordless sudo for
32
+ # the deploy user so the ssh baseline checks (cloud-init status
33
+ # --wait, ufw status, iptables -L, systemctl is-active fail2ban)
34
+ # never block on a tty prompt.
35
+ deploy ALL=(ALL) NOPASSWD:ALL
36
+ - path: /etc/ssh/sshd_config.d/hardening.conf
37
+ permissions: '0644'
38
+ content: |
39
+ # deploy-hetzner-server v1.0.0 hardening (baseline blocks 1 and 2).
40
+ PermitRootLogin no
41
+ PasswordAuthentication no
42
+ KbdInteractiveAuthentication no
43
+ ChallengeResponseAuthentication no
44
+ # Block 4: DOCKER-USER iptables chain drops non-conforming egress.
45
+ - path: /etc/iptables/rules.v4.d/docker-user.rules
46
+ permissions: '0644'
47
+ content: |
48
+ # deploy-hetzner-server v1.0.0 hardening baseline block 4:
49
+ # DOCKER-USER iptables chain wired to drop non-conforming egress.
50
+ *filter
51
+ :DOCKER-USER - [0:0]
52
+ -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
53
+ -A DOCKER-USER -j DROP
54
+ COMMIT
55
+ # Block 5: fail2ban SSH jail.
56
+ - path: /etc/fail2ban/jail.d/sshd.conf
57
+ permissions: '0644'
58
+ content: |
59
+ # deploy-hetzner-server v1.0.0 hardening baseline block 5:
60
+ # fail2ban SSH jail on port 22.
61
+ [sshd]
62
+ enabled = true
63
+ port = 22
64
+ maxretry = 5
65
+ findtime = 600
66
+ bantime = 3600
67
+ # Block 6: unattended-upgrades on (security updates).
68
+ - path: /etc/apt/apt.conf.d/50unattended-upgrades
69
+ permissions: '0644'
70
+ content: |
71
+ // deploy-hetzner-server v1.0.0 hardening baseline block 6:
72
+ // unattended-upgrades on for security updates
73
+ // per https://wiki.debian.org/UnattendedUpgrades
74
+ Unattended-Upgrade::Origins-Pattern {
75
+ "origin=Debian,codename=${distro_codename},label=Debian-Security";
76
+ "origin=Ubuntu,archive=${distro_codename}-security";
77
+ };
78
+ Unattended-Upgrade::Automatic-Reboot "false";
79
+ - path: /etc/apt/apt.conf.d/20auto-upgrades
80
+ permissions: '0644'
81
+ content: |
82
+ APT::Periodic::Update-Package-Lists "1";
83
+ APT::Periodic::Unattended-Upgrade "1";
84
+
85
+ # Package install: ufw for block 3, fail2ban for block 5.
86
+ # unattended-upgrades ships in the ubuntu-24.04 base image (v1.0.1 fix:
87
+ # was in packages: v1.0.0 which caused a spurious NoNewestVersion line).
88
+ # iptables-persistent conflicts with ufw on ubuntu-24.04 noble (ufw
89
+ # Breaks: iptables-persistent), so we DO NOT install it; the DOCKER-USER
90
+ # chain is loaded in runcmd via iptables directly (block 4 stays valid
91
+ # for the running boot; reboot-persistence for the chain is a follow-up
92
+ # item outside the H-1 hardening scope).
93
+ packages:
94
+ - ufw
95
+ - fail2ban
96
+
97
+ runcmd:
98
+ # Block 3: UFW default-deny incoming, allow 22 80 443.
99
+ - ufw default deny incoming
100
+ - ufw default allow outgoing
101
+ - ufw allow 22/tcp
102
+ - ufw allow 80/tcp
103
+ - ufw allow 443/tcp
104
+ - ufw --force enable
105
+ - systemctl reload ssh || systemctl restart ssh
106
+ - systemctl enable --now fail2ban
107
+ - systemctl enable --now unattended-upgrades
108
+ # Block 4 runtime: create the DOCKER-USER chain directly via iptables
109
+ # (v1.0.1 fix: iptables-persistent conflicts with ufw on Ubuntu 24.04
110
+ # noble; iptables itself is preinstalled, so the chain lands cleanly).
111
+ - iptables -N DOCKER-USER 2>/dev/null || true
112
+ - iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN
113
+ - iptables -A DOCKER-USER -j DROP
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37101",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "The provisioner opens on boot as the sole token reader and emits provisionerReady",
8
+ "asA": "developer whose project provisions a Hetzner Cloud VM as its cloudHost",
9
+ "iWant": "one provisioner facade module to be the sole reader of HETZNER_ACCOUNT_API_KEY and to emit provisionerReady on boot with a metadata-only payload",
10
+ "soThat": "the token has one auditable seam and the observability sink surfaces boot ordering without ever carrying the secret",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37101-1",
14
+ "description": "On process boot the provisioner facade opens against the token, and (a) only the provisioner module reads HETZNER_ACCOUNT_API_KEY (grep across the fixture source refuses any other reader), (b) provisionerReady fires on the injected event sink with a payload of exactly {tool, apiHost}.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3801-deploy-hetzner-server-provisioner"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37102",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-002",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "hetzner/servers/name.json manifests validate against the shipped JSON Schema",
8
+ "asA": "developer whose project holds Hetzner server declarations in git",
9
+ "iWant": "every hetzner/servers/name.json file to validate against a shipped draft-07 JSON Schema naming type, location, image, sshKeyIds, firewallId, networkId, cloudInitPath, labels, firewallRules and snapshotCadence",
10
+ "soThat": "the operator has a stable declarative source of truth and manifest defects surface locally before they reach the API",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37102-1",
14
+ "description": "manifest-schema-validate.mjs asserts every hetzner/servers/*.json under the shared throwaway-server fixture validates against blueprints/deploy-hetzner-server/contributions/schemas/hetzner-server.schema.json and carries every required field; a mutation SIMULATE_MANIFEST_INVALID=true replaces location with an unknown token and the probe FAILS naming the offending field.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3802-deploy-hetzner-server-manifest-schema"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37103",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-002",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Manifest applies: the provisioner creates a matching live server and fires hetznerServerProvisioned",
8
+ "asA": "developer whose project provisions a Hetzner Cloud VM from git-managed manifests",
9
+ "iWant": "applying the provisioner to read each manifest under hetzner/servers/, create a matching live Hetzner Cloud server, and emit hetznerServerProvisioned with id, primaryIpv4, location and serverType",
10
+ "soThat": "the manifest is the source of truth for what actually exists in the project",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37103-1",
14
+ "description": "The real-account-throwaway-server-provision probe (accountBound: true, skipped without CI_HAS_HETZNER_ACCOUNT) applies the ci-throwaway.json manifest via provision.mjs, asserts (a) hcloud server list --output json shows the new server whose id is written to the fixture scratch file, (b) hetznerServerProvisioned fired with id, primaryIpv4, location=fsn1, serverType=cx23; teardown via destroy.mjs runs regardless of probe verdict.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3801-deploy-hetzner-server-provisioner",
21
+ "TAC-3802-deploy-hetzner-server-manifest-schema"
22
+ ],
23
+ "createdAt": "2026-09-08T00:00:00.000Z",
24
+ "updatedAt": "2026-09-08T00:00:00.000Z"
25
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37104",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-003",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Cloud-init template renders the six hardening baseline blocks",
8
+ "asA": "developer whose project provisions Hetzner servers that face the public internet",
9
+ "iWant": "the shipped cloud-init template to render YAML that lands SSH key-only, root disabled, UFW default-deny incoming, DOCKER-USER iptables chain, fail2ban SSH jail and unattended-upgrades on every server",
10
+ "soThat": "I inherit the six-block hardening baseline declaratively without hand-authoring per host",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37104-1",
14
+ "description": "cloud-init-render-lint.mjs renders the template with the fixture manifest and asserts each of the six baseline blocks (SSH key-only, root disabled, UFW default-deny incoming, DOCKER-USER iptables chain, fail2ban SSH jail, unattended-upgrades) appears in the rendered YAML by grep; a mutation SIMULATE_HARDENING_DRIFT=true removes the unattended-upgrades block and the probe FAILS with a message naming the missing line.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3803-deploy-hetzner-server-cloud-init-template"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37105",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-003",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Provisioned server is hardened at runtime as the rendered cloud-init promised",
8
+ "asA": "operator running Hetzner servers provisioned by this blueprint",
9
+ "iWant": "the running server to refuse root ssh, refuse password auth, run UFW default-deny incoming, hold the DOCKER-USER chain and run unattended-upgrades",
10
+ "soThat": "I can trust the cloud-init contract without ssh-in-and-check on every server",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37105-1",
14
+ "description": "real-account-cloud-init-hardened.mjs (accountBound: true, skipped without CI_HAS_HETZNER_ACCOUNT) waits for cloud-init status --wait on the throwaway server, then ssh-checks the six baseline blocks and asserts each check passes; failure names the missing block. Teardown via destroy.mjs runs regardless.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3803-deploy-hetzner-server-cloud-init-template"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37106",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-004",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Firewall rule shape restricts SSH to nominated prefixes, permits 80/443, denies all else",
8
+ "asA": "operator running Hetzner servers that face the public internet",
9
+ "iWant": "the firewall rule set carried in hetzner/servers/name.json to permit 22 only from an operator-nominated set of source-prefix ranges, permit 80 and 443 from any source for the reverse proxy, and deny all other inbound; the provisioner applies the rules at provision time",
10
+ "soThat": "I inherit a documented least-privilege firewall shape on every server without opening ssh to the world",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37106-1",
14
+ "description": "manifest-schema-validate.mjs asserts every fixture manifest carries a firewallRules array matching the shape (22 permitted only from a nonempty list of source-prefix ranges with no 0.0.0.0/0; 80 and 443 permitted from 0.0.0.0/0; no other inbound rule); an induced defect (a manifest that opens 22 to 0.0.0.0/0) FAILS schema validation with a message naming the offending rule.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3802-deploy-hetzner-server-manifest-schema",
21
+ "TAC-3804-deploy-hetzner-server-firewall-shape"
22
+ ],
23
+ "createdAt": "2026-09-08T00:00:00.000Z",
24
+ "updatedAt": "2026-09-08T00:00:00.000Z"
25
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37107",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-005",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Snapshot cadence is elicited (weekly default, daily and off alternatives) and validated on the manifest",
8
+ "asA": "operator whose project takes recovery-point snapshots on Hetzner Cloud",
9
+ "iWant": "the elicited snapshot cadence value on hetzner/servers/name.json to match the shipped enum {weekly, daily, off}, with weekly the recommended default per ADR-3802",
10
+ "soThat": "I have one project-wide answer for how often each server snapshots",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37107-1",
14
+ "description": "manifest-schema-validate.mjs asserts every fixture manifest carries snapshotCadence with a value in the shipped enum {weekly, daily, off}; a manifest missing the field FAILS schema validation naming the missing field.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3802-deploy-hetzner-server-manifest-schema"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37108",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-005",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Snapshot-on-demand fires a tagged snapshot and emits hetznerSnapshotTaken",
8
+ "asA": "operator whose project needs an on-demand recovery point at any moment",
9
+ "iWant": "the snapshot-on-demand verb on the provisioner to shell to hcloud image create-image (or the raw-api equivalent) and tag the snapshot with server name and wall-clock time; hetznerSnapshotTaken carries snapshot id and time",
10
+ "soThat": "I have a documented on-demand recovery point at any moment without a manual dashboard visit",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37108-1",
14
+ "description": "real-account-snapshot-on-demand.mjs (accountBound: true, skipped without CI_HAS_HETZNER_ACCOUNT) fires the snapshot verb on the throwaway server, then asserts hcloud image list --type=snapshot --output json shows a snapshot tagged with the server name and current wall-clock time; hetznerSnapshotTaken event carries snapshot id and time. Teardown via destroy.mjs.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3801-deploy-hetzner-server-provisioner"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "usId": "deploy-hetzner-server-US-37109",
3
+ "prdId": "PRD-001",
4
+ "reqId": "deploy-hetzner-server-REQ-006",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "title": "Four lifecycle events fire with metadata-only payloads and no event body carries a secret",
8
+ "asA": "operator whose observability sink carries the Hetzner lifecycle trail",
9
+ "iWant": "the provisioner facade to fire the four lifecycle events (provisionerReady, hetznerServerProvisioned, hetznerSnapshotTaken, hetznerServerDestroyed) with metadata-only payloads and no secret content",
10
+ "soThat": "the observability sink carries the operational trail without ever leaking a token or an ssh key",
11
+ "acceptanceCriteria": [
12
+ {
13
+ "id": "AC-37109-1",
14
+ "description": "hcloud-dry-run-mock.mjs drives every lifecycle event through the facade against a mocked hcloud shim; the accumulated event stream carries only the metadata fields named on REQ-006; a mutation SIMULATE_EVENT_SECRECY_LEAK=true injects the token into the hetznerServerProvisioned payload and the probe FAILS with a defensive-fake finding naming the leaked field.",
15
+ "testable": true,
16
+ "scope": "runtime"
17
+ }
18
+ ],
19
+ "tacIds": [
20
+ "TAC-3801-deploy-hetzner-server-provisioner"
21
+ ],
22
+ "createdAt": "2026-09-08T00:00:00.000Z",
23
+ "updatedAt": "2026-09-08T00:00:00.000Z"
24
+ }
@@ -0,0 +1,48 @@
1
+ # deploy-hetzner-server topics
2
+
3
+ Shelf registry of global ADR topics and shelf-band anchors relevant
4
+ to `deploy-hetzner-server` v1.0.0.
5
+
6
+ ## Global topics minted by this blueprint
7
+
8
+ | Topic | ADR | Meaning |
9
+ |---|---|---|
10
+ | `linuxCloudHostContract` | `ADR-3801` | The Hetzner Cloud VM as the shipped `cloudHost` shape. A future non-Hetzner sibling (Vultr, DigitalOcean, Fly.io) contributes the same topic string with a different provider and forces a deliberate conflict per the round-2 pattern. |
11
+ | `snapshotAndBackupCadence` | `ADR-3802` | Weekly snapshot as the shipped default, daily and off alternatives, Hetzner-managed backups as an elicited paid opt-in at approximately 20 percent of monthly server price. |
12
+
13
+ ## Shelf id band and suffix block
14
+
15
+ | Blueprint | US band | ADR/TAC suffix block | Status | Global topics |
16
+ |---|---|---|---|---|
17
+ | application-datatable | 17101-17899 | 18xx | shipped v1.0.0 | none |
18
+ | application-charts | 18101-18899 | 19xx | shipped v1.0.0 | none |
19
+ | application-dashboard | 19101-19899 | 20xx | shipped v1.0.0 | none |
20
+ | application-notifications-in-app | 20101-20899 | 21xx | shipped v1.0.0 | none |
21
+ | application-admin-console | 21101-21899 | 22xx | shipped v1.1.0 | `adminConsoleSignInSurface` |
22
+ | application-empty-error-states | 22101-22899 | 23xx | shipped v1.0.0 | none |
23
+ | application-file-upload | 23101-23899 | 24xx | shipped v1.0.0 | none |
24
+ | application-forms-wizard | 24101-24899 | 25xx | shipped v1.0.0 | none |
25
+ | application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
26
+ | application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
27
+ | persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
28
+ | object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
29
+ | messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
30
+ | jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
31
+ | platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
32
+ | platform-cloudflare-cron-triggers | 32101-32899 | 33xx | shipped v1.0.0 | `scheduledTriggerContract` |
33
+ | platform-cloudflare-durable-objects | 33101-33899 | 34xx | shipped v1.0.0 | `strongConsistencyCellContract`, `websocketHubContract` |
34
+ | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
35
+ | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
36
+ | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
37
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
38
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
39
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
40
+
41
+ The shelf-wide band registry lives in `packages/rcf-lite/docs/blueprint-authoring.md` section 5.
42
+
43
+ ## Cross-references
44
+
45
+ - `security-secrets-management` (v1.0.1): the provisioner facade reads `HETZNER_ACCOUNT_API_KEY` through the applied secrets facade. The facade is the sole reader; every other module in the applying project imports the facade and never dereferences the token.
46
+ - `deploy-cloudflare-workers` (v1.2.0): sibling in the `deploy-*` family. The family enforces one-target-per-project at apply time (a project applying both refuses with `deploy-family-multiple-targets`).
47
+ - Round-7 T-2 `platform-docker-compose-host`: composes on `capabilities: [cloudHost]`; ships the compose runtime on the Hetzner host this blueprint provisions.
48
+ - Round-7 T-3 `edge-cloudflare-tunnel`: composes on `cloudHost` (systemd unit shape) or on `containerHost` (compose service shape); either shape bridges the Hetzner host to the Cloudflare edge without opening origin ports.