cloudfall 0.5.1__tar.gz → 0.6.0rc1__tar.gz

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 (312) hide show
  1. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/ARCHITECTURE.md +2 -2
  2. cloudfall-0.6.0rc1/CHANGELOG.md +588 -0
  3. cloudfall-0.6.0rc1/CODE_OF_CONDUCT.md +83 -0
  4. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/CONTRIBUTING.md +13 -0
  5. cloudfall-0.6.0rc1/PKG-INFO +232 -0
  6. cloudfall-0.6.0rc1/README.md +204 -0
  7. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/ROADMAP.md +2 -2
  8. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/Taskfile.yml +5 -5
  9. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/brownfield-design.md +1 -1
  10. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/fleet-goals.md +2 -2
  11. cloudfall-0.6.0rc1/docs/getting-started.md +149 -0
  12. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/landscape.md +4 -4
  13. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/logging-service-guide.md +10 -10
  14. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/neon-migration-guide.md +10 -10
  15. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/operator-guide.md +25 -14
  16. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/render-migration-guide.md +9 -8
  17. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/secrets-guide.md +6 -6
  18. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/README.md +26 -2
  19. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/tasks/main.yml +33 -1
  20. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/artifact.py +38 -29
  21. cloudfall-0.6.0rc1/engine/src/cloudfall_engine/cli.py +473 -0
  22. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/playbook.py +2 -12
  23. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_ansible_inventory.py +47 -11
  24. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_artifact_builder.py +62 -23
  25. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_playbook.py +126 -8
  26. cloudfall-0.6.0rc1/engine/tests/test_time_role.py +119 -0
  27. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/render.yaml +1 -1
  28. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/pyproject.toml +25 -6
  29. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/README.md +28 -23
  30. cloudfall-0.6.0rc1/sdk/src/cloudfall/agent_server.py +449 -0
  31. cloudfall-0.6.0rc1/sdk/src/cloudfall/agent_tools.py +52 -0
  32. cloudfall-0.6.0rc1/sdk/src/cloudfall/app.py +3626 -0
  33. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/audit.py +4 -1
  34. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/authoring.py +15 -7
  35. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/catalog.py +3 -1
  36. cloudfall-0.6.0rc1/sdk/src/cloudfall/cli.py +21 -0
  37. cloudfall-0.6.0rc1/sdk/src/cloudfall/commands.py +587 -0
  38. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/dashboard_server.py +28 -2
  39. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/decision.py +5 -5
  40. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/importer.py +1 -1
  41. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/lifecycle.py +73 -40
  42. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/observe.py +33 -6
  43. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/operator.py +102 -17
  44. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/project.py +62 -23
  45. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/render_api.py +7 -3
  46. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/secrets.py +4 -4
  47. cloudfall-0.6.0rc1/sdk/src/cloudfall/why.py +436 -0
  48. cloudfall-0.6.0rc1/sdk/tests/test_agent_server.py +400 -0
  49. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_ansible_reader.py +12 -10
  50. cloudfall-0.6.0rc1/sdk/tests/test_app.py +180 -0
  51. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_arguments.py +35 -74
  52. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_audit.py +59 -5
  53. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_authoring.py +79 -5
  54. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_catalog.py +9 -7
  55. cloudfall-0.6.0rc1/sdk/tests/test_commands.py +112 -0
  56. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_dashboard_server.py +20 -0
  57. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_decision.py +16 -13
  58. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_importer.py +31 -4
  59. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_inventory.py +3 -2
  60. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_lifecycle.py +109 -25
  61. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_migrate.py +9 -5
  62. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_observe.py +56 -8
  63. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_operator.py +192 -1
  64. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_project.py +28 -25
  65. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_render_api.py +5 -4
  66. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_validation.py +14 -9
  67. cloudfall-0.6.0rc1/sdk/tests/test_why.py +227 -0
  68. cloudfall-0.5.1/.github/workflows/ci.yml +0 -53
  69. cloudfall-0.5.1/.github/workflows/release.yml +0 -78
  70. cloudfall-0.5.1/CHANGELOG.md +0 -248
  71. cloudfall-0.5.1/PKG-INFO +0 -418
  72. cloudfall-0.5.1/README.md +0 -392
  73. cloudfall-0.5.1/engine/src/cloudfall_engine/cli.py +0 -273
  74. cloudfall-0.5.1/evaluations/cloudfall/README.md +0 -43
  75. cloudfall-0.5.1/evaluations/cloudfall/environment.md +0 -61
  76. cloudfall-0.5.1/evaluations/cloudfall/findings.md +0 -26
  77. cloudfall-0.5.1/evaluations/cloudfall/issues.md +0 -78
  78. cloudfall-0.5.1/evaluations/cloudfall/linkedin.md +0 -33
  79. cloudfall-0.5.1/evaluations/cloudfall/readiness.md +0 -84
  80. cloudfall-0.5.1/evaluations/cloudfall/report-agent-dev.md +0 -1003
  81. cloudfall-0.5.1/evaluations/cloudfall/report-dev.md +0 -1048
  82. cloudfall-0.5.1/evaluations/cloudfall/report-index.md +0 -49
  83. cloudfall-0.5.1/evaluations/cloudfall/report-issues.md +0 -410
  84. cloudfall-0.5.1/evaluations/cloudfall/report-runtime.md +0 -74
  85. cloudfall-0.5.1/evaluations/cloudfall/trace.md +0 -1276
  86. cloudfall-0.5.1/evaluations/cloudfall/x.md +0 -45
  87. cloudfall-0.5.1/sdk/src/cloudfall/agent_tools.py +0 -868
  88. cloudfall-0.5.1/sdk/src/cloudfall/arguments.py +0 -92
  89. cloudfall-0.5.1/sdk/src/cloudfall/cli.py +0 -2138
  90. cloudfall-0.5.1/sdk/src/cloudfall/commands.py +0 -294
  91. cloudfall-0.5.1/sdk/src/cloudfall/fleet_tools.py +0 -249
  92. cloudfall-0.5.1/sdk/src/cloudfall/mcp_server.py +0 -944
  93. cloudfall-0.5.1/sdk/tests/test_agent_tools.py +0 -362
  94. cloudfall-0.5.1/sdk/tests/test_commands.py +0 -91
  95. cloudfall-0.5.1/sdk/tests/test_fleet_tools.py +0 -301
  96. cloudfall-0.5.1/site/public/apple-touch-icon.png +0 -0
  97. cloudfall-0.5.1/site/public/favicon-32.png +0 -0
  98. cloudfall-0.5.1/site/public/icon-192.png +0 -0
  99. cloudfall-0.5.1/site/public/icon-512.png +0 -0
  100. cloudfall-0.5.1/site/public/index.html +0 -491
  101. cloudfall-0.5.1/site/public/manifesto.html +0 -367
  102. cloudfall-0.5.1/site/public/og.png +0 -0
  103. cloudfall-0.5.1/site/public/site.webmanifest +0 -23
  104. cloudfall-0.5.1/site/src/worker.js +0 -10
  105. cloudfall-0.5.1/site/wrangler.jsonc +0 -18
  106. cloudfall-0.5.1/uv.lock +0 -1198
  107. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/.ansible-lint +0 -0
  108. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/.gitignore +0 -0
  109. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/LICENSE +0 -0
  110. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/MANIFESTO.md +0 -0
  111. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/SECURITY.md +0 -0
  112. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/README.md +0 -0
  113. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/alert-rules/postgresql-down.yaml +0 -0
  114. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/applications/crm.yaml +0 -0
  115. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/components/crm-backend.yaml +0 -0
  116. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/domains/crm-site.yaml +0 -0
  117. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/logging-stacks/operations.yaml +0 -0
  118. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/operator-policies/production.yaml +0 -0
  119. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/server-types/debian-application.yaml +0 -0
  120. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/servers/h1.yaml +0 -0
  121. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/servers/h2.yaml +0 -0
  122. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/services/postgresql-main.yaml +0 -0
  123. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/services/redis-cache.yaml +0 -0
  124. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/ssh-public-keys/example-admin.yaml +0 -0
  125. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/alert-rule.schema.json +0 -0
  126. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/application.schema.json +0 -0
  127. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/artifact.schema.json +0 -0
  128. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/backup-receipt.schema.json +0 -0
  129. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/common.schema.json +0 -0
  130. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/component.schema.json +0 -0
  131. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/deployment-receipt.schema.json +0 -0
  132. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/domain.schema.json +0 -0
  133. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/environment-receipt.schema.json +0 -0
  134. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/logging-stack.schema.json +0 -0
  135. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/observed-domain.schema.json +0 -0
  136. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/observed-server.schema.json +0 -0
  137. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operation-decision.schema.json +0 -0
  138. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operation.schema.json +0 -0
  139. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operator-policy.schema.json +0 -0
  140. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operator-proposal.schema.json +0 -0
  141. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/release-receipt.schema.json +0 -0
  142. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/server-type.schema.json +0 -0
  143. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/server.schema.json +0 -0
  144. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/service.schema.json +0 -0
  145. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/ssh-public-key.schema.json +0 -0
  146. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/applications/crm.yaml +0 -0
  147. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/components/crm-backend.yaml +0 -0
  148. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/server-types/debian-application.yaml +0 -0
  149. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/servers/h1.yaml +0 -0
  150. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/schema/components/component-unknown-field.yaml +0 -0
  151. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/observed/compliant/h1.json +0 -0
  152. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/observed/compliant/h2.json +0 -0
  153. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/ansible-context.md +0 -0
  154. /cloudfall-0.5.1/site/public/favicon.svg → /cloudfall-0.6.0rc1/docs/assets/logo.svg +0 -0
  155. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/availability-design.md +0 -0
  156. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/decisions/2026-09-14-no-ansible-replacement.md +0 -0
  157. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/decisions/2026-09-14-no-nixos-base.md +0 -0
  158. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/decisions/2026-09-14-no-rust-rewrite.md +0 -0
  159. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/hybrid-storage-design.md +0 -0
  160. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/new-server-storage-guide.md +0 -0
  161. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-debian-container-m1.md +0 -0
  162. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-dns-cutover.md +0 -0
  163. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-guided-data-migration.md +0 -0
  164. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-hetzner-m1.md +0 -0
  165. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-hetzner-m2-m3.md +0 -0
  166. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-hetzner-m4-m5.md +0 -0
  167. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-render-cutover.md +0 -0
  168. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-10-hetzner-m7.md +0 -0
  169. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-10-hetzner-m8.md +0 -0
  170. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-10-hetzner-m9.md +0 -0
  171. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-11-hetzner-m10.md +0 -0
  172. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/ansible.cfg +0 -0
  173. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/backup.yml +0 -0
  174. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/baseline.yml +0 -0
  175. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/bootstrap.yml +0 -0
  176. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/data.yml +0 -0
  177. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/deploy.yml +0 -0
  178. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/domains.yml +0 -0
  179. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/health.yml +0 -0
  180. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/inspect.yml +0 -0
  181. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/logging.yml +0 -0
  182. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/restart.yml +0 -0
  183. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/rollback.yml +0 -0
  184. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/services.yml +0 -0
  185. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/time.yml +0 -0
  186. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/defaults/main.yml +0 -0
  187. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/handlers/main.yml +0 -0
  188. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/meta/argument_specs.yml +0 -0
  189. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/meta/main.yml +0 -0
  190. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/tasks/main.yml +0 -0
  191. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/defaults/main.yml +0 -0
  192. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +0 -0
  193. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/meta/main.yml +0 -0
  194. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/tasks/main.yml +0 -0
  195. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/defaults/main.yml +0 -0
  196. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +0 -0
  197. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/meta/main.yml +0 -0
  198. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/tasks/main.yml +0 -0
  199. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/defaults/main.yml +0 -0
  200. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +0 -0
  201. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/meta/main.yml +0 -0
  202. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/health.yml +0 -0
  203. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +0 -0
  204. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/main.yml +0 -0
  205. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/restart.yml +0 -0
  206. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/rollback.yml +0 -0
  207. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/templates/component.service.j2 +0 -0
  208. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/defaults/main.yml +0 -0
  209. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/handlers/main.yml +0 -0
  210. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +0 -0
  211. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/meta/main.yml +0 -0
  212. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/tasks/main.yml +0 -0
  213. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +0 -0
  214. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/defaults/main.yml +0 -0
  215. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +0 -0
  216. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/meta/main.yml +0 -0
  217. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/tasks/main.yml +0 -0
  218. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/defaults/main.yml +0 -0
  219. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/handlers/main.yml +0 -0
  220. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +0 -0
  221. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/meta/main.yml +0 -0
  222. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/tasks/main.yml +0 -0
  223. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +0 -0
  224. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +0 -0
  225. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +0 -0
  226. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +0 -0
  227. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +0 -0
  228. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +0 -0
  229. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +0 -0
  230. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +0 -0
  231. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +0 -0
  232. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +0 -0
  233. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +0 -0
  234. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/defaults/main.yml +0 -0
  235. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/handlers/main.yml +0 -0
  236. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +0 -0
  237. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/meta/main.yml +0 -0
  238. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/tasks/main.yml +0 -0
  239. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +0 -0
  240. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +0 -0
  241. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/defaults/main.yml +0 -0
  242. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/handlers/main.yml +0 -0
  243. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +0 -0
  244. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/meta/main.yml +0 -0
  245. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/tasks/main.yml +0 -0
  246. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +0 -0
  247. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/defaults/main.yml +0 -0
  248. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/handlers/main.yml +0 -0
  249. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +0 -0
  250. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/meta/main.yml +0 -0
  251. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/tasks/main.yml +0 -0
  252. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +0 -0
  253. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +0 -0
  254. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +0 -0
  255. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +0 -0
  256. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +0 -0
  257. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +0 -0
  258. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +0 -0
  259. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/defaults/main.yml +0 -0
  260. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/handlers/main.yml +0 -0
  261. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/meta/argument_specs.yml +0 -0
  262. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/meta/main.yml +0 -0
  263. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/tasks/main.yml +0 -0
  264. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/backup.service.j2 +0 -0
  265. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +0 -0
  266. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +0 -0
  267. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +0 -0
  268. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +0 -0
  269. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +0 -0
  270. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +0 -0
  271. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/defaults/main.yml +0 -0
  272. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/meta/argument_specs.yml +0 -0
  273. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/meta/main.yml +0 -0
  274. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +0 -0
  275. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +0 -0
  276. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +0 -0
  277. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +0 -0
  278. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/__init__.py +0 -0
  279. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/__main__.py +0 -0
  280. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/ansible_inventory.py +0 -0
  281. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/py.typed +0 -0
  282. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_deploy_templates.py +0 -0
  283. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_firewall_templates.py +0 -0
  284. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_logging_templates.py +0 -0
  285. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_nginx_site_templates.py +0 -0
  286. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_postgresql_templates.py +0 -0
  287. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/README.md +0 -0
  288. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/pyproject.toml +0 -0
  289. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/src/demo/__init__.py +0 -0
  290. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/src/demo/asgi.py +0 -0
  291. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/uv.lock +0 -0
  292. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/__init__.py +0 -0
  293. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/__main__.py +0 -0
  294. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/ansible_api.py +0 -0
  295. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/ansible_reader.py +0 -0
  296. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/cutover.py +0 -0
  297. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/dashboard.py +0 -0
  298. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/domain.py +0 -0
  299. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/inventory.py +0 -0
  300. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/migrate.py +0 -0
  301. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/observation.py +0 -0
  302. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/operations.py +0 -0
  303. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/py.typed +0 -0
  304. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/resources.py +0 -0
  305. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/service_evidence.py +0 -0
  306. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/validation.py +0 -0
  307. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_cutover.py +0 -0
  308. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_data_migration.py +0 -0
  309. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_operations_dashboard.py +0 -0
  310. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_resources.py +0 -0
  311. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_secrets.py +0 -0
  312. {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_service_evidence.py +0 -0
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Cloudfall is the operator's record for a small fleet run by an AI agent.
3
+ Cloudfall is the operator's record for self-hosted servers run by an AI agent.
4
4
  The agent decides, Ansible executes, and Cloudfall sits between them: it
5
5
  declares what the agent may do, checks every result, and keeps the evidence
6
6
  behind every decision. Ansible is the hand, the agent is the brain,
@@ -32,7 +32,7 @@ Human / AI agent ──runs───► CLI / MCP / Python API ──► Gate
32
32
 
33
33
  The config is a store, not a pipeline stage: humans and agents edit it
34
34
  directly, the CLI validates and reads it, and the engine converges servers
35
- to it. The agent operates through a stable CLI, the `cloudfall-mcp` server
35
+ to it. The agent operates through a stable CLI, the `cloudfall mcp serve` server
36
36
  and a Python API instead of inventing shell commands or discovering
37
37
  infrastructure over SSH. Every mutating operation goes through the engine's
38
38
  explicit playbook contracts, waits behind a gate the agent does not control
@@ -0,0 +1,588 @@
1
+ # Changelog
2
+
3
+ Notable changes to Cloudfall. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
+ [semantic versioning](https://semver.org/) once the project reaches 1.0.
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - `cloudfall why` answers "why did the agent do that" from the decision
12
+ records alone, for a host (`--host`), an operation (`--operation`) or a
13
+ time window (`--since`, `--until`, ISO 8601 or a bare date), as JSON or
14
+ as one HTML page (`--format html`). Each decision is told as what it was
15
+ based on, what was proposed, what check mode showed, what gated it, who
16
+ approved, what the run and the verify step did and how it ended, every
17
+ sentence drawn from a field of the record. A decision is about a host
18
+ when the record names it, as the target or in the per-host evidence of
19
+ a stage; a group pattern is not expanded, because that would need an
20
+ inventory the record does not depend on. The same question is the
21
+ read-only `why` tool on `cloudfall-mcp --repository`
22
+ - Every JSON document `cloudfall` prints, results and errors alike, carries
23
+ `meta.schema_version` (`MAJOR.MINOR` of the output contract, now `1.0`)
24
+ and `meta.tool_version` (the installed package version), so an agent can
25
+ tell when the output shape may have changed without a separate call.
26
+ `meta.request_id` names the invocation with a UUID, the same on every
27
+ document it writes, for quoting in a retry or a bug report, and
28
+ `meta.duration_ms` counts the milliseconds from the start of the command
29
+ to that document, not counting Python's own startup.
30
+ MINOR moves when a command gains a key, MAJOR when one is removed,
31
+ renamed or changes meaning. `cloudfall --version` prints the same
32
+ version as JSON and exits 0
33
+ - Every JSON document also carries a `warnings` list. A key is removed only
34
+ after a release in which documents holding it warn `FIELD_DEPRECATED`
35
+ with the replacement key and the schema version that drops it
36
+ - `cloudfall changelog [--since MAJOR.MINOR]` lists changes to the output
37
+ contract, newest first: version, date, `breaking`, and the added,
38
+ removed and changed keys
39
+ - `cloudfall --schema-version MAJOR <command>` pins the output contract: it
40
+ fails with `invalid_argument` before the command runs when this build no
41
+ longer writes that MAJOR, so a pinned caller stops at a breaking release
42
+ instead of misreading it. `--version` and `changelog` report the current
43
+ and minimum supported versions
44
+ - `cloudfall --schema` (alias `--print-schema`) prints every command in one
45
+ JSON document: its flags, read from the parser; its effect and gate, from
46
+ the command catalog; and an `output_schema` per command, a JSON Schema of
47
+ each stdout shape with the condition that selects it. Every key, at the
48
+ top level and under `data`, carries `x-stability`: `stable` keys change
49
+ only in a MAJOR release after
50
+ a deprecation warning, `experimental` keys (unreleased commands such as
51
+ `why` and `changelog`) may change in any release. What each key holds is
52
+ not declared yet. The tests validate real output against these schemas,
53
+ and an `etag` changes only when a command, flag or declared output does
54
+
55
+ - Every error carries `exit_code`, the code the process exits with, so a
56
+ caller that reads the JSON needs no second channel: `{"code",
57
+ "message", "exit_code"}`. Results that exit non-zero, such as an
58
+ `audit` that finds drift, say so with `ok: false` and keep `error`
59
+ `null`
60
+ - Every `cloudfall` `--help` ends with the exit codes and what each one
61
+ means: 0 positive, 1 ran with a negative result, 2 nothing ran, 3
62
+ unknown. The generated `AGENTS.md` renders the same table from the same
63
+ source, so the two cannot drift
64
+ - `--quiet` writes nothing to stderr: no error document, no help text, and
65
+ no request lines from `dashboard serve`. The exit code still says what
66
+ happened, and results still go to stdout
67
+ - `--warnings-as-errors` fails a result that carries a warning: the
68
+ document says `ok: false` with the error `warnings_as_errors`, its
69
+ `status` and `data` unchanged, and the command exits 1 instead of 0
70
+
71
+ ### Changed
72
+
73
+ - The README opens with what Cloudfall is, a diagram of the gate, a short
74
+ quickstart and a guide index; the long walkthroughs moved to
75
+ [docs/getting-started.md](docs/getting-started.md). The repository has issue
76
+ forms (bug, feature, pilot request), a pull request template, a support
77
+ page and a code of conduct, and the PyPI summary matches the project's description
78
+ - The repository moved to the `cloudfall-dev` organization:
79
+ github.com/cloudfall-dev/cloudfall. The package metadata, the README,
80
+ the site and `REPOSITORY_URL` point there; the old
81
+ github.com/romamo/cloudfall address redirects
82
+ - Releases are cut by [shipmill](https://github.com/shipmill/shipmill) from
83
+ this CHANGELOG: an rc (`0.6.0rc1`, ...) after each batch of merges, and a
84
+ stable release promoted from an rc that soaked 3 days once its milestone
85
+ closes. The source distribution leaves out the repository's tooling,
86
+ evaluations and website
87
+ - **Breaking:** the CLI follows one name per meaning, and its output is
88
+ typed:
89
+ - Decision records have their own group: `decisions list` (was
90
+ `operations decisions`), `decisions approve` (was `operations
91
+ approve`), and a new `decisions show`. `operations` keeps the catalog:
92
+ `list`, `show`, `propose`
93
+ - `data migrate` is `data copy`, no longer read as `migrate`, and
94
+ `services inspect` is `services observe`, as `observe` collects server
95
+ evidence. Each old path answers 13 `REDIRECTED` with the new command in
96
+ `error.redirect.command`
97
+ - `migrate --release` and `--env-file` are `--component-release` and
98
+ `--component-env-file`: they take `COMPONENT=VALUE` pairs, where
99
+ `deploy`'s take one value
100
+ - `--observed` defaults to `tmp/observed` on every command, where
101
+ `observe` writes: `audit`, `services status` and `dashboard` required
102
+ it, and the operator's drift checks used `tmp/operator/observed`
103
+ - Field names in `data` are snake_case at every depth, as treaty's own
104
+ envelope keys are (`openProposals` is `open_proposals`, `wouldRun` is
105
+ `would_run`). Map keys that are data keep their spelling (resource
106
+ kinds, environment variable names, ids), and a record or resource in
107
+ `data`, anything with an `apiVersion`, keeps its stored form
108
+ - Every command's output schema gives each `data` key its JSON type, and
109
+ an optional list or object is written empty rather than `null`
110
+ (`inventory show` writes `ansible` as `{}` for a project)
111
+ - **Breaking:** argparse is gone: the last three `cloudfall` commands
112
+ move to treaty, and so do root `--help`, `--version` and `--schema`.
113
+ - `operator run` streams one envelope line per pass as it ends, with
114
+ `data.pass` (`alerts`, `drift`, `autonomy`) and `data.effect`:
115
+ `created` for a pass that wrote proposals, `updated` for one that ran
116
+ licensed proposals, `noop` otherwise. Stopping it ends the stream with a
117
+ `CANCELLED` line and exit 130 (SIGINT) or 143 (SIGTERM), so a systemd
118
+ unit needs `SuccessExitStatus=143` (the
119
+ [operator guide](docs/operator-guide.md) has it). A gateway it cannot
120
+ use (none declared, or a missing or unreadable certificate) is 4
121
+ `PRECONDITION`, an unreachable one 12 `UNAVAILABLE`. `--interval` and `--drift-interval` must be above 0. The
122
+ operating contract now lists it with the commands that change servers,
123
+ since a declared `OperatorPolicy` lets it run proposals
124
+ - `dashboard serve` streams one line once the server listens, then serves
125
+ until stopped, ending like `operator run`. Its request lines are info
126
+ log lines on stderr, shown at a terminal or with `--verbose`
127
+ - `changelog` is treaty's: it lists interface changes per release from
128
+ the packaged `schema-changelog.json`, which `treaty changelog-add
129
+ cloudfall.app:app` writes, and `--since` takes a release version
130
+ - `operator approve` reports such a gateway as 4 `PRECONDITION` (was 86
131
+ `RECORD_INVALID`) and an unreachable one as 12 `UNAVAILABLE`
132
+ - The argparse-era output contract added earlier in this release is
133
+ gone: the six-key document with a top-level `status`, `--output json`,
134
+ `--schema-version MAJOR` against that contract, the parser-built
135
+ `--schema`, the `--help` exit-code table and exit codes 0 to 3. Every
136
+ command answers treaty's envelope, and `cloudfall --schema` lists every
137
+ command's flags, output schema and exit codes. The `AGENTS.md` that
138
+ `cloudfall init` writes describes this contract
139
+ - **Breaking:** `cloudfall-mcp` is gone; the agent server is `cloudfall mcp
140
+ serve` (treaty 1.0.0rc29, from issues #239, #240, #281 and #285 filed for
141
+ it). Its tools are Cloudfall's own commands, answering with the CLI's
142
+ envelope, and tool names follow the commands (`list_operations` is
143
+ `operations_list`, `validate_config` is `config_validate`). MCP client
144
+ configs change from `cloudfall-mcp …` to `cloudfall mcp serve …`
145
+ - The server serves one fleet, chosen as before: `--project`, or
146
+ `--repository` for a team's Ansible repository. The fleet, the schema
147
+ and engine directories, and every directory a tool writes (snapshots,
148
+ receipts, artifacts, proposals, import output, the migrate plan) are
149
+ fixed for the run: they leave the tool schemas, and a call that passes
150
+ one is refused, so no call reaches another fleet or engine or writes
151
+ outside the project. So are the Render API URL `import_render-api`
152
+ sends its key file to, the env file `secrets_render` writes, and the
153
+ alert gateway `operator_approve` verifies against. Files a tool reads
154
+ as input (a blueprint, a key file, an env file) stay arguments
155
+ - On a project, `deploy`, `rollback`, `restart`, `migrate` and
156
+ `data_migrate` return their plan until called with `yes: true`, as on
157
+ the CLI; the old `confirm=true` handshake is gone. `build_artifact` and
158
+ the `converge_baseline`, `converge_services` and `converge_domains`
159
+ tools stay, the converges destructive and run only with
160
+ `confirm_destructive: true`. `operator_approve`, `backup_run` and
161
+ `backup_verify` run in one call, as their commands do (they were
162
+ confirm-gated in `cloudfall-mcp`), and `operator_watch` is gone: the
163
+ watch loop runs as a service
164
+ - On a repository, the tools are the read-only fleet commands and one
165
+ `operation_*` tool per declared operation, which runs check mode,
166
+ records a proposal and answers with the approval command in
167
+ `data.next`; a destructive operation's tool asks for
168
+ `confirm_destructive` even to preview
169
+ - Each directory has one flag name across the commands that read and
170
+ write it, so a server can fix it once and a non-default one reaches
171
+ them all: `observe --output-dir` is `--observed` (as `audit` reads it),
172
+ `services inspect --output-dir` is `--service-observed` (as `services
173
+ status` reads it), and `--receipts` is `--releases` on `deploy` and
174
+ `migrate`, `--backups` on `backup run|verify`, `--data-migrations` on
175
+ `data migrate` and `--env-receipts` on `secrets render` (as `audit`
176
+ reads it). `mcp serve` takes `--releases`, `--backups` and
177
+ `--data-migrations` too
178
+ - `init`, `operations approve`, `operator run` and `dashboard serve` are
179
+ never MCP tools (`mcp=False`), on this server or `treaty-mcp`
180
+ - A fleet that cannot be read stops the server before a client connects,
181
+ with the envelope on stderr: 79 `PROJECT_INVALID`, 80 `CONFIG_INVALID`,
182
+ 85 `INVENTORY_UNREADABLE`. `--list-tools` prints the tool list
183
+ - `dashboard serve` reports a port it cannot bind as 4 `PRECONDITION`
184
+ with `dashboard_listen_failed`; an unreadable evidence file is no
185
+ longer reported as a listen failure
186
+ - **Breaking:** `cloudfall-engine` runs on [treaty](https://github.com/romamo/treaty).
187
+ Every command answers one JSON envelope on stdout (`ok`, `data`, `error`,
188
+ `warnings`, `meta`), errors included, which used to go to stderr in their
189
+ own shape; `inventory render` puts the inventory under `data.inventory`
190
+ (the `--output` file is unchanged). Failures exit with their own codes:
191
+ 79 `PROJECT_INVALID`, 80 `CONFIG_INVALID`, 81 `ARTIFACT_BUILD_FAILED`,
192
+ 82 `PLAYBOOK_FAILED`; the old snake_case code is in `error.context.code`.
193
+ A missing playbook, inventory, or role directory exits 2 before Ansible
194
+ starts. `playbook run --check` is now `--dry-run`; Ansible's play log
195
+ streams to stderr. `cloudfall-engine manifest` describes every command
196
+ - **Breaking:** the last eight commands that change state move to treaty:
197
+ `observe`, `secrets render`, `import render`, `import render-api`,
198
+ `deploy`, `rollback`, `restart` and `data migrate` (treaty #183 lets
199
+ their camelCase keys through). Each keeps its `data` keys, `status`
200
+ included; the ones that write add `effect`. `deploy`, `rollback`,
201
+ `restart` and `data migrate` still change nothing without `--yes`: that
202
+ run is treaty's dry run (`effect: would_update`, `meta.dry_run: true`,
203
+ treaty #197). The plan writes the result keys as `null`, and the result
204
+ writes the plan keys (`wouldRun`, `instruction`) as `null`, since treaty
205
+ answers one object per command. `observe` is read-only (it declares the
206
+ snapshots it writes under the project as output, kept by `cleanup`) and exits 91 `INCOMPLETE` (was 1)
207
+ with the run in `data` when a server gave no snapshot. Engine steps,
208
+ sops and the Render API go through treaty: `doctor` checks sops, and
209
+ `import render-api` honours `--proxy` and marks its answer as content
210
+ from outside. Failures that were exit 2 are 4 `PRECONDITION` with the
211
+ old code in `error.context.code`. A key a command writes only sometimes
212
+ is `null` when absent, so `inventory show` writes `data.ansible` as
213
+ `null` for a project
214
+ - **Breaking:** `why` runs on treaty, `--format html` included (treaty
215
+ #179): the page is drawn from the answer's JSON document, as before. Its
216
+ verdict moves to `data.status`; a bad `--since` or `--until` is 2
217
+ `ARG_ERROR` with `why_instant_invalid` in `error.context.code`, and a
218
+ record that cannot be read 86 `RECORD_INVALID`. `--format html` is a
219
+ `why` format only; other commands refuse it
220
+ - **Breaking:** seven commands that run Ansible move to treaty: `health`,
221
+ `backup run`, `backup verify`, `operations propose`, `operations approve`,
222
+ `operator approve` and `migrate`. Ansible runs through treaty in the
223
+ project directory, so `--timeout` and Ctrl-C stop it, and `doctor` checks
224
+ ansible-playbook. `operations approve` and `migrate` still change nothing
225
+ without `--yes`; that preview is treaty's dry run, `effect: would_update`
226
+ and `meta.dry_run: true`, with `data.status` `pending` or `plan`. New exit
227
+ codes, each with the result
228
+ in `data`: 87 `ENGINE_STEP_FAILED` (an inventory render or playbook
229
+ failed, was 1), 88 `UNHEALTHY` (was 1), 89 `CHECK_FAILED` when check mode
230
+ fails in `propose` (was 1), 90 `NOT_VERIFIED` when an approved run fails
231
+ or does not verify (was 1), 3 `PARTIAL_FAILURE` for a paused `migrate`.
232
+ A failed `migrate` step's own code and message move from the top-level
233
+ `error` to `error.context`. `deploy`, `rollback`, `restart` and
234
+ `data migrate` stay on argparse until treaty #183, since their plan
235
+ answer has a `wouldRun` key
236
+ - **Breaking:** six more `cloudfall` commands run on treaty: `init`,
237
+ `add ssh-key`, `add server-type`, `add server`, `dashboard build` and
238
+ `services inspect`. Their answer is the treaty envelope described below,
239
+ with the same `data` keys, plus `effect: created` on `init` and `add`,
240
+ which treaty requires of a command that changes the project.
241
+ `dashboard build` and `services inspect` stay read-only and declare the
242
+ reports they write under `tmp/` as their output, which treaty's `cleanup`
243
+ never removes. Failures: 6 `CONFLICT` for a resource that already
244
+ exists (was 2), 7 `PERMISSION_DENIED` for a read-only project or path
245
+ (was 1 or 2), 4 `PRECONDITION` for a non-empty `init` directory (was 2),
246
+ 5 `NOT_FOUND` for a missing key file, 2 `ARG_ERROR` for a bad id, name or
247
+ address; the old code is in `error.context.code`. `init` runs git through
248
+ treaty, so `cloudfall doctor` checks for git 2.24. `observe`,
249
+ `secrets render` and the two `import` commands stay on argparse until
250
+ treaty can carry their camelCase keys on a command that writes (#183)
251
+ - **Breaking:** nine read-only `cloudfall` commands run on treaty, the
252
+ first step of moving the CLI off argparse: `config validate`,
253
+ `inventory show`, `operations list`, `operations show`,
254
+ `operations decisions`, `operator list`, `operator show`, `audit` and
255
+ `services status`. `cloudfall manifest` lists them; every other command,
256
+ root `--help` and `--version` stay on argparse for now. Their answer is a
257
+ treaty envelope (`ok`, `data`, `error`, `meta`, `warnings`): the verdict
258
+ moves from the top-level `status` to `data.status`, and the other `data`
259
+ keys are unchanged. `inventory show` writes `data.ansible` as `null` for a
260
+ project. Failures are an envelope on stdout with their own exit codes:
261
+ 79 `PROJECT_INVALID`, 80 `CONFIG_INVALID` (resources or evidence files),
262
+ 85 `INVENTORY_UNREADABLE`, 86 `RECORD_INVALID`, 5 `NOT_FOUND` for an
263
+ undeclared operation or a missing proposal, 2 `ARG_ERROR` for bad input;
264
+ the old snake_case code is in `error.context.code`. `audit` exits 83
265
+ `DRIFT` (was 1) or 84 `UNKNOWN` (was 3) with the report in `data`. Pick
266
+ the format with `--format json`: `--output` after one of these commands
267
+ is refused, since treaty's `--output` names a file. Relative paths still
268
+ resolve against the project, without changing the working directory
269
+ - `cloudfall-engine` runs Ansible and git through treaty's `ctx.run`
270
+ (treaty 1.0.0rc18), so `--timeout` and Ctrl-C stop the whole process
271
+ group, and secrets are redacted from the play log. The play log streams
272
+ to stderr as plain text, as before, terminal or not; `--quiet` silences
273
+ it. A failed playbook carries the last 4096 characters of the log in
274
+ `error.context.output`, marked as untrusted content from the hosts.
275
+ `playbook run` reports `meta.dry_run: true` under `--dry-run`. A git step
276
+ that runs past its 600-second limit now exits 10 `TIMEOUT`, not 81 with
277
+ `artifact_git_timeout`. `cloudfall-engine doctor` checks for
278
+ ansible-playbook 2.21 and git 2.24
279
+ - Every JSON document has the same six top-level keys: `ok` (true exactly
280
+ when the exit code is 0), `status` (the command's verdict, such as `ok`,
281
+ `plan`, `drift` or `unhealthy`), `data` (the command's payload, which
282
+ used to sit at the top level beside `status`), `error` (`null` unless
283
+ the command failed), and `meta` and `warnings`. A failure on stderr is
284
+ `{"ok": false, "status": "error", "data": null, "error": {"code",
285
+ "message"}}`. An agent reads `ok` and `data` without
286
+ knowing which command it ran. Anything that read a payload key from the
287
+ top level, such as `.inventory`, now reads it under `data`
288
+ - `--help` prints to stderr when stdout is not a terminal, so a caller
289
+ capturing stdout never finds usage text where it expects JSON. At a
290
+ terminal it stays on stdout
291
+ - `--output json` is accepted before the command and after every command.
292
+ JSON is the only format, so the flag changes nothing; a caller that asks
293
+ for JSON the usual way no longer gets a usage error. `--output` with any
294
+ other value is `invalid_argument`
295
+ - The six options that named an output path are renamed so `--output`
296
+ means one thing everywhere: `--output-dir` on `observe`,
297
+ `services inspect`, `dashboard build`, `import render` and
298
+ `import render-api`, and `--output-file` on `secrets render`. Scripts
299
+ passing `--output DIR` to these commands must switch
300
+ - `operator` errors use the same envelope as every other command,
301
+ `{"status": "error", "error": {"code", "message"}}`, instead of a bare
302
+ `{"code", "message"}`, so one parser reads every failure
303
+ - `operator show` and `operator approve` print the receipt under
304
+ `data.proposal` with a top-level `status`, instead of the bare receipt.
305
+ `approve` reports `status: failed` when the verify step did not confirm
306
+ the fix, as the `cloudfall-mcp` tool already did
307
+
308
+ ### Fixed
309
+
310
+ - `migrate --env-file NAME=PATH` and `--data NAME=PATH` resolve a relative
311
+ path against the project again, as every other path does; since the
312
+ move to treaty they resolved against the current directory
313
+ - `migrate --build` and the `cloudfall-mcp` `build_artifact` tool read the
314
+ release from the engine's treaty envelope, under `data`. Since the engine
315
+ moved to treaty every build step failed with "artifact builder returned no
316
+ release id". `build_artifact` now returns the engine's `data` keys
317
+ (`git_ref`, `archive_sha256`, ...) beside `status`
318
+ - The time baseline accepts a host whose clock another daemon keeps
319
+ (ntp, ntpsec, chrony). `timedatectl set-ntp` drives systemd-timesyncd
320
+ only, so on such hosts check mode reported "Would enable network time
321
+ synchronization" and a deploy failed with "NTP not supported". The role
322
+ now switches NTP on only where systemd-timesyncd is installed, and
323
+ otherwise requires the clock to be synchronized; check mode warns when
324
+ nothing keeps time, and a deploy fails with a message naming the daemon
325
+ to check when the clock does not synchronize within 60 seconds
326
+ - `cloudfall add` into a read-only or full project failed with a Python
327
+ traceback and could leave a new server type behind when only the
328
+ server file failed. It now removes what it wrote and fails with
329
+ `project_write_failed` (exit 2), naming the file and the reason. Any
330
+ other command that cannot write a path because it is read-only or not
331
+ the caller's reports `path_not_writable` as JSON (exit 1, since part of
332
+ the work may be done) instead of a traceback
333
+ - `cloudfall observe` no longer prints Ansible's play log on stdout ahead
334
+ of its JSON document, which broke any caller parsing stdout as JSON.
335
+ The playbook's output is captured, and when the run fails its last 2000
336
+ characters, ending with the play recap, are kept in `data.detail`. The
337
+ same capture keeps the `observe` tool of `cloudfall-mcp` from writing
338
+ Ansible text into the protocol stream
339
+ - `cloudfall-mcp` `operator_watch` and `operator_approve` wrapped engine
340
+ failures in the error envelope twice (`error.error.code`); they now
341
+ return one envelope like every other tool
342
+ - A failed `operator approve` is recorded even when the engine's log is
343
+ long. The receipt keeps the error code and the end of the log, where the
344
+ failing task and the play recap are, within the 1000-character cap.
345
+ Before, a run against unreachable hosts crashed while writing the
346
+ `failed` receipt, left the proposal `proposed` with no trace that it ran,
347
+ and showed the caller a traceback
348
+
349
+ ## [0.5.1] - 2026-09-22
350
+
351
+ ### Changed
352
+
353
+ - Decision records default to `decisions/` beside the operations rather
354
+ than to `tmp/decisions`: the record is what a team keeps, and `tmp/` is
355
+ what they throw away. The diffs and logs beside each record are raw
356
+ Ansible output, so a repository that commits them wants `no_log` on the
357
+ tasks that handle secrets
358
+
359
+ ### Fixed
360
+
361
+ - A decision is `verified` only when its verify run changed nothing. A
362
+ verify step that exits zero while changing a host found the fleet not as
363
+ the run left it and converged it further, which verifies nothing, so it
364
+ is recorded `failed`. Every approved decision now carries a `verdict`
365
+ saying why it ended where it did
366
+ - A record cites its artifacts and its observation basis as the repository
367
+ sees them rather than by absolute path, because a record is committed
368
+ and one machine's home directory means nothing in anyone else's checkout
369
+
370
+ ## [0.5.0] - 2026-09-21
371
+
372
+ ## [0.4.0] - 2026-09-21
373
+
374
+ ### Added
375
+
376
+ - Brownfield fleet reader: `cloudfall audit` and `cloudfall inventory show`
377
+ read the fleet from the team's own Ansible inventory, with `--inventory`
378
+ or from the inventory an `ansible.cfg` in the current directory names.
379
+ Hosts, groups and connection settings come from Ansible; a `cloudfall`
380
+ block per host, `cloudfall_defaults` per group and the
381
+ `cloudfall_server_types` catalog carry what Ansible does not model. The
382
+ documents are validated against the same schemas a project directory
383
+ gets, so `PlatformInventory` and everything built on it is unchanged
384
+ - `cloudfall.ansible_api` is the only module that imports ansible-core. It
385
+ reads in process and falls back to the `ansible-inventory` command,
386
+ reporting which served the read
387
+ - `cloudfall observe` collects one read-only snapshot per server through
388
+ the team's inventory plus an ephemeral overlay holding only the
389
+ `cloudfall_servers` group and the two variables the inspect role cannot
390
+ derive, so the audit loop closes without a Cloudfall project. Their own
391
+ `ansible.cfg` keeps deciding how Ansible connects; only the roles path
392
+ is forced. `--limit` takes an Ansible host pattern, which Ansible
393
+ resolves, and the run is judged against the hosts it asked for
394
+ - `StateValidator.validate_documents` validates resource documents
395
+ assembled in memory, with the schemas, reference checks and error codes
396
+ documents on disk get
397
+ - The operations catalog: every playbook the team runs is declared in
398
+ `operations/` with a risk level, a target scope, typed inputs,
399
+ preconditions and a verify step, and `cloudfall operations list|show`
400
+ reads it. A playbook nobody declared is not an operation
401
+ - The gate and the record: `cloudfall operations propose` runs an
402
+ operation in check mode and records the operation, targets, inputs, the
403
+ evidence it was based on and the diff by sha256;
404
+ `cloudfall operations approve --yes` records the approver, runs it, runs
405
+ the verify playbook and closes the record as executed, verified or
406
+ failed; `cloudfall operations decisions` reads the trail without the
407
+ catalog
408
+ - The agent surface: `cloudfall-mcp --repository` serves a brownfield
409
+ repository, where the tool list is the catalog. Each declared operation
410
+ is one tool carrying its risk as MCP annotations, calling it runs check
411
+ mode and records a proposal, and no tool approves anything: that stays a
412
+ command a person runs
413
+
414
+ ## [0.3.0] - 2026-09-20
415
+
416
+ ### Removed
417
+
418
+ - `cloudfall init --rev` and `--source`, and the machinery behind them:
419
+ `GitRevision`, `GitSourceUrl`, `GitPin`, `IndexPin`, `CheckoutState`, the
420
+ checkout inspection, and the `project_revision_unresolved`,
421
+ `project_revision_uncommitted` and `project_revision_unpublished` errors.
422
+ Cloudfall is on PyPI, so a project pins a release and resolves it like any
423
+ other dependency; `resolve_installed_version` replaces
424
+ `resolve_installed_pin`, and the `init` envelope reports `version` rather
425
+ than a `pin` object. To run an unreleased Cloudfall in a project, point the
426
+ dependency at a checkout with uv's own `[tool.uv.sources]`
427
+
428
+ ### Changed
429
+
430
+ - The quickstart and the Render migration guide install Cloudfall from the
431
+ index rather than from git
432
+
433
+ ## [0.2.1] - 2026-09-20
434
+
435
+ ### Fixed
436
+
437
+ - `cloudfall init` works from a PyPI install. An install from the package
438
+ index records no origin to read a commit from, so `init` failed with
439
+ `project_revision_unresolved` on the first command a new user runs, and
440
+ `--rev` only worked around it by writing a project that installed
441
+ Cloudfall from git instead of from the index. A project now pins whatever
442
+ ran `init`: `cloudfall==<version>` with no `[tool.uv.sources]` table for a
443
+ released install, the commit and repository for a git or source install
444
+ - The project README links the secrets guide and the reference examples at
445
+ the release tag when the pin is a released version, rather than at a
446
+ commit the install does not know
447
+
448
+ ### Changed
449
+
450
+ - `InitOptions` and the `cloudfall init` envelope carry one `pin` instead of
451
+ a `revision` and a `source`: `{"kind": "index", "version": ...}` or
452
+ `{"kind": "git", "revision": ..., "source": ...}`. `resolve_installed_pin`
453
+ replaces `resolve_installed_revision`
454
+ - `--rev` documents that it pins a commit from `--source`, which is now the
455
+ explicit alternative to the default rather than the only mechanism
456
+
457
+ ## [0.2.0] - 2026-09-20
458
+
459
+ ### Added
460
+
461
+ - `cloudfall-mcp` exposes `add_ssh_key`, `add_server_type`, and `add_server`
462
+ so an agent can declare the fleet without writing YAML by hand; each
463
+ returns the `cloudfall add` envelope, writes into the project only, and
464
+ re-validates it. `cloudfall init` stays CLI-only because the server runs
465
+ inside an existing project (#3)
466
+ - `cloudfall init` lays out the secrets setup (`secrets/` and a `.sops.yaml`
467
+ template), runs `git init` unless the directory already lies inside a
468
+ repository, links the secrets guide and the reference examples at the
469
+ pinned commit, and takes `--description` for an About section in the
470
+ README and the `description` in `pyproject.toml`
471
+ - `cloudfall init` writes `AGENTS.md`, the operating contract for AI agents
472
+ in the project: canonical invocation, every command sorted by effect with
473
+ the gate each server-changing command demands, the output contract and
474
+ exit codes, what may be read under `tmp/`, the secrets rule, and git
475
+ discipline; plus a one-line `CLAUDE.md` pointing at it. The
476
+ classification comes from the new `cloudfall.commands` catalog, which
477
+ the tests check against both argparse trees (#2)
478
+ - `cloudfall init` from a source checkout refuses to pin `HEAD` when tracked
479
+ files are modified (`project_revision_uncommitted`) or when `HEAD` is on
480
+ no remote branch (`project_revision_unpublished`), so a project never pins
481
+ a commit that is not the running code or that `uv sync` cannot fetch
482
+ - Fleet repositories consume Cloudfall as a package: the wheel bundles the
483
+ v1 schema catalog and the Ansible engine, and every `--schemas` and
484
+ `--engine` default resolves to the bundled copies (or to the source tree
485
+ when run from a checkout), so no submodule or sibling checkout is needed
486
+ - `cloudfall-engine playbook run` executes a bundled playbook by name or a
487
+ fleet playbook by path under the engine's Ansible configuration, with
488
+ `--roles` directories searched before the bundled roles and `--check`,
489
+ `--diff`, `--syntax-check`, `--limit`, `--tags`, and `--extra-vars`
490
+ passed through; `cloudfall-engine playbook list` names the bundled ones
491
+ - Timer-driven restore drill: an optional `backup.restoreCheckOnCalendar`
492
+ schedule installs an audited restore-check service and timer for
493
+ PostgreSQL and Redis, so backup restorability is verified continuously
494
+ (M10; proven live on 2026-09-11 together with alert delivery to an
495
+ external destination)
496
+
497
+ ### Changed
498
+
499
+ - `ansible-core` is a runtime dependency rather than a development one, so
500
+ an installed `cloudfall` package can run its engine
501
+ - **Breaking: common-vocabulary rename across schemas, CLI, and layout.**
502
+ Resource kinds `Project` → `Application` and `HostProfile` → `ServerType`
503
+ (schema files renamed to match); the `Server` spec field `profile` →
504
+ `serverType`; the `Component` spec field `project` → `application`; the
505
+ top-level `state/` directory → `config/` with resource directories
506
+ `projects/` → `applications/` and `host-profiles/` → `server-types/`;
507
+ `cloudfall state validate` → `cloudfall config validate`; importer flag
508
+ `--project` → `--application`; Python API `validate_state` →
509
+ `validate_config`, `ValidatedState` → `ValidatedConfig`,
510
+ `StateValidationError` → `ConfigValidationError`; Taskfile variable
511
+ `STATE_DIR` → `CONFIG_DIR`; inventory payload key `hostProfiles` →
512
+ `serverTypes` and observed-server key `profile` → `serverType`. Existing
513
+ config directories must be migrated by renaming the directories and the
514
+ `kind`/`profile`/`project` fields; no compatibility aliases are provided
515
+ - `cloudfall-mcp --help` now documents every option and the confirmation
516
+ handshake
517
+ - README quickstart covers cloning, `uv sync`, and automatic Python 3.14
518
+ provisioning; the Task prerequisite is documented with a direct `uv run`
519
+ equivalent
520
+ - Roadmap gained a forward-looking M6 section (catalog breadth, Render API
521
+ import, cutover generator, secrets v2, bare-metal provisioning, backup and
522
+ restore operations, fleet observability)
523
+ - `cloudfall`, `cloudfall-engine`, and `cloudfall-mcp` match long options
524
+ exactly and report usage errors as the JSON error envelope
525
+ (`invalid_argument`, exit 2) instead of argparse prose; unrecognized
526
+ options are named without their values
527
+ - **Breaking: `cloudfall deploy`, `rollback`, `restart`, and `data migrate`
528
+ change servers only with `--yes`.** Without it they run every
529
+ controller-side check (declared component or service, verified artifact,
530
+ declared database, source URL file) and print a `status: plan` preview
531
+ naming the target servers, exit `0`, matching `cloudfall migrate` and the
532
+ MCP confirmation handshake; scripts and Taskfile targets must add `--yes`
533
+ - Runtime path options (`--output`, `--receipts`, `--inventory-file`,
534
+ `--observed`, `--plan-file`, and the other evidence and receipt
535
+ directories) on all three entry points, and the MCP tools' output and plan
536
+ paths, refuse a relative path that climbs out of the project
537
+ (`tmp/../../x`); a location outside the project must be given as an
538
+ absolute path
539
+
540
+ ### Fixed
541
+
542
+ - A guessed `--api-key` or `--source-url` no longer binds to
543
+ `--api-key-file`/`--source-url-file` and echoes the secret in the
544
+ resulting file-not-found error
545
+ - Malformed component, service, proposal, application, server, and release
546
+ ids fail as `invalid_argument` before any work starts instead of crashing
547
+ with a traceback on exit 1
548
+ - JSON documents are flushed as they are written, so `operator run
549
+ --interval` under a pipe or journald delivers each pass instead of
550
+ holding it in the buffer and losing it on termination
551
+ - A missing or unreadable gateway CA, certificate, or key reports
552
+ `operator_gateway_material_invalid` instead of an uncaught traceback
553
+ - `cloudfall init` suggests `uv run cloudfall config validate` as the next
554
+ step; the previous `config validate .` hint no longer parsed
555
+
556
+ ## [0.1.0] - 2026-09-08
557
+
558
+ First public milestone: the Render-to-Hetzner wedge proven live end to end
559
+ on disposable Hetzner Cloud Debian 13 servers (see
560
+ [`docs/proving-runs/`](docs/proving-runs/)).
561
+
562
+ ### Added
563
+
564
+ - Declarative state module: versioned v1 JSON Schemas for `Server`,
565
+ `HostProfile`, `Project`, `Component`, `Service`, `Domain`, `SshPublicKey`,
566
+ and `LoggingStack`, plus observation, receipt, and artifact schemas
567
+ - `cloudfall` CLI and Python API: state validation, typed inventory,
568
+ config-versus-observed drift audit with distinct exit codes, service
569
+ lifecycle status, and the evidence-derived operations dashboard
570
+ (static build and live `dashboard serve`)
571
+ - `cloudfall-engine`: deterministic Ansible inventory rendering and the
572
+ artifact builder (git ref to hashed tarball with release metadata)
573
+ - Engine roles: Debian bootstrap, UTC time baseline, SSH access hardening,
574
+ nftables default-deny firewall, unattended security upgrades, read-only
575
+ inspection, PostgreSQL with peer-auth databases and backup timers,
576
+ Nginx/TLS domain routes with Let's Encrypt issuance, and the guarded
577
+ Loki/Grafana/Alloy logging stack over an mTLS gateway
578
+ - Health-gated deploy slice: digest-verified artifact transfer, symlink
579
+ releases, automatic rollback on failed health checks, and release receipts
580
+ - `cloudfall import render`: `render.yaml` blueprint importer with a
581
+ structured gap report (`IMPORT-REPORT.md`)
582
+ - `cloudfall data migrate`: guided managed-Postgres dump and restore with
583
+ row-count verification and a `DataMigrationReceipt`
584
+ - `cloudfall migrate`: resumable end-to-end orchestrator with persisted step
585
+ progress and a DNS-verification pause at the cutover moment
586
+ - `cloudfall-mcp`: seventeen annotated MCP tools; read-only evidence tools
587
+ exposed freely, every server-changing tool gated behind a two-step
588
+ confirmation handshake