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.
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/ARCHITECTURE.md +2 -2
- cloudfall-0.6.0rc1/CHANGELOG.md +588 -0
- cloudfall-0.6.0rc1/CODE_OF_CONDUCT.md +83 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/CONTRIBUTING.md +13 -0
- cloudfall-0.6.0rc1/PKG-INFO +232 -0
- cloudfall-0.6.0rc1/README.md +204 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/ROADMAP.md +2 -2
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/Taskfile.yml +5 -5
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/brownfield-design.md +1 -1
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/fleet-goals.md +2 -2
- cloudfall-0.6.0rc1/docs/getting-started.md +149 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/landscape.md +4 -4
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/logging-service-guide.md +10 -10
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/neon-migration-guide.md +10 -10
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/operator-guide.md +25 -14
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/render-migration-guide.md +9 -8
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/secrets-guide.md +6 -6
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/README.md +26 -2
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/tasks/main.yml +33 -1
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/artifact.py +38 -29
- cloudfall-0.6.0rc1/engine/src/cloudfall_engine/cli.py +473 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/playbook.py +2 -12
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_ansible_inventory.py +47 -11
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_artifact_builder.py +62 -23
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_playbook.py +126 -8
- cloudfall-0.6.0rc1/engine/tests/test_time_role.py +119 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/render.yaml +1 -1
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/pyproject.toml +25 -6
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/README.md +28 -23
- cloudfall-0.6.0rc1/sdk/src/cloudfall/agent_server.py +449 -0
- cloudfall-0.6.0rc1/sdk/src/cloudfall/agent_tools.py +52 -0
- cloudfall-0.6.0rc1/sdk/src/cloudfall/app.py +3626 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/audit.py +4 -1
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/authoring.py +15 -7
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/catalog.py +3 -1
- cloudfall-0.6.0rc1/sdk/src/cloudfall/cli.py +21 -0
- cloudfall-0.6.0rc1/sdk/src/cloudfall/commands.py +587 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/dashboard_server.py +28 -2
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/decision.py +5 -5
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/importer.py +1 -1
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/lifecycle.py +73 -40
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/observe.py +33 -6
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/operator.py +102 -17
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/project.py +62 -23
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/render_api.py +7 -3
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/secrets.py +4 -4
- cloudfall-0.6.0rc1/sdk/src/cloudfall/why.py +436 -0
- cloudfall-0.6.0rc1/sdk/tests/test_agent_server.py +400 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_ansible_reader.py +12 -10
- cloudfall-0.6.0rc1/sdk/tests/test_app.py +180 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_arguments.py +35 -74
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_audit.py +59 -5
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_authoring.py +79 -5
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_catalog.py +9 -7
- cloudfall-0.6.0rc1/sdk/tests/test_commands.py +112 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_dashboard_server.py +20 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_decision.py +16 -13
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_importer.py +31 -4
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_inventory.py +3 -2
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_lifecycle.py +109 -25
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_migrate.py +9 -5
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_observe.py +56 -8
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_operator.py +192 -1
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_project.py +28 -25
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_render_api.py +5 -4
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_validation.py +14 -9
- cloudfall-0.6.0rc1/sdk/tests/test_why.py +227 -0
- cloudfall-0.5.1/.github/workflows/ci.yml +0 -53
- cloudfall-0.5.1/.github/workflows/release.yml +0 -78
- cloudfall-0.5.1/CHANGELOG.md +0 -248
- cloudfall-0.5.1/PKG-INFO +0 -418
- cloudfall-0.5.1/README.md +0 -392
- cloudfall-0.5.1/engine/src/cloudfall_engine/cli.py +0 -273
- cloudfall-0.5.1/evaluations/cloudfall/README.md +0 -43
- cloudfall-0.5.1/evaluations/cloudfall/environment.md +0 -61
- cloudfall-0.5.1/evaluations/cloudfall/findings.md +0 -26
- cloudfall-0.5.1/evaluations/cloudfall/issues.md +0 -78
- cloudfall-0.5.1/evaluations/cloudfall/linkedin.md +0 -33
- cloudfall-0.5.1/evaluations/cloudfall/readiness.md +0 -84
- cloudfall-0.5.1/evaluations/cloudfall/report-agent-dev.md +0 -1003
- cloudfall-0.5.1/evaluations/cloudfall/report-dev.md +0 -1048
- cloudfall-0.5.1/evaluations/cloudfall/report-index.md +0 -49
- cloudfall-0.5.1/evaluations/cloudfall/report-issues.md +0 -410
- cloudfall-0.5.1/evaluations/cloudfall/report-runtime.md +0 -74
- cloudfall-0.5.1/evaluations/cloudfall/trace.md +0 -1276
- cloudfall-0.5.1/evaluations/cloudfall/x.md +0 -45
- cloudfall-0.5.1/sdk/src/cloudfall/agent_tools.py +0 -868
- cloudfall-0.5.1/sdk/src/cloudfall/arguments.py +0 -92
- cloudfall-0.5.1/sdk/src/cloudfall/cli.py +0 -2138
- cloudfall-0.5.1/sdk/src/cloudfall/commands.py +0 -294
- cloudfall-0.5.1/sdk/src/cloudfall/fleet_tools.py +0 -249
- cloudfall-0.5.1/sdk/src/cloudfall/mcp_server.py +0 -944
- cloudfall-0.5.1/sdk/tests/test_agent_tools.py +0 -362
- cloudfall-0.5.1/sdk/tests/test_commands.py +0 -91
- cloudfall-0.5.1/sdk/tests/test_fleet_tools.py +0 -301
- cloudfall-0.5.1/site/public/apple-touch-icon.png +0 -0
- cloudfall-0.5.1/site/public/favicon-32.png +0 -0
- cloudfall-0.5.1/site/public/icon-192.png +0 -0
- cloudfall-0.5.1/site/public/icon-512.png +0 -0
- cloudfall-0.5.1/site/public/index.html +0 -491
- cloudfall-0.5.1/site/public/manifesto.html +0 -367
- cloudfall-0.5.1/site/public/og.png +0 -0
- cloudfall-0.5.1/site/public/site.webmanifest +0 -23
- cloudfall-0.5.1/site/src/worker.js +0 -10
- cloudfall-0.5.1/site/wrangler.jsonc +0 -18
- cloudfall-0.5.1/uv.lock +0 -1198
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/.ansible-lint +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/.gitignore +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/LICENSE +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/MANIFESTO.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/SECURITY.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/README.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/alert-rules/postgresql-down.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/applications/crm.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/components/crm-backend.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/domains/crm-site.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/logging-stacks/operations.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/operator-policies/production.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/server-types/debian-application.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/servers/h1.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/servers/h2.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/services/postgresql-main.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/services/redis-cache.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/examples/ssh-public-keys/example-admin.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/alert-rule.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/application.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/artifact.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/backup-receipt.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/common.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/component.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/deployment-receipt.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/domain.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/environment-receipt.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/logging-stack.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/observed-domain.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/observed-server.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operation-decision.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operation.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operator-policy.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/operator-proposal.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/release-receipt.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/server-type.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/server.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/service.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/schemas/v1/ssh-public-key.schema.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/applications/crm.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/components/crm-backend.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/server-types/debian-application.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/missing-server/servers/h1.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/invalid/schema/components/component-unknown-field.yaml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/observed/compliant/h1.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/config/tests/observed/compliant/h2.json +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/ansible-context.md +0 -0
- /cloudfall-0.5.1/site/public/favicon.svg → /cloudfall-0.6.0rc1/docs/assets/logo.svg +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/availability-design.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/decisions/2026-09-14-no-ansible-replacement.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/decisions/2026-09-14-no-nixos-base.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/decisions/2026-09-14-no-rust-rewrite.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/hybrid-storage-design.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/new-server-storage-guide.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-debian-container-m1.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-dns-cutover.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-guided-data-migration.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-hetzner-m1.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-hetzner-m2-m3.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-hetzner-m4-m5.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-08-render-cutover.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-10-hetzner-m7.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-10-hetzner-m8.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-10-hetzner-m9.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/docs/proving-runs/2026-09-11-hetzner-m10.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/ansible.cfg +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/backup.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/baseline.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/bootstrap.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/data.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/deploy.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/domains.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/health.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/inspect.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/logging.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/restart.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/rollback.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/services.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/playbooks/time.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_access/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_bootstrap/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_data_migration/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/health.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/restart.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/tasks/rollback.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_deploy/templates/component.service.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_inspect/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/handlers/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/backup.service.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_time/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/__init__.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/__main__.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/ansible_inventory.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/src/cloudfall_engine/py.typed +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_deploy_templates.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_firewall_templates.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_logging_templates.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_nginx_site_templates.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/engine/tests/test_postgresql_templates.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/README.md +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/pyproject.toml +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/src/demo/__init__.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/src/demo/asgi.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/examples/render-demo/uv.lock +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/__init__.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/__main__.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/ansible_api.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/ansible_reader.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/cutover.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/dashboard.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/domain.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/inventory.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/migrate.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/observation.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/operations.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/py.typed +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/resources.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/service_evidence.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/src/cloudfall/validation.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_cutover.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_data_migration.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_operations_dashboard.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_resources.py +0 -0
- {cloudfall-0.5.1 → cloudfall-0.6.0rc1}/sdk/tests/test_secrets.py +0 -0
- {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
|
|
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
|
|
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
|