rcf-lite 0.24.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 (232) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/blueprints/application-account-settings/docs/topics.md +3 -0
  3. package/blueprints/application-admin-console/docs/topics.md +3 -0
  4. package/blueprints/application-api-rest/docs/topics.md +3 -0
  5. package/blueprints/application-charts/docs/topics.md +3 -0
  6. package/blueprints/application-dashboard/docs/topics.md +3 -0
  7. package/blueprints/application-datatable/docs/topics.md +3 -0
  8. package/blueprints/application-empty-error-states/docs/topics.md +3 -0
  9. package/blueprints/application-error-handling/docs/topics.md +3 -0
  10. package/blueprints/application-file-upload/docs/topics.md +3 -0
  11. package/blueprints/application-forms-wizard/docs/topics.md +3 -0
  12. package/blueprints/application-notifications-in-app/docs/topics.md +3 -0
  13. package/blueprints/application-onboarding-tour/docs/topics.md +3 -0
  14. package/blueprints/application-spa/docs/topics.md +3 -0
  15. package/blueprints/delivery-ci-workflows/docs/topics.md +3 -0
  16. package/blueprints/deploy-cloudflare-workers/docs/topics.md +3 -0
  17. package/blueprints/deploy-hetzner-server/CHANGELOG.md +34 -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 +64 -0
  25. package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +116 -0
  26. package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +180 -0
  27. package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +106 -0
  28. package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +63 -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 +93 -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 +3 -0
  61. package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +3 -0
  62. package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +13 -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 +234 -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 +54 -0
  73. package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-tunnel-hostname-routes.mjs +73 -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 +3 -0
  99. package/blueprints/email-smtp-resend/docs/topics.md +3 -0
  100. package/blueprints/jobs-background/docs/topics.md +3 -0
  101. package/blueprints/messaging-queue-cloudflare/docs/topics.md +3 -0
  102. package/blueprints/object-storage-s3/docs/topics.md +3 -0
  103. package/blueprints/observability-essentials/docs/topics.md +3 -0
  104. package/blueprints/observability-logging/docs/topics.md +3 -0
  105. package/blueprints/observability-probe-endpoints/docs/topics.md +3 -0
  106. package/blueprints/persistence-data-d1/docs/topics.md +3 -0
  107. package/blueprints/persistence-data-postgres/docs/topics.md +3 -0
  108. package/blueprints/persistence-data-sqlite/docs/topics.md +3 -0
  109. package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +3 -0
  110. package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +3 -0
  111. package/blueprints/platform-cloudflare-kv/docs/topics.md +3 -0
  112. package/blueprints/platform-docker-compose-host/CHANGELOG.md +12 -0
  113. package/blueprints/platform-docker-compose-host/README.md +76 -0
  114. package/blueprints/platform-docker-compose-host/blueprint.json +209 -0
  115. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3901-platform-docker-compose-host-contract.json +14 -0
  116. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +14 -0
  117. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +14 -0
  118. package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3904-platform-docker-compose-host-log-driver.json +14 -0
  119. package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +108 -0
  120. package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +231 -0
  121. package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +185 -0
  122. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +41 -0
  123. package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +36 -0
  124. package/blueprints/platform-docker-compose-host/contributions/probes/run-caddyfile-validate.mjs +5 -0
  125. package/blueprints/platform-docker-compose-host/contributions/probes/run-compose-config-lint.mjs +5 -0
  126. package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-minimal-stack-up.mjs +5 -0
  127. package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-reload-burst.mjs +5 -0
  128. package/blueprints/platform-docker-compose-host/contributions/probes/run-secrets-as-files-scan.mjs +5 -0
  129. package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +116 -0
  130. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-001.json +18 -0
  131. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-002.json +18 -0
  132. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-003.json +18 -0
  133. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-004.json +18 -0
  134. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-005.json +18 -0
  135. package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-006.json +18 -0
  136. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +26 -0
  137. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +26 -0
  138. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +26 -0
  139. package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +26 -0
  140. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38101.json +24 -0
  141. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38102.json +24 -0
  142. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38103.json +24 -0
  143. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38104.json +24 -0
  144. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38105.json +25 -0
  145. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38106.json +24 -0
  146. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38107.json +24 -0
  147. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38108.json +24 -0
  148. package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38109.json +24 -0
  149. package/blueprints/platform-docker-compose-host/docs/topics.md +71 -0
  150. package/blueprints/platform-docker-compose-host/guide/platform-docker-compose-host.md +94 -0
  151. package/blueprints/security-auth-clerk/docs/topics.md +3 -0
  152. package/blueprints/security-auth-keycloak/docs/topics.md +3 -0
  153. package/blueprints/security-auth-magic-link/docs/topics.md +3 -0
  154. package/blueprints/security-auth-oauth2/docs/topics.md +3 -0
  155. package/blueprints/security-secrets-management/docs/topics.md +3 -0
  156. package/fixtures/canary-manifest.json +9 -9
  157. package/package.json +1 -1
  158. package/rcf/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +13 -0
  159. package/rcf/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +13 -0
  160. package/rcf/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +13 -0
  161. package/rcf/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +13 -0
  162. package/rcf/adrs/adr-3901-platform-docker-compose-host-contract.json +13 -0
  163. package/rcf/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +13 -0
  164. package/rcf/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +13 -0
  165. package/rcf/adrs/adr-3904-platform-docker-compose-host-log-driver.json +13 -0
  166. package/rcf/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +13 -0
  167. package/rcf/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +13 -0
  168. package/rcf/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +13 -0
  169. package/rcf/code-nodes/cn-410.json +20 -0
  170. package/rcf/code-nodes/cn-440.json +20 -0
  171. package/rcf/code-nodes/cn-470.json +19 -0
  172. package/rcf/fbs/fbs-130.json +23 -0
  173. package/rcf/fbs/fbs-140.json +23 -0
  174. package/rcf/fbs/fbs-150.json +22 -0
  175. package/rcf/requirements/req-110.json +58 -0
  176. package/rcf/requirements/req-111.json +32 -0
  177. package/rcf/requirements/req-112.json +42 -0
  178. package/rcf/requirements/req-113.json +33 -0
  179. package/rcf/requirements/req-114.json +20 -0
  180. package/rcf/requirements/req-115.json +38 -0
  181. package/rcf/requirements/req-120.json +20 -0
  182. package/rcf/requirements/req-121.json +20 -0
  183. package/rcf/requirements/req-122.json +20 -0
  184. package/rcf/requirements/req-123.json +42 -0
  185. package/rcf/requirements/req-124.json +27 -0
  186. package/rcf/requirements/req-125.json +20 -0
  187. package/rcf/requirements/req-130.json +32 -0
  188. package/rcf/requirements/req-131.json +54 -0
  189. package/rcf/requirements/req-132.json +27 -0
  190. package/rcf/requirements/req-133.json +20 -0
  191. package/rcf/requirements/req-134.json +42 -0
  192. package/rcf/tacs/tac-3801-deploy-hetzner-server-provisioner.json +17 -0
  193. package/rcf/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +17 -0
  194. package/rcf/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +17 -0
  195. package/rcf/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +16 -0
  196. package/rcf/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +17 -0
  197. package/rcf/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +17 -0
  198. package/rcf/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +17 -0
  199. package/rcf/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +17 -0
  200. package/rcf/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +17 -0
  201. package/rcf/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +17 -0
  202. package/rcf/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +16 -0
  203. package/rcf/test-suites/ts-140.json +86 -0
  204. package/rcf/test-suites/ts-150.json +86 -0
  205. package/rcf/test-suites/ts-160.json +78 -0
  206. package/rcf/user-stories/us-11001.json +23 -0
  207. package/rcf/user-stories/us-11101.json +23 -0
  208. package/rcf/user-stories/us-11102.json +24 -0
  209. package/rcf/user-stories/us-11201.json +23 -0
  210. package/rcf/user-stories/us-11202.json +23 -0
  211. package/rcf/user-stories/us-11301.json +24 -0
  212. package/rcf/user-stories/us-11401.json +23 -0
  213. package/rcf/user-stories/us-11402.json +23 -0
  214. package/rcf/user-stories/us-11501.json +23 -0
  215. package/rcf/user-stories/us-12001.json +23 -0
  216. package/rcf/user-stories/us-12101.json +23 -0
  217. package/rcf/user-stories/us-12102.json +23 -0
  218. package/rcf/user-stories/us-12201.json +23 -0
  219. package/rcf/user-stories/us-12202.json +24 -0
  220. package/rcf/user-stories/us-12301.json +23 -0
  221. package/rcf/user-stories/us-12401.json +23 -0
  222. package/rcf/user-stories/us-12402.json +23 -0
  223. package/rcf/user-stories/us-12501.json +23 -0
  224. package/rcf/user-stories/us-13001.json +23 -0
  225. package/rcf/user-stories/us-13002.json +23 -0
  226. package/rcf/user-stories/us-13101.json +23 -0
  227. package/rcf/user-stories/us-13102.json +23 -0
  228. package/rcf/user-stories/us-13201.json +23 -0
  229. package/rcf/user-stories/us-13301.json +23 -0
  230. package/rcf/user-stories/us-13302.json +23 -0
  231. package/rcf/user-stories/us-13401.json +23 -0
  232. package/releases/releases.yaml +11 -1
@@ -0,0 +1,204 @@
1
+ # Guide: edge-cloudflare-tunnel
2
+
3
+ The edge-cloudflare-tunnel blueprint bridges a service on a `cloudHost`
4
+ or a `containerHost` to the Cloudflare edge through a Cloudflare Tunnel,
5
+ with no public origin ports. This guide walks the shape end to end:
6
+ what the connector looks like, what the manifest looks like, how the
7
+ Access-gated shape composes on top of the public-hostname shape, and
8
+ how the credentials-file discipline stays honest on every reapply.
9
+
10
+ Ratifying spec: `projects/blueprint-library/specs/hetzner-round-7-spec-2026-09-07.md` section 5.3.
11
+
12
+ ## When to reach for this blueprint
13
+
14
+ Reach when the applying project needs to expose a service on the public
15
+ internet without opening origin ports on the host and without standing
16
+ up a bespoke reverse proxy in front of the CDN. The tunnel connector
17
+ dials out to the Cloudflare edge and terminates ingress there; the host
18
+ firewall (T-1 deploy-hetzner-server) can stay closed on 80/443 unless a
19
+ reverse proxy is also applied.
20
+
21
+ Do not reach for this blueprint when the applying project runs on
22
+ Cloudflare Workers (`deploy-cloudflare-workers` handles ingress
23
+ directly), when the applying project runs behind a corporate VPN
24
+ (private connectivity has different tools), or when the ingress path
25
+ needs a non-Cloudflare bridge shape (Tailscale Funnel, Fly.io proxy;
26
+ those will mint on operator demand into their own reserved slots).
27
+
28
+ ## Connector runtime: when to reach for compose-service vs systemd-unit
29
+
30
+ The connector runtime is elicited via `connector-runtime` per ADR-4002
31
+ with `recommendedDefault: compose-service`.
32
+
33
+ Reach for **compose-service** (the shipped default when `containerHost`
34
+ is applied) when the applying project already runs docker compose on
35
+ the host. The connector ships as another service in the same stack,
36
+ sharing the compose network, the log driver and the restart discipline.
37
+ The T-2 platform-docker-compose-host contract makes this the natural
38
+ home; the connector composes on `containerHost` from T-2.
39
+
40
+ Reach for **systemd-unit** when the host runs bare (only T-1 applied)
41
+ and there is no compose stack to add a service to. The connector runs
42
+ as a systemd unit per the vendor cloudflared as-a-service documented
43
+ systemd unit; the unit file lives at `/etc/systemd/system/cloudflared.service`
44
+ on the host and reads the local configuration file at
45
+ `/etc/cloudflared/<name>.yaml`. Boot ordering plays nicely with journald.
46
+
47
+ The fixture ships both variants under
48
+ `packages/rcf-lite/test/fixtures/hetzner-throwaway-server/cloudflared/{compose-service,systemd-unit}/`
49
+ and both pass `manifest-schema-validate`, `cloudflared-config-lint` and
50
+ `aud-presence-check` on every reviewer boot.
51
+
52
+ ## Hostname mode: when to reach for Access-gated vs public-hostname
53
+
54
+ The hostname mode is not elicited. It is DISCOVERED from the applied
55
+ capability set (ADR-4003). When `zeroTrustGate` is applied (i.e.
56
+ edge-cloudflare-access v1.0.0 is applied on the same project), every
57
+ ingress rule attaches `originRequest.access.aud` with the AUD from the
58
+ sidecar. When `zeroTrustGate` is not applied, the block is omitted and
59
+ the tunnel serves in public-hostname mode.
60
+
61
+ Reach for **access-gated** when the service behind the tunnel needs an
62
+ authentication gate at the edge (any request without a valid Access
63
+ JWT is refused before it hits the origin). The Access side of the
64
+ composition is owned by edge-cloudflare-access; edge-cloudflare-tunnel
65
+ only reads the sidecar and attaches the AUD block. See the vendor
66
+ self-hosted-public-app documentation for the full AUD binding shape.
67
+
68
+ Reach for **public-hostname** when the service is deliberately public
69
+ (a marketing page, a documentation site, or a public API where
70
+ authorisation lives at the origin). The tunnel still terminates
71
+ ingress at the CF edge; no origin port is opened; the difference is
72
+ that cloudflared does not check for a JWT before forwarding.
73
+
74
+ Graceful degrade: when Access is not applied, the blueprint still ships
75
+ in public-hostname mode. The `aud-presence-check` probe runs on both
76
+ fixture variants and asserts the shape flips correctly.
77
+
78
+ ## Tunnel manifest walkthrough
79
+
80
+ The applying project ships one tunnel manifest per named tunnel at
81
+ `cloudflare/tunnels/<name>.yaml`. A minimal public-hostname manifest:
82
+
83
+ ```yaml
84
+ tunnel: 12345678-90ab-4cde-8f12-3456789abcde
85
+ credentialsFile:
86
+ secretRef: hq-estate/CLOUDFLARE_TUNNEL_CREDENTIALS_MY_PROJECT
87
+ ingress:
88
+ - hostname: my-app.example.com
89
+ service: http://web:8080
90
+ - service: http_status:404
91
+ ```
92
+
93
+ An access-gated manifest adds one `originRequest.access` block per
94
+ non-catch-all rule:
95
+
96
+ ```yaml
97
+ tunnel: 12345678-90ab-4cde-8f12-3456789abcde
98
+ credentialsFile:
99
+ secretRef: hq-estate/CLOUDFLARE_TUNNEL_CREDENTIALS_MY_PROJECT
100
+ ingress:
101
+ - hostname: my-app.example.com
102
+ service: http://web:8080
103
+ originRequest:
104
+ access:
105
+ aud: AUD-example-team
106
+ teamName: my-team
107
+ required: true
108
+ - service: http_status:404
109
+ ```
110
+
111
+ The shipped JSON schema at
112
+ `blueprints/edge-cloudflare-tunnel/contributions/probes/tunnel-manifest.schema.json`
113
+ requires: a uuid-shaped `tunnel` id, a `credentialsFile.secretRef`
114
+ reference (never a literal path or inline JSON body), and an ingress
115
+ rules array whose last entry is a catch-all `service: http_status:404`.
116
+ Every ingress service URL must bind to an internal address (a compose
117
+ service name or a loopback interface); binding to `0.0.0.0` or a
118
+ non-loopback numeric IP refuses at apply time.
119
+
120
+ ## Credentials rotation pattern
121
+
122
+ The tunnel credentials JSON file lands on the host at mode 0o400 (owner
123
+ read only) and is referenced from the manifest as a
124
+ `credentialsFile.secretRef`. To rotate:
125
+
126
+ 1. Regenerate the credentials via `cloudflared tunnel token <name>` or
127
+ the Cloudflare API against the tunnel id.
128
+ 2. Update the secret named by the manifest's `secretRef` via
129
+ `security-secrets-management`.
130
+ 3. Trigger a reapply on the applying project; the connector picks up
131
+ the new credentials on its next boot cycle.
132
+
133
+ The credentials file MUST NOT be committed to the applying tree; a
134
+ grep across the working tree for the tunnel id and account tag
135
+ literals refuses on any tracked file. Every `cloudflaredReady`,
136
+ `tunnelConnectorUp` and `ingressRuleReloaded` event body carries
137
+ metadata only; the event-secrecy scan refuses on a body that mentions
138
+ the credentials literal.
139
+
140
+ ## Composition on the shelf
141
+
142
+ Consumes: `platform-docker-compose-host` (compose-service runtime),
143
+ `deploy-hetzner-server` (bare cloudHost systemd runtime),
144
+ `security-secrets-management` (credentials secretRef). Optionally
145
+ consumes `edge-cloudflare-access` (the Access-gated shape).
146
+
147
+ Provides: `tunnelBridge` capability; new global topic
148
+ `edgeIngressBridge` (ADR-4001 scope global).
149
+
150
+ The `deliver-ci-workflows` shelf blueprint invokes the two account-bound
151
+ probes on a scratch subdomain when the account tokens are present;
152
+ without them the probes record `accountBoundSkipped: true` and the
153
+ aggregate flips to pass per hetzner-round-7-spec section 3.5.
154
+
155
+ ## Running the probes
156
+
157
+ Every reviewer boot exercises the three local probes on the fixture:
158
+
159
+ ```
160
+ cd packages/rcf-lite/test/fixtures/hetzner-throwaway-server
161
+ node ./run-tunnel-manifest-schema-validate.mjs
162
+ node ./run-cloudflared-config-lint.mjs
163
+ node ./run-aud-presence-check.mjs
164
+ ```
165
+
166
+ Every probe writes its report envelope to
167
+ `.rcf/reports/blueprints/edge-cloudflare-tunnel/<probe-name>.json`
168
+ under the repo root.
169
+
170
+ Mutations (fixture-side, one per switch; the probe modules never read
171
+ `SIMULATE_` env vars):
172
+
173
+ - `SIMULATE_MANIFEST_INVALID_TUNNEL_ID=true` on `run-tunnel-manifest-schema-validate.mjs`
174
+ - `SIMULATE_MANIFEST_CREDENTIALS_INLINE=true` on `run-tunnel-manifest-schema-validate.mjs`
175
+ - `SIMULATE_MANIFEST_MISSING_CATCHALL=true` on `run-tunnel-manifest-schema-validate.mjs`
176
+ - `SIMULATE_ORIGIN_PORT_OPEN=true` on `run-tunnel-manifest-schema-validate.mjs`
177
+ - `SIMULATE_EVENT_SECRECY_LEAK=true` on `run-tunnel-manifest-schema-validate.mjs`
178
+ - `SIMULATE_INGRESS_INVALID=true` on `run-cloudflared-config-lint.mjs`
179
+ - `SIMULATE_AUD_DROP=true` on `run-aud-presence-check.mjs`
180
+
181
+ Real-account probes need `CI_HAS_CLOUDFLARE_ACCOUNT=true` and
182
+ `CI_HAS_HETZNER_ACCOUNT=true` (plus `CI_HAS_CLOUDFLARE_ACCESS=true` for
183
+ the gated sub-case per the round-7 Q4 ratification):
184
+
185
+ ```
186
+ CI_HAS_CLOUDFLARE_ACCOUNT=true CI_HAS_HETZNER_ACCOUNT=true \
187
+ node ./run-real-account-connector-healthy.mjs
188
+ CI_HAS_CLOUDFLARE_ACCOUNT=true CI_HAS_HETZNER_ACCOUNT=true CI_HAS_CLOUDFLARE_ACCESS=true \
189
+ node ./run-real-account-tunnel-hostname-routes.mjs
190
+ ```
191
+
192
+ Without the env vars the probes record `accountBoundSkipped: true` and
193
+ the aggregate flips to pass.
194
+
195
+ ## Standards trace
196
+
197
+ - Cloudflare Tunnel overview: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
198
+ - Get-started with Tunnel: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/
199
+ - cloudflared downloads: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
200
+ - Routing to a tunnel: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/routing-to-tunnel/
201
+ - Local configuration file: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/local-management/configuration-file/
202
+ - cloudflared as-a-service: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/install-and-setup/tunnel-guide/local/as-a-service/
203
+ - Self-hosted app in front of a tunnel (Access): https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-public-app/
204
+ - Cloudflare Access policies: https://developers.cloudflare.com/cloudflare-one/policies/access/
@@ -55,6 +55,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
55
55
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
56
56
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
57
57
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
58
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
59
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
60
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
58
61
 
59
62
  US 35101-35108 sit at the LOW end of the 35101-35899 band on purpose (watchpost run-4 lesson). ADR/TAC suffixes for this blueprint use the 3601-3699 block, continuing the shelf pattern.
60
63
 
@@ -51,6 +51,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
51
51
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
52
52
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
53
53
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
54
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
55
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
56
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
54
57
 
55
58
  US 4101-4106 sit at the LOW end of the 4101-4899 band on purpose. A project-side story that mechanically derives from an email-smtp-resend REQ id into the number `4106` (leading `4` + sequence `106`) would collide against email-smtp-resend-US-4106 in this package; the band leaves headroom at the HIGH end (US 4181-4899) so a project's own stories anchored to email-smtp-resend REQs can allocate without conflict. The watchpost run4 lesson applies here too.
56
59
 
@@ -52,6 +52,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
52
52
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
53
53
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
54
54
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
55
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
56
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
57
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
55
58
 
56
59
  US 30101-30109 sit at the LOW end of the 30101-30899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from a jobs-background REQ id into the number 30110 would collide against a shipped US-30110; band headroom (30110-30899) leaves that space.
57
60
 
@@ -52,6 +52,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
52
52
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
53
53
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
54
54
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
55
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
56
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
57
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
55
58
 
56
59
  US 29101-29108 sit at the LOW end of the 29101-29899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from a messaging-queue-cloudflare REQ id into the number 29110 would collide against a shipped US-29110; band headroom (29109-29899) leaves that space.
57
60
 
@@ -54,6 +54,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
54
54
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
55
55
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
56
56
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
57
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
58
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
59
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
57
60
 
58
61
  US 28101-28108 sit at the LOW end of the 28101-28899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from an object-storage-s3 REQ id into the number 28110 would collide against a shipped US-28110; band headroom (28109-28899) leaves that space.
59
62
 
@@ -70,6 +70,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
70
70
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
71
71
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
72
72
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
73
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
74
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
75
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
73
76
 
74
77
  US 7101-7110 sit at the LOW end of the 7101-7899 band on purpose. A project-side story that mechanically derives from an observability-essentials REQs id into the number `7110` would collide against observability-essentials-US-7110 in this package; the band leaves headroom at the HIGH end (US 7181-7899) so a project's own stories anchored to observability-essentials REQs can allocate without conflict. The watchpost run4 lesson applies here too.
75
78
 
@@ -37,5 +37,8 @@
37
37
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
38
38
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
39
39
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
40
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
41
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
42
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
40
43
 
41
44
  The shelf-wide band registry lives in `packages/rcf-lite/docs/blueprint-authoring.md` section 5.
@@ -60,6 +60,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
60
60
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
61
61
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
62
62
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
63
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
64
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
65
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
63
66
 
64
67
  US 14101-14108 sit at the LOW end of the 14101-14899 band on purpose. A project-side story that mechanically derives from an observability-probe-endpoints REQ id into the number `14108` would collide against observability-probe-endpoints-US-14108 in this package; the band leaves headroom at the HIGH end (US 14181-14899) so a project's own stories anchored to observability-probe-endpoints REQs can allocate without conflict. The watchpost run4 lesson applies here too. Band spacing between the previous HQ-lane row (persistence-data-d1, 13101-13899, 14xx) and this one (14101-14899, 15xx) reflects the four-digit widening pattern opened by security-auth-clerk at 10xx and continued through the round-2 HQ lane.
65
68
 
@@ -58,6 +58,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
58
58
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
59
59
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
60
60
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
61
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
62
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
63
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
61
64
 
62
65
  US 13101-13107 sit at the LOW end of the 13101-13899 band on purpose. A project-side story that mechanically derives from a persistence-data-d1 REQ id into the number `13107` would collide against persistence-data-d1-US-13107 in this package; the band leaves headroom at the HIGH end (US 13181-13899) so a project's own stories anchored to persistence-data-d1 REQs can allocate without conflict. The watchpost run4 lesson applies here too.
63
66
 
@@ -56,6 +56,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
56
56
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
57
57
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
58
58
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
59
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
60
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
61
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
59
62
 
60
63
  US 27101-27110 sit at the LOW end of the 27101-27899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from a persistence-data-postgres REQ id into the number 27110 would collide against the shipped US-27110; band headroom (27111-27899) leaves that space.
61
64
 
@@ -58,6 +58,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
58
58
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
59
59
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
60
60
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
61
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
62
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
63
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
61
64
 
62
65
  US 5101-5111 sit at the LOW end of the 5101-5899 band on purpose. A project-side story that mechanically derives from `persistence-data-sqlite-REQ-011` into the number `5111` would collide against persistence-data-sqlite-US-5111 in this package; the band leaves headroom at the HIGH end (US 5181-5899) so a project's own stories anchored to persistence-data-sqlite REQs can allocate without conflict. The watchpost run4 lesson applies here too.
63
66
 
@@ -55,6 +55,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
55
55
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
56
56
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
57
57
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
58
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
59
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
60
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
58
61
 
59
62
  US 32101-32107 sit at the LOW end of the 32101-32899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from a platform-cloudflare-cron-triggers REQ id into the number 32110 would collide against a shipped US-32110; band headroom (32108-32899) leaves that space.
60
63
 
@@ -56,6 +56,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
56
56
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
57
57
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
58
58
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
59
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
60
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
61
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
59
62
 
60
63
  US 33101-33112 sit at the LOW end of the 33101-33899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from a platform-cloudflare-durable-objects REQ id into the number 33113 would collide against a shipped US-33113; band headroom (33113-33899) leaves that space.
61
64
 
@@ -55,6 +55,9 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
55
55
  | edge-cloudflare-access | 34101-34899 | 35xx | shipped v1.0.0 | `edgeAuthenticationGate` |
56
56
  | edge-cloudflare-turnstile | 35101-35899 | 36xx | shipped v1.0.0 | `humanVerificationGate` |
57
57
  | edge-cloudflare-rate-limiting | 36101-36899 | 37xx | shipped v1.0.0 | `edgeThrottleContract` |
58
+ | deploy-hetzner-server | 37101-37899 | 38xx | shipped v1.0.0 | `linuxCloudHostContract`, `snapshotAndBackupCadence` |
59
+ | platform-docker-compose-host | 38101-38899 | 39xx | shipped v1.0.0 | `containerHostContract` |
60
+ | edge-cloudflare-tunnel | 39101-39899 | 40xx | shipped v1.0.0 | `edgeIngressBridge` |
58
61
 
59
62
  US 31101-31108 sit at the LOW end of the 31101-31899 band on purpose (watchpost run-4 lesson). A project-side story mechanically derived from a platform-cloudflare-kv REQ id into the number 31110 would collide against a shipped US-31110; band headroom (31109-31899) leaves that space.
60
63
 
@@ -0,0 +1,12 @@
1
+ # platform-docker-compose-host CHANGELOG
2
+
3
+ ## 1.0.0 - 2026-09-08
4
+
5
+ Initial release. Round-7 T-2 of the Hetzner spec at `projects/blueprint-library/specs/hetzner-round-7-spec-2026-09-07.md`.
6
+
7
+ - Mints capability `containerHost` and global topic `containerHostContract`.
8
+ - Six REQs, nine USs, four TACs, four ADRs; five Node-only probes.
9
+ - Extends the shared throwaway-Hetzner-server fixture with a minimal compose stack (`compose.yaml`, `caddy/Caddyfile`, `secrets/web-token` file mount, `src/serve.mjs` healthchecked stub) plus five `run-<probe>.mjs` delegate shims.
10
+ - Compose-config-lint carries four mutation switches (`SIMULATE_MISSING_HEALTHCHECK`, `SIMULATE_UNCLASSIFIED_RESTART`, `SIMULATE_UNCLASSIFIED_LOG_DRIVER`, `SIMULATE_EVENT_SECRECY_LEAK`); secrets-as-files-scan carries `SIMULATE_PLAINTEXT_SECRET`; caddyfile-validate carries `SIMULATE_INVALID_CADDYFILE` and runs `caddy validate` via the `caddy:2` container when a local `caddy` binary is not on PATH.
11
+ - Reverse-proxy choice elicited via `reverse-proxy` (caddy default per Baz decision 19; traefik and none alternatives); Coolify rejected verbatim per Baz decision 20; log driver elicited via `log-driver` (journald default; loki opt-in).
12
+ - Consumers: `w-2026-09-07-dave-004` migrates the ops-01 remote-library compose stack onto this contract after 0.25.0 ships; blueprint acceptance never depends on that migration.
@@ -0,0 +1,76 @@
1
+ # platform-docker-compose-host v1.0.0
2
+
3
+ A container-orchestration facade over docker compose running on a Linux `cloudHost`. Provides `containerHost` for consumer blueprints that ship as containers.
4
+
5
+ ## What it commits
6
+
7
+ Six REQs, nine USs, four TACs, four ADRs and five Node-only probes. Round-7 T-2 of the Hetzner spec at `projects/blueprint-library/specs/hetzner-round-7-spec-2026-09-07.md`.
8
+
9
+ | REQ | Contract |
10
+ |---|---|
11
+ | REQ-001 | `compose.yaml` at repo root is the single-source runtime topology (service-per-container, named networks and volumes, `.env` for non-secret config) |
12
+ | REQ-002 | Every secret injected via a docker compose secrets file mount; no plaintext literal in `compose.yaml` (grep-refuse); references `security-secrets-management` when applied |
13
+ | REQ-003 | Healthcheck contract on every HTTP-terminating service; `docker compose config` refuses on missing |
14
+ | REQ-004 | Restart discipline enforced at lint: `unless-stopped` on long-running, `on-failure` on jobs; unclassified value refuses |
15
+ | REQ-005 | Zero-downtime reverse-proxy reload via bind-mounted config; verb per proxy; no non-2xx or dropped connection inside the elicited window (10 s default) |
16
+ | REQ-006 | Log shipping to the elicited `observability-logging` sink: `journald` default matching Debian 12 / Ubuntu 24.04 baseline; `loki` opt-in when the project runs Grafana |
17
+
18
+ ## Capability
19
+
20
+ - `containerHost` (extends `blueprint-authoring.md` section 6a).
21
+
22
+ ## Suggested companions
23
+
24
+ - `logging`: every `composeStackReady`, `containerHealthy`, `containerCrashed` and `reverseProxyReloaded` event writes through the applied logger; a logging companion supplies the factory and the log-driver binding.
25
+ - `errorHandling`: a healthcheck timeout, a compose lint refusal, a secret-file-mode mismatch, a reload window overshoot constructs an internal error record; an error-handling companion supplies the record factory and the boundary.
26
+
27
+ ## Elicits (via `elicits[]`)
28
+
29
+ - `reverse-proxy`: `caddy` (default per Baz decision 19, matches dev-01 and ops-01) | `traefik` (alternative for docker-label auto-discovery) | `none` (alternative for projects fronted only by the round-7 tunnel).
30
+ - `docker-engine-channel`: `stable` (default) | `test`.
31
+ - `compose-file-location`: `repo-root` (default) | `compose/compose.yaml`.
32
+ - `healthcheck-default-kind`: `http` (default) | `tcp` | `command`.
33
+ - `log-driver`: `journald` (default per ADR-3904) | `loki` (opt-in).
34
+ - `reload-window-seconds`: default `10`.
35
+
36
+ ## ADR shape
37
+
38
+ - ADR-3901 container host contract: scope `global` on new topic `containerHostContract`; standards trace clause `Docker Compose file reference AND Docker Engine install-on-Ubuntu docs per hetzner-round-7-spec-2026-09-07.md`.
39
+ - ADR-3902 reverse proxy: elicited, `caddy` default; standards trace clause `Caddy docs AND Traefik docs per hetzner-round-7-spec-2026-09-07.md`.
40
+ - ADR-3903 reject Coolify: decision quoted verbatim from Baz ruling 20 ("reject; it shadows what the blueprints contract and adds its own DB and UI as a second source of state"); standards trace clause `generic enterprise practice`.
41
+ - ADR-3904 log driver: elicited, `journald` default; standards trace clause `generic enterprise practice`.
42
+
43
+ ## Probes
44
+
45
+ Five modules under `contributions/probes/`, each writes an envelope to `.rcf/reports/blueprints/platform-docker-compose-host/<probe>.json`:
46
+
47
+ - `compose-config-lint` (accountBound: false; anchors AC-composeHost-healthcheckLint, AC-composeHost-restartClassification, AC-composeHost-logDriverClassification). Mutations: `SIMULATE_MISSING_HEALTHCHECK`, `SIMULATE_UNCLASSIFIED_RESTART`, `SIMULATE_UNCLASSIFIED_LOG_DRIVER`, `SIMULATE_EVENT_SECRECY_LEAK`.
48
+ - `secrets-as-files-scan` (accountBound: false; anchors AC-composeHost-secretsAreFiles, AC-composeHost-secretShape). Mutation: `SIMULATE_PLAINTEXT_SECRET`.
49
+ - `caddyfile-validate` (accountBound: false; anchors AC-composeHost-reverseProxyArtefactValid). Runs a local `caddy` binary when present, otherwise the `caddy:2` container via `docker run --rm`. Mutation: `SIMULATE_INVALID_CADDYFILE`. Skipped when `REVERSE_PROXY` is not `caddy`.
50
+ - `real-account-minimal-stack-up` (accountBound: true; anchors AC-composeHost-upClean). Provisions the throwaway server, `scp`s the compose stack, runs `docker compose up -d --wait`, asserts every declared service reaches healthy inside the elicited timeout, tears down in `always()`. Records `accountBoundSkipped: true` without `CI_HAS_HETZNER_ACCOUNT`.
51
+ - `real-account-reload-burst` (accountBound: true; anchors AC-composeHost-zeroDowntimeReload). Fires an `undici` burst against the caddy service while `docker compose exec caddy caddy reload` runs, asserts every request returns 2xx inside the elicited window with no dropped connection, tears down in `always()`. Records `accountBoundSkipped: true` without `CI_HAS_HETZNER_ACCOUNT`.
52
+
53
+ ## Running the probes
54
+
55
+ From the shared throwaway-server fixture directory:
56
+
57
+ ```
58
+ cd packages/rcf-lite/test/fixtures/hetzner-throwaway-server
59
+ node ./run-compose-config-lint.mjs && node ./run-secrets-as-files-scan.mjs && node ./run-caddyfile-validate.mjs
60
+ ```
61
+
62
+ Real-account probes gate on `CI_HAS_HETZNER_ACCOUNT`:
63
+
64
+ ```
65
+ cd packages/rcf-lite/test/fixtures/hetzner-throwaway-server
66
+ CI_HAS_HETZNER_ACCOUNT=true HCLOUD_TOKEN=$HETZNER_ACCOUNT_API_KEY node ./run-real-account-minimal-stack-up.mjs && node ./run-real-account-reload-burst.mjs
67
+ ```
68
+
69
+ ## Fixture extension path
70
+
71
+ Round-7 T-1 (`deploy-hetzner-server`) minted the shared fixture at `packages/rcf-lite/test/fixtures/hetzner-throwaway-server/`. T-2 EXTENDS that fixture, never duplicates it: `compose.yaml`, `caddy/Caddyfile`, `secrets/web-token`, `src/serve.mjs` and five `run-<probe>.mjs` delegate shims land alongside T-1's `provision.mjs`/`destroy.mjs`/`sweep-orphans.mjs`. T-3 (`edge-cloudflare-tunnel`) extends the same fixture further; consumers of that fixture see one folder, one manifest, three tracks worth of runtime.
72
+
73
+ ## When to reach for this blueprint
74
+
75
+ - Any project running a containerised stack on a `cloudHost` (T-1 `deploy-hetzner-server` provides the shipped v1.0.0 provider).
76
+ - Any project that wants secrets as file mounts (not environment literals), healthchecks that gate boot, a checked-in reverse-proxy artefact, and a log driver on the elicited sink.