cloudfall 0.2.0__py3-none-any.whl

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 (165) hide show
  1. cloudfall/__init__.py +19 -0
  2. cloudfall/__main__.py +5 -0
  3. cloudfall/_bundled/schemas/v1/alert-rule.schema.json +63 -0
  4. cloudfall/_bundled/schemas/v1/application.schema.json +60 -0
  5. cloudfall/_bundled/schemas/v1/artifact.schema.json +71 -0
  6. cloudfall/_bundled/schemas/v1/backup-receipt.schema.json +58 -0
  7. cloudfall/_bundled/schemas/v1/common.schema.json +115 -0
  8. cloudfall/_bundled/schemas/v1/component.schema.json +234 -0
  9. cloudfall/_bundled/schemas/v1/deployment-receipt.schema.json +53 -0
  10. cloudfall/_bundled/schemas/v1/domain.schema.json +208 -0
  11. cloudfall/_bundled/schemas/v1/environment-receipt.schema.json +55 -0
  12. cloudfall/_bundled/schemas/v1/logging-stack.schema.json +473 -0
  13. cloudfall/_bundled/schemas/v1/observed-domain.schema.json +158 -0
  14. cloudfall/_bundled/schemas/v1/observed-server.schema.json +627 -0
  15. cloudfall/_bundled/schemas/v1/operator-policy.schema.json +97 -0
  16. cloudfall/_bundled/schemas/v1/operator-proposal.schema.json +253 -0
  17. cloudfall/_bundled/schemas/v1/release-receipt.schema.json +66 -0
  18. cloudfall/_bundled/schemas/v1/server-type.schema.json +289 -0
  19. cloudfall/_bundled/schemas/v1/server.schema.json +146 -0
  20. cloudfall/_bundled/schemas/v1/service.schema.json +222 -0
  21. cloudfall/_bundled/schemas/v1/ssh-public-key.schema.json +55 -0
  22. cloudfall/agent_tools.py +868 -0
  23. cloudfall/arguments.py +92 -0
  24. cloudfall/audit.py +905 -0
  25. cloudfall/authoring.py +341 -0
  26. cloudfall/cli.py +1748 -0
  27. cloudfall/commands.py +257 -0
  28. cloudfall/cutover.py +370 -0
  29. cloudfall/dashboard.py +407 -0
  30. cloudfall/dashboard_server.py +201 -0
  31. cloudfall/domain.py +1157 -0
  32. cloudfall/importer.py +906 -0
  33. cloudfall/inventory.py +2116 -0
  34. cloudfall/lifecycle.py +874 -0
  35. cloudfall/mcp_server.py +704 -0
  36. cloudfall/migrate.py +730 -0
  37. cloudfall/observation.py +143 -0
  38. cloudfall/operations.py +1228 -0
  39. cloudfall/operator.py +1099 -0
  40. cloudfall/project.py +841 -0
  41. cloudfall/py.typed +0 -0
  42. cloudfall/render_api.py +338 -0
  43. cloudfall/resources.py +67 -0
  44. cloudfall/secrets.py +262 -0
  45. cloudfall/service_evidence.py +659 -0
  46. cloudfall/validation.py +1340 -0
  47. cloudfall-0.2.0.dist-info/METADATA +408 -0
  48. cloudfall-0.2.0.dist-info/RECORD +165 -0
  49. cloudfall-0.2.0.dist-info/WHEEL +4 -0
  50. cloudfall-0.2.0.dist-info/entry_points.txt +4 -0
  51. cloudfall-0.2.0.dist-info/licenses/LICENSE +661 -0
  52. cloudfall_engine/__init__.py +5 -0
  53. cloudfall_engine/__main__.py +5 -0
  54. cloudfall_engine/_bundled/ansible/ansible.cfg +7 -0
  55. cloudfall_engine/_bundled/ansible/playbooks/backup.yml +86 -0
  56. cloudfall_engine/_bundled/ansible/playbooks/baseline.yml +35 -0
  57. cloudfall_engine/_bundled/ansible/playbooks/bootstrap.yml +24 -0
  58. cloudfall_engine/_bundled/ansible/playbooks/data.yml +28 -0
  59. cloudfall_engine/_bundled/ansible/playbooks/deploy.yml +35 -0
  60. cloudfall_engine/_bundled/ansible/playbooks/domains.yml +21 -0
  61. cloudfall_engine/_bundled/ansible/playbooks/health.yml +31 -0
  62. cloudfall_engine/_bundled/ansible/playbooks/inspect.yml +15 -0
  63. cloudfall_engine/_bundled/ansible/playbooks/logging.yml +35 -0
  64. cloudfall_engine/_bundled/ansible/playbooks/restart.yml +31 -0
  65. cloudfall_engine/_bundled/ansible/playbooks/rollback.yml +33 -0
  66. cloudfall_engine/_bundled/ansible/playbooks/services.yml +37 -0
  67. cloudfall_engine/_bundled/ansible/playbooks/time.yml +9 -0
  68. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/defaults/main.yml +2 -0
  69. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/handlers/main.yml +5 -0
  70. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/argument_specs.yml +34 -0
  71. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/main.yml +13 -0
  72. cloudfall_engine/_bundled/ansible/roles/cloudfall_access/tasks/main.yml +59 -0
  73. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/defaults/main.yml +11 -0
  74. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +52 -0
  75. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/main.yml +13 -0
  76. cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/tasks/main.yml +99 -0
  77. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/defaults/main.yml +15 -0
  78. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +36 -0
  79. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/main.yml +13 -0
  80. cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/tasks/main.yml +201 -0
  81. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/defaults/main.yml +10 -0
  82. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +125 -0
  83. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/main.yml +13 -0
  84. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health.yml +3 -0
  85. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +37 -0
  86. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/main.yml +285 -0
  87. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/restart.yml +17 -0
  88. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/rollback.yml +52 -0
  89. cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/templates/component.service.j2 +24 -0
  90. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/defaults/main.yml +2 -0
  91. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/handlers/main.yml +5 -0
  92. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +33 -0
  93. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/main.yml +13 -0
  94. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/tasks/main.yml +51 -0
  95. cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +31 -0
  96. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/defaults/main.yml +6 -0
  97. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +24 -0
  98. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/main.yml +13 -0
  99. cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/tasks/main.yml +643 -0
  100. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/defaults/main.yml +6 -0
  101. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/handlers/main.yml +29 -0
  102. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +227 -0
  103. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/main.yml +11 -0
  104. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/tasks/main.yml +487 -0
  105. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +2 -0
  106. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +33 -0
  107. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +20 -0
  108. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +11 -0
  109. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +3 -0
  110. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +49 -0
  111. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +45 -0
  112. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +11 -0
  113. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +2 -0
  114. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +20 -0
  115. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +24 -0
  116. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/defaults/main.yml +7 -0
  117. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/handlers/main.yml +6 -0
  118. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +122 -0
  119. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/main.yml +11 -0
  120. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/tasks/main.yml +224 -0
  121. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +3 -0
  122. cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +127 -0
  123. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/defaults/main.yml +4 -0
  124. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/handlers/main.yml +5 -0
  125. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +69 -0
  126. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/main.yml +13 -0
  127. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/tasks/main.yml +212 -0
  128. cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +54 -0
  129. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/defaults/main.yml +6 -0
  130. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/handlers/main.yml +9 -0
  131. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +91 -0
  132. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/main.yml +13 -0
  133. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/tasks/main.yml +295 -0
  134. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +11 -0
  135. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +17 -0
  136. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +10 -0
  137. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +3 -0
  138. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +11 -0
  139. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +27 -0
  140. cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +10 -0
  141. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/defaults/main.yml +2 -0
  142. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/handlers/main.yml +6 -0
  143. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/argument_specs.yml +68 -0
  144. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/main.yml +13 -0
  145. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/tasks/main.yml +148 -0
  146. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.service.j2 +11 -0
  147. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +14 -0
  148. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +10 -0
  149. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +6 -0
  150. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +11 -0
  151. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +15 -0
  152. cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +10 -0
  153. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/defaults/main.yml +2 -0
  154. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/argument_specs.yml +10 -0
  155. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/main.yml +13 -0
  156. cloudfall_engine/_bundled/ansible/roles/cloudfall_time/tasks/main.yml +119 -0
  157. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +3 -0
  158. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +8 -0
  159. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +13 -0
  160. cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +37 -0
  161. cloudfall_engine/ansible_inventory.py +253 -0
  162. cloudfall_engine/artifact.py +258 -0
  163. cloudfall_engine/cli.py +273 -0
  164. cloudfall_engine/playbook.py +175 -0
  165. cloudfall_engine/py.typed +0 -0
@@ -0,0 +1,408 @@
1
+ Metadata-Version: 2.5
2
+ Name: cloudfall
3
+ Version: 0.2.0
4
+ Summary: AI-native control plane for dedicated Debian application fleets
5
+ Project-URL: Homepage, https://cloudfall.dev
6
+ Project-URL: Repository, https://github.com/romamo/cloudfall
7
+ Project-URL: Changelog, https://github.com/romamo/cloudfall/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/romamo/cloudfall/issues
9
+ License-Expression: AGPL-3.0-or-later
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: System :: Installation/Setup
17
+ Classifier: Topic :: System :: Systems Administration
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.14
20
+ Requires-Dist: ansible-core<2.22,>=2.21
21
+ Requires-Dist: jsonschema[format]<5,>=4.23
22
+ Requires-Dist: pyyaml<7,>=6
23
+ Provides-Extra: mcp
24
+ Requires-Dist: mcp<3,>=2; extra == 'mcp'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Cloudfall
28
+
29
+ Cloudfall is the operator's record for a small fleet run by an AI agent.
30
+ The agent decides; Ansible executes; Cloudfall says what the agent may do,
31
+ checks every result, and keeps the evidence. Every operation has a verify
32
+ step and every decision an audit entry: what the fleet looked like, what
33
+ was proposed, who approved, what verify reported. Cloudfall never reports
34
+ success it cannot prove. Open source, plain Debian, no Kubernetes.
35
+
36
+ It is built for teams with one to twenty servers and no platform hire:
37
+ founders leaving a PaaS whose bill crossed $1,000 a month, and anyone whose
38
+ servers are already run by Claude Code, Codex or another agent. Today it
39
+ moves your applications and databases onto one or two Hetzner-class servers
40
+ you own (hardened baseline, monitoring, PostgreSQL and Redis with backups
41
+ that provably restore, health-gated deploys with rollback, a real DNS
42
+ cutover) and an always-on operator runs them there with a receipt for every
43
+ action. The wedge use case is a migration from Render; see the
44
+ [roadmap](ROADMAP.md) for the milestone plan and the
45
+ [manifesto](MANIFESTO.md) for the principles behind the project.
46
+
47
+ Where it is going: your existing Ansible inventory and playbooks as the
48
+ only config, with each playbook exposed to the agent as a declared tool.
49
+ That design is written up and not yet built; see
50
+ [Direction](#direction-your-ansible-not-ours) below.
51
+
52
+ > **Proven live, not promised.** Everything implemented has run on
53
+ > disposable Hetzner Cloud Debian 13 servers: an application actually hosted
54
+ > on Render was cut over with its data and a real TTL-lowered DNS flip on an
55
+ > owned domain, a deliberately bad release was rolled back automatically
56
+ > along the way, an induced failure was remediated by the operator first
57
+ > with a single human approval and then autonomously under declared policy,
58
+ > a timer-driven drill proved a real backup restorable, and an alert crossed
59
+ > the public network to a second machine. The entire migration was driven by
60
+ > an AI agent through `cloudfall-mcp` alone. Full reports:
61
+ > [proving-runs](docs/proving-runs/). The one layer not yet exercised live
62
+ > is bare-metal RAID/storage provisioning (see the [roadmap](ROADMAP.md))
63
+
64
+ ## Why Cloudfall
65
+
66
+ - **A record that answers "why did the agent do that".** Every deploy,
67
+ remediation and drill leaves a receipt; every operator decision records
68
+ the evidence it was based on, the proposal, the approval and the outcome.
69
+ An agent's own log says a tool was called. This says what the fleet looked
70
+ like when it was
71
+ - **Verified, not just ran.** Status is derived from validated observations,
72
+ `cloudfall audit` reports drift between declared config and observed
73
+ servers with distinct exit codes, deploys are gated on health and rolled
74
+ back when it fails. Cloudfall never reports success it cannot prove
75
+ - **Autonomy earned from the record.** The always-on operator watches
76
+ declared alerts and audited drift, remediates what its receipt history
77
+ proves reversible under declared policy, and asks you only about what
78
+ can't be undone
79
+ - **AI-agent native.** Agents operate through a stable CLI and Python API
80
+ with structured JSON results instead of inventing shell commands over SSH.
81
+ The `cloudfall-mcp` server exposes read-only evidence tools freely and
82
+ gates every server-changing tool behind an explicit confirmation handshake
83
+ - **The bill buys computers, not margins.** A steady $1,000/mo cloud bill is
84
+ mostly margin on commodity compute and managed databases. On servers you
85
+ own, that difference becomes runway, every year
86
+ - **Good-enough databases, with proof.** Managed PostgreSQL is mostly
87
+ insurance. Cloudfall keeps the coverage and drops the premium: loopback
88
+ PostgreSQL and Redis with receipted backups and timer-driven restore
89
+ drills that prove a real backup restorable instead of assuming it
90
+ - **Boring on purpose.** Applications run as native systemd services with
91
+ artifact releases and symlink rollback, behind nginx, on stock Debian. Any
92
+ Linux admin can take over the server without Cloudfall installed
93
+
94
+ ## Architecture
95
+
96
+ Three modules with strict boundaries:
97
+
98
+ - [`config/`](config/README.md) — the config: declarative YAML resources and
99
+ their JSON Schemas
100
+ - [`sdk/`](sdk/README.md) — the `cloudfall` CLI and Python API used by
101
+ agents and tooling
102
+ - [`engine/`](engine/README.md) — internal execution machinery (Ansible-based)
103
+
104
+ Rules: the config contains no execution logic, the CLI reads and validates
105
+ the config without Ansible internals, the engine never silently rewrites the
106
+ config, and entry points call the CLI rather than Ansible directly. Mutating
107
+ operations execute through the engine's command-line and playbook contracts,
108
+ never its internals. See [`ARCHITECTURE.md`](ARCHITECTURE.md) for the
109
+ full architecture and the long-term fleet vision.
110
+
111
+ What you run all of this against is a **project**: one directory, usually a
112
+ private repository, holding your fleet (servers, server types, SSH public
113
+ keys), your applications (applications, components, services, domains), the
114
+ declarations that operate them (alert rules, operator policies, logging
115
+ stacks), and a `pyproject.toml` pinning the Cloudfall commit it runs with.
116
+ Every `cloudfall` command runs inside a project: the current directory when
117
+ it is one, or the directory named by `--project` or by the
118
+ `CLOUDFALL_PROJECT` environment variable. Relative paths, including the
119
+ `tmp/` defaults, resolve against the project, as if the command had been
120
+ started there.
121
+
122
+ ## Status
123
+
124
+ Cloudfall is pre-1.0 and honestly labeled. Implemented today, and proven
125
+ live on disposable Debian targets:
126
+
127
+ - Typed YAML config validated against JSON Schemas, deterministic Ansible
128
+ inventory rendering, read-only server inspection, and
129
+ config-versus-observed drift audit with distinct exit codes
130
+ - Hardened Debian baseline, UTC time, nftables firewall, and a guarded
131
+ Loki/Grafana/Alloy logging stack with declared alert rules and
132
+ notification channels over mTLS
133
+ - A service catalog (loopback PostgreSQL, Redis, and Nginx/TLS sites) with
134
+ sops/age secret rendering, receipted backups, and a timer-driven restore
135
+ drill with external alert delivery
136
+ - Health-gated deploys with artifact releases and symlink rollback, plus an
137
+ evidence-derived operations dashboard
138
+ - Render blueprint and live-API importers, guided data migration, and the
139
+ resumable `cloudfall migrate` orchestrator with explicit cutover steps
140
+ - The `cloudfall-mcp` agent server and the always-on operator with receipted
141
+ proposals and policy-bounded autonomy
142
+
143
+ The one implemented layer not yet validated live is bare-metal RAID/storage
144
+ provisioning. Next up is the fleet-density track: enforced resource sharing,
145
+ application mobility with node drain, PostgreSQL point-in-time recovery, and
146
+ the demand-gated failover formation, alongside broader catalog breadth. See
147
+ the [roadmap](ROADMAP.md) for the milestone plan.
148
+
149
+ ## Direction: your Ansible, not ours
150
+
151
+ Most teams that would use Cloudfall already run Ansible. The next step is
152
+ to read their inventory as the only config instead of Cloudfall's own
153
+ resource documents, keep Cloudfall's declarations under one `cloudfall` key
154
+ in their `group_vars` and `host_vars`, and expose each of their playbooks to
155
+ the agent as a declared operation with a risk level, a verify step and an
156
+ audit entry. Ansible is the hand, the agent is the brain, Cloudfall is the
157
+ conscience. The design, its build order and what it deliberately leaves out
158
+ are in [docs/brownfield-design.md](docs/brownfield-design.md). None of it
159
+ is implemented yet; everything below this line is.
160
+
161
+ ## Quickstart
162
+
163
+ Cloudfall is not on PyPI yet; run it from a clone of this repository. The
164
+ only prerequisite is [`uv`](https://docs.astral.sh/uv/): it provisions the
165
+ required Python 3.14 interpreter and all dependencies automatically, so you
166
+ do not need Python 3.14 preinstalled.
167
+
168
+ ```console
169
+ git clone https://github.com/romamo/cloudfall.git
170
+ cd cloudfall
171
+ uv sync
172
+ uv run cloudfall config validate --project config/examples
173
+ ```
174
+
175
+ The command validates every YAML config document against the v1 JSON Schemas
176
+ and then checks cross-resource references. Successful and failed results are
177
+ emitted as structured JSON.
178
+
179
+ Show the non-secret platform inventory and render it as deterministic Ansible
180
+ JSON:
181
+
182
+ ```console
183
+ uv run cloudfall inventory show --project config/examples
184
+ uv run cloudfall-engine inventory render --project config/examples
185
+ ```
186
+
187
+ ### Start your own project
188
+
189
+ Your servers, applications, evidence, and any playbooks of your own belong in
190
+ a project: a directory of its own, kept private. Cloudfall's wheel bundles the
191
+ schema catalog and the Ansible engine, so the project needs no checkout of
192
+ this repository. `cloudfall init` is the first command; run it straight from
193
+ git or from a clone (`uv run cloudfall init ../my-project`), and it pins the
194
+ project to the exact Cloudfall commit it ran from. Without a directory
195
+ argument it initializes the current directory, which must be empty:
196
+
197
+ ```console
198
+ uvx --from git+https://github.com/romamo/cloudfall.git cloudfall init my-project
199
+ cd my-project
200
+ uv sync
201
+ ```
202
+
203
+ The project holds one directory per resource kind (`servers/`,
204
+ `server-types/`, `ssh-public-keys/`, `applications/`, `components/`,
205
+ `services/`, `domains/`, `alert-rules/`, `operator-policies/`,
206
+ `logging-stacks/`), a `secrets/` directory with a `.sops.yaml` template for
207
+ its encrypted fragments, a README with the next steps (pass `--description`
208
+ to say what the project manages), an `AGENTS.md` stating the terms on which
209
+ an AI agent operates the project (see below), a `.gitignore` for the
210
+ runtime `tmp/` directory where evidence and receipts land, a fresh git
211
+ repository, and a `pyproject.toml`:
212
+
213
+ ```toml
214
+ [project]
215
+ name = "my-project"
216
+ version = "0"
217
+ requires-python = ">=3.14"
218
+ dependencies = ["cloudfall"]
219
+
220
+ [tool.uv]
221
+ package = false
222
+
223
+ [tool.uv.sources]
224
+ cloudfall = { git = "https://github.com/romamo/cloudfall.git", rev = "<commit>" }
225
+ ```
226
+
227
+ Only the kind directories are read as resources, so playbooks, roles, docs,
228
+ and tooling files may live anywhere else in the project. Commands find the
229
+ project on their own: the current directory when it is one, else
230
+ `--project`, else `CLOUDFALL_PROJECT`. Declare your SSH key and your first
231
+ server with `cloudfall add`; the server's type is created from the bundled
232
+ Debian 13 baseline when it does not exist yet, and every written file is
233
+ plain YAML you can edit (the reference set under `config/examples/` shows
234
+ every kind). Then validate, render, inspect, and audit from inside the
235
+ project:
236
+
237
+ ```console
238
+ uv run cloudfall add ssh-key ~/.ssh/id_ed25519.pub --owner roman
239
+ uv run cloudfall add server h1 --address 203.0.113.10
240
+ uv run cloudfall config validate
241
+ uv run cloudfall-engine inventory render --output tmp/ansible-inventory.json
242
+ uv run cloudfall-engine playbook run inspect --inventory tmp/ansible-inventory.json --extra-vars cloudfall_inspect_output_directory=$PWD/tmp/observed
243
+ uv run cloudfall audit --observed tmp/observed
244
+ uv run cloudfall-engine playbook run playbooks/proxy.yml --roles roles
245
+ ```
246
+
247
+ Bundled playbooks are addressed by name (`cloudfall-engine playbook list`
248
+ shows them); project playbooks by path, with `--roles` directories searched
249
+ before the bundled roles. To move a project to a newer Cloudfall commit, bump
250
+ `rev` and run `uv sync`.
251
+
252
+ ### The agent contract
253
+
254
+ Humans and AI agents both edit the resources and both run the CLI, with a
255
+ human approving anything that changes a server. `cloudfall init` writes
256
+ that arrangement down as the project's `AGENTS.md`, the operating contract
257
+ an agent started in the directory (Claude Code, Codex, or `cloudfall-mcp`)
258
+ reads first: the canonical `uv run cloudfall` invocation and project
259
+ resolution, every command sorted by effect (changes nothing on servers,
260
+ writes project files, changes servers) with the gate each server-changing
261
+ command demands, the JSON-on-stdout, JSON-error-on-stderr output contract
262
+ and exit codes, what the agent may read under `tmp/`, the rule that secret
263
+ values are never read or written by an agent, and what is committed. A
264
+ one-line `CLAUDE.md` points Claude Code at it. The classification is
265
+ generated from the same catalog the tests check against the CLI, so a new
266
+ command cannot ship unclassified.
267
+
268
+ ## Migrate from Render
269
+
270
+ Map a `render.yaml` blueprint onto Cloudfall config:
271
+
272
+ ```console
273
+ uv run cloudfall import render render.yaml --application acme --server h1
274
+ ```
275
+
276
+ Web, private, and worker services become `Component` resources (workers use
277
+ a service-active health gate instead of a fabricated HTTP check), managed
278
+ PostgreSQL becomes a `Service` with application-owned peer-authentication
279
+ databases, and custom domains become TLS-required `Domain` routes. Each web
280
+ component receives an explicit listen port written as `PORT` into an
281
+ environment file rather than into any resource — the config never contains
282
+ secret values. The full walkthrough, including the Render-to-Cloudfall name
283
+ mapping, is in the [Render migration guide](docs/render-migration-guide.md).
284
+
285
+ The importer never guesses silently. `IMPORT-REPORT.md` records everything
286
+ that was not imported (cron, static, and container services today), every
287
+ assumption it made, and every action required before cutover, including data
288
+ migration and DNS steps. Merge the emitted fragment with your servers and
289
+ server types, validate, then drive the whole migration with one resumable
290
+ plan:
291
+
292
+ ```console
293
+ uv run cloudfall migrate --build acme-api=main
294
+ uv run cloudfall migrate --build acme-api=main --yes
295
+ ```
296
+
297
+ Without `--yes` the command shows the plan; with it, the plan executes step
298
+ by step — baseline, services, artifact builds, health-gated deployments,
299
+ HTTP routes, a DNS-verification pause at the cutover moment, TLS issuance,
300
+ and a final evidence pass that requires a compliant audit and healthy
301
+ routes. Progress persists after every step, so a failed step or the DNS
302
+ pause resumes exactly where it stopped.
303
+
304
+ Managed-database contents follow the same guided path: save the source
305
+ connection URL into a local file and either run `cloudfall data migrate`
306
+ directly (it shows the plan until you add `--yes`) or add `--data <database>=<url-file>` to the migration plan. The
307
+ engine dumps on the target host, restores over the peer-authenticated
308
+ socket, refuses non-empty target databases, verifies per-table row counts,
309
+ and writes a `DataMigrationReceipt`. The same path serves databases hosted
310
+ without a platform blueprint — serverless PostgreSQL providers such as
311
+ Neon — where only the data moves: see the
312
+ [Neon migration guide](docs/neon-migration-guide.md).
313
+
314
+ ## Inspect servers and audit drift
315
+
316
+ Each `Server` references a reusable `ServerType`
317
+ describing its required Debian version, software RAID, filesystem capacity,
318
+ packages, systemd services, and allowlisted configuration evidence.
319
+
320
+ Collect a read-only snapshot from every reachable server, then compare it
321
+ with the config. Workflow commands use [Task](https://taskfile.dev)
322
+ (`brew install go-task` / `apt install task`); every task is a thin wrapper
323
+ over a `uv run` command, so if you prefer not to install Task, copy the
324
+ underlying command from [`Taskfile.yml`](Taskfile.yml):
325
+
326
+ ```console
327
+ task inspect
328
+ task audit
329
+ ```
330
+
331
+ The direct equivalent of `task audit`, for example, is:
332
+
333
+ ```console
334
+ uv run cloudfall audit --project config/examples --observed tmp/observed
335
+ ```
336
+
337
+ Snapshots are written to `tmp/observed/<server>.json` with mode `0600` and
338
+ never include configuration-file contents. The audit emits one JSON report:
339
+ exit code `0` compliant, `1` drift, `2` invalid input, `3` compliance unknown.
340
+
341
+ ## Operations dashboard
342
+
343
+ Cloudfall renders validated config, host observations, and audit results into
344
+ a local read-only dashboard with an evidence-derived task queue and a public-service
345
+ lifecycle (planned → ready → deployed → configured → healthy):
346
+
347
+ ```console
348
+ task dashboard
349
+ ```
350
+
351
+ The build writes `tmp/dashboard/index.html` and machine-readable
352
+ `tmp/dashboard/operations.json`. Missing receipts or observations remain
353
+ visible as `no` or `unknown`; Cloudfall does not infer deployment merely because
354
+ a playbook exists.
355
+
356
+ For a live view, run the dashboard as a server on the management host (a
357
+ VPS or your local machine):
358
+
359
+ ```console
360
+ task dashboard:serve
361
+ ```
362
+
363
+ `cloudfall dashboard serve` re-derives the projection from state and evidence
364
+ on a refresh interval (default 10 seconds) and serves a page that updates
365
+ itself in place; `--inspect-services` additionally probes DNS, TLS, origin,
366
+ and public routes on every refresh, so service health is live. Server
367
+ hardware evidence still comes from observation snapshots: schedule
368
+ `task inspect` (cron or a timer) on the management host to keep it fresh.
369
+ The server binds `127.0.0.1:8100` by default; set `DASHBOARD_HOST`,
370
+ `DASHBOARD_PORT`, and `DASHBOARD_REFRESH` to override. The projection stays
371
+ strictly read-only either way.
372
+
373
+ ## Always-on operator
374
+
375
+ The operator is the always-on half of Cloudfall: a process on the management
376
+ host that watches declared alerts and audited drift, writes every diagnosis
377
+ as a schema-validated proposal receipt, and executes only existing engine
378
+ entry points — after an explicit approval, or autonomously for operation
379
+ classes licensed by a declared `OperatorPolicy` and earned receipt history.
380
+ DNS cutover, data deletion, and database promotion always require explicit
381
+ confirmation. See the [operator guide](docs/operator-guide.md).
382
+
383
+ ## Logging, secrets, and storage guides
384
+
385
+ - A guarded parallel logging slice deploys Loki, loopback-only Grafana, an
386
+ mTLS ingestion gateway, and Alloy without touching legacy agents: see the
387
+ [logging service guide](docs/logging-service-guide.md)
388
+ - Declared secret references render into per-component `0600` environment
389
+ files from a sops/age-encrypted secrets directory: see the
390
+ [secrets guide](docs/secrets-guide.md)
391
+ - New two-drive servers use a RAID1 system area plus independent storage
392
+ tails: review the [hybrid storage design](docs/hybrid-storage-design.md)
393
+ and the destructive, new-server-only
394
+ [storage provisioning guide](docs/new-server-storage-guide.md)
395
+
396
+ ## Contributing and security
397
+
398
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
399
+ Notable changes are recorded in the [changelog](CHANGELOG.md).
400
+
401
+ Everything in this repository is free under the AGPL and sufficient on its
402
+ own. The hosted always-on operator and commercial licensing are how the
403
+ project sustains itself; neither holds back the open core.
404
+
405
+ ## License
406
+
407
+ Cloudfall is licensed under the [GNU AGPL-3.0-or-later](LICENSE). Commercial
408
+ licensing exceptions are available from the copyright holder.
@@ -0,0 +1,165 @@
1
+ cloudfall_engine/__init__.py,sha256=1AOMXhQ7e_fb1iBuIgVGYn_AyzWy1juOLYD-2IO7uEA,147
2
+ cloudfall_engine/__main__.py,sha256=SPGsh5_M2PqYK7MyH-hfOazFkC_HZjBpgHGmQNqTQeo,112
3
+ cloudfall_engine/ansible_inventory.py,sha256=aNgVpz-CFUk5TNHLPzKcshADlBvY1H8Zh-5KgcHGZPI,9060
4
+ cloudfall_engine/artifact.py,sha256=qPNTD12wmekVkllaa8oN3fuSwSY4Q2Dr4xglAoZP87Q,8646
5
+ cloudfall_engine/cli.py,sha256=yv3Gll-G04kKDGb78ZAOGncUc8Q4TOzp_3Lns9wXGjM,9354
6
+ cloudfall_engine/playbook.py,sha256=_0NHtPlvc6-mXp-dqAx9m_DE3keav_o6fWxk2tTDTcc,6484
7
+ cloudfall_engine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ cloudfall/__init__.py,sha256=P4YtIclUb0Q4_H6lrJsiqSyq-_kYqIa7WgJeS6txXnY,577
9
+ cloudfall/__main__.py,sha256=PVPHf6iCzATqfZmcrtjCC3klBO6SXbteDQt-WCmm2k8,98
10
+ cloudfall/agent_tools.py,sha256=Gwql8-_ez1pywSWOLpUCOKvf70t5LHuWJxFcDzVSW9w,30603
11
+ cloudfall/arguments.py,sha256=MsoGEekmGXf9UE7CTRHkKVWvgydAQFaXMw7uGu1SaE8,3308
12
+ cloudfall/audit.py,sha256=FdMryZVdfZDzj982R6YI7NmfvEaMydzJC8brT8Vgqjc,29449
13
+ cloudfall/authoring.py,sha256=iAsulNfr1PuDGuLWHguT-i07VczkrfR158VyzD6e3iQ,11381
14
+ cloudfall/cli.py,sha256=QsMd-e5Iue-LKbRJqyhqarN21_qcQ_x1GIXCKY9SpTU,58639
15
+ cloudfall/commands.py,sha256=jDp_FwH1Nfxd1TplRU8I1ZX5-yITRHjj-k7lU4oMDCM,7616
16
+ cloudfall/cutover.py,sha256=Qzr0JgnYwZgbZv5lSCTNWcMNuBRsgGYMiaKUwnNkBOQ,12736
17
+ cloudfall/dashboard.py,sha256=bbKgvcbsEnkYSVItF4UYJk1LwkBHn2K1ETL3OeLaKG4,16396
18
+ cloudfall/dashboard_server.py,sha256=vwDSP7R58Ei3nXLQ9bZeGyHv0cWkQ2TLOxG41_bVqns,7164
19
+ cloudfall/domain.py,sha256=17-2e8Tns1HfT7sOOlnGr_g95AoOcbPCxuFS00qJLQ4,37584
20
+ cloudfall/importer.py,sha256=pWDNWHnaRis7gAGZaRbOGl2mw_jkyav3TXjXaEF-FZk,30436
21
+ cloudfall/inventory.py,sha256=2oVm8raRbruovGdheZHo5ZWYW3HRFb9HPaajUVqMmDM,74104
22
+ cloudfall/lifecycle.py,sha256=C5pDOfM0pcXX1SXp1D41vUDBXQ_9eKbjvZzrntQ9O3U,27701
23
+ cloudfall/mcp_server.py,sha256=G3JdcrfZkPPFsAUPHZzd75UxcfxnBwGmv8eDogURw7I,23394
24
+ cloudfall/migrate.py,sha256=1N6JBmUGTLAVj-Vz_1WmFWDMbu9AwP7fQ3BwJDulvD8,24100
25
+ cloudfall/observation.py,sha256=bFdANORVvDFRi1-78felMh25xzr5VAA1x9I7J9zwI1g,5066
26
+ cloudfall/operations.py,sha256=4LPc4JeB-nHV2xm653lG8-1yPwg5HIdlW9JMpd5I1wQ,42449
27
+ cloudfall/operator.py,sha256=VUcQo2HasAVpl3SdIvpUvlNeC65tsOjrSy8r7ZE49-0,38038
28
+ cloudfall/project.py,sha256=M-Z08naZLKTR3tdSNsrUUQLnAJnjyvOr8hFNZJMeJF8,30965
29
+ cloudfall/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ cloudfall/render_api.py,sha256=AVj27Q9XqnPmTGDrVlmcfraTgiAikRilzuiVHrwR1DM,12330
31
+ cloudfall/resources.py,sha256=ZLUeXu3RJGi2dPjE-mvEXF8ud20Fh6-sf8yXiPl_o7I,2278
32
+ cloudfall/secrets.py,sha256=lqCcYczb0QDMQkzHppOOvETXey3NdD347Daj-2J-ctY,9208
33
+ cloudfall/service_evidence.py,sha256=NDkf85atCbiCD5-bT_64jEJ7zT0JkBqjJAa9hOz7VvE,22642
34
+ cloudfall/validation.py,sha256=5GtpXU0t5Aoslfwqzf4UOYL6z7q5yoUMA92Eo1o6dqk,50547
35
+ cloudfall/_bundled/schemas/v1/alert-rule.schema.json,sha256=edGZiHltzQ8rc3MZRgssWMQ9uusPfVRKkUR_D60Mmb4,1427
36
+ cloudfall/_bundled/schemas/v1/application.schema.json,sha256=5G8dchgeuUn0u2RnF1nBr3hDWPsblIsviNPylyElk8U,1373
37
+ cloudfall/_bundled/schemas/v1/artifact.schema.json,sha256=Lj5XqM974Zdpa8aSxwFl7ckiOXccx_vyJsqB6cS_j1Y,1657
38
+ cloudfall/_bundled/schemas/v1/backup-receipt.schema.json,sha256=LD9r-NqukNsKqhRUaHtSMkOJV9sTgQzJEahn4QN10Lg,1285
39
+ cloudfall/_bundled/schemas/v1/common.schema.json,sha256=PFGzDYdCYpAayxrB7vgLigYcVTcCzCNr_fRUqsssGXM,2442
40
+ cloudfall/_bundled/schemas/v1/component.schema.json,sha256=1ijKZ3zUO2ESqYpg-AGMoahs_ZOFK13ogN-kVJ9cJ5s,5982
41
+ cloudfall/_bundled/schemas/v1/deployment-receipt.schema.json,sha256=Z8ijenLv3JxusDZulhkcM0WH39-Z7vYZofOe5BZBlZc,1286
42
+ cloudfall/_bundled/schemas/v1/domain.schema.json,sha256=Ca9zm6f3lZuQ2PECCWqvoGkgAtWQyB-66125APuwk54,5117
43
+ cloudfall/_bundled/schemas/v1/environment-receipt.schema.json,sha256=52TDGJOoryyGIEel2-3Nn-RQiYMuV3odQDD6i7EK5ks,1258
44
+ cloudfall/_bundled/schemas/v1/logging-stack.schema.json,sha256=HMueR_bmspug2DnGzAdPuTUFZoVinX0vSDI2Ukss7fw,13525
45
+ cloudfall/_bundled/schemas/v1/observed-domain.schema.json,sha256=VWRgZjgVZXKMGKpiROcxiXl6fMhC8ezhnzQ4si-BIzM,3504
46
+ cloudfall/_bundled/schemas/v1/observed-server.schema.json,sha256=KwuQB-YMlj9zCKezLBtyUwIbkKrDM9yJny9j5kgdTb8,15413
47
+ cloudfall/_bundled/schemas/v1/operator-policy.schema.json,sha256=u7LyFRxR4DC3jz7Vm1EYUP9n8dqln9ueWlfkLIK_oBk,2578
48
+ cloudfall/_bundled/schemas/v1/operator-proposal.schema.json,sha256=4yAJT7igwmCvAwsniNUAlKE66egmvj9HTPb9e7amcRM,6340
49
+ cloudfall/_bundled/schemas/v1/release-receipt.schema.json,sha256=s1ORzpOuaGGBWU0h3djKKQHWKJTaRjHlTF0VlCGN2RU,1579
50
+ cloudfall/_bundled/schemas/v1/server-type.schema.json,sha256=x9D_eqteymnVe02E2sGxdiLvQWQSYcDd7_7Z2Ckkx14,7890
51
+ cloudfall/_bundled/schemas/v1/server.schema.json,sha256=zWvMt3G4gmusF3OL78re7-B13TFb97uZYCJ7s8SjPiQ,3476
52
+ cloudfall/_bundled/schemas/v1/service.schema.json,sha256=KOvdpxmpdXxo6Q9ZU36_YxKeWTwpt2sTTmTSuCxARv4,5275
53
+ cloudfall/_bundled/schemas/v1/ssh-public-key.schema.json,sha256=B-kV_wnmKO6zYT1Ebd2O-Y2FfFxeDCdoT8_JXEO37YY,1290
54
+ cloudfall_engine/_bundled/ansible/ansible.cfg,sha256=jFj7kGaZWQL-p4Q1YWImxlLtN3NoCgeTtngSSvh1oQU,129
55
+ cloudfall_engine/_bundled/ansible/playbooks/backup.yml,sha256=q1MQHjLXG7zGUINx7mDKtEax1-_bw0_MIzjNjyed8Es,2960
56
+ cloudfall_engine/_bundled/ansible/playbooks/baseline.yml,sha256=HmfLqArEWTl9FNAPxGWu0O_fFGHwldA0kkIql34Wu5U,1059
57
+ cloudfall_engine/_bundled/ansible/playbooks/bootstrap.yml,sha256=ZpXxLscEN2wUUUWjiuEXB0ODFpZmpZtmVKlduMMVjSE,639
58
+ cloudfall_engine/_bundled/ansible/playbooks/data.yml,sha256=QfxFZt3j7viG0u7oqSPPVPZCnt8MIryA5K_YMazI-Yw,1043
59
+ cloudfall_engine/_bundled/ansible/playbooks/deploy.yml,sha256=RnVBdkfS9zBiOfJdKoXmVcjXUyQMQfR648le6qU_zKw,1230
60
+ cloudfall_engine/_bundled/ansible/playbooks/domains.yml,sha256=c6UL1pzz_mCms1VRb5YlLhiY3eECpzYd4G4WU_7KdJU,784
61
+ cloudfall_engine/_bundled/ansible/playbooks/health.yml,sha256=ERopZKQPppLT_X4v5I9RlkYDIfbpGd5xDIGxQbSZkV4,986
62
+ cloudfall_engine/_bundled/ansible/playbooks/inspect.yml,sha256=yjxQcQaYTB-FzvGAy1Qn19DWIpBqaLvXWr8dE7v3veU,523
63
+ cloudfall_engine/_bundled/ansible/playbooks/logging.yml,sha256=AUnDrOjP5UHbeJDOwICMuV7BCYEBs-stNzj129lHWkU,1129
64
+ cloudfall_engine/_bundled/ansible/playbooks/restart.yml,sha256=seZExJSoF-PN9o5jjnXAhfNCwgtplPP9Zg_6u8M-6S0,984
65
+ cloudfall_engine/_bundled/ansible/playbooks/rollback.yml,sha256=Bln2j-j9qciOjIXZCWeHHaMITg12k7txIjUWbCXShwQ,1070
66
+ cloudfall_engine/_bundled/ansible/playbooks/services.yml,sha256=eiJ_wJSci30-Bn-N86lSbzo5xErG4UtyD5kfyGaZuy8,1092
67
+ cloudfall_engine/_bundled/ansible/playbooks/time.yml,sha256=w12oCjrrlDdeJwRNaynzvyCXAqaz-i61rO8ZckVa3tM,188
68
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_access/defaults/main.yml,sha256=7t23XBIDEopDMJdVL5juDusSz5Qidw0o_Zwao0R8Frs,85
69
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_access/handlers/main.yml,sha256=DJZsTk4j2JIs-tjcQUDLHlqTOpuhGaoJFvjDPIqd5eI,104
70
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/argument_specs.yml,sha256=5LrS2Dn_feslOO3F--xXlkz0iduA0iAfcDVtaCwAvLk,834
71
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_access/meta/main.yml,sha256=dIJE878tMdTLiY12oTPNn8FKQfmNNNSBnakBRqEZM5Y,301
72
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_access/tasks/main.yml,sha256=GZFR9lhyBfor1Io9BdZgqKo4xXNLfuLrWw1pTjEyYKw,1540
73
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/defaults/main.yml,sha256=1aRtP7ZZKWwEwCwAYnGuLX46ivKk-Ydj112TA66ssng,212
74
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml,sha256=Ohpx_6MK_zCW5_eaubu7gXgBIzPonpIKMP-9KNYOxGU,1209
75
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/meta/main.yml,sha256=3df3fdS4MjeqAPUE1QoeWfG2M-DZf5ITpMa01SLf5l8,304
76
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_bootstrap/tasks/main.yml,sha256=2mHGkVxfAFZyw_vDpyhn_Okx5KsPznbqjOhK5CJcHho,2786
77
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/defaults/main.yml,sha256=obVdHCV6AwAegwooX9yG1dlJIu_2svgzu8ZoydAk_yI,728
78
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml,sha256=FJ4gTuLBapvulBR3OTZKKcl02xZEsAUXKC6dz46OB60,1337
79
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/meta/main.yml,sha256=RQz8937dIj_I3yAnQAzaOlfGMdMeEQAEGswzF9iPofc,328
80
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_data_migration/tasks/main.yml,sha256=TpKs2ot9u3ZPl-DhWZgFblyloLfVHOEW4281ZbRF0SI,6998
81
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/defaults/main.yml,sha256=_y95OnepJImApDoS-Vd8VSrWrOT29dYAlnPHC-0N5fs,373
82
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/argument_specs.yml,sha256=jyY73V5UzxYOzWePyrEyy3jRFevL2vbLQbC9cPRbsK8,3204
83
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/meta/main.yml,sha256=b_boJhoNuJHZdwig9TZNYCFjO0Ysl-IJdEDi8NCU2Uw,307
84
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health.yml,sha256=ghZEWHSKTii-tiKwyGzn3mUc5t6CRpItpb8yIF0HgnA,105
85
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/health_gate.yml,sha256=Nostav1h9Dl29z5hvXEiQj51gFdXgSDYgn2FwxzUoe8,1246
86
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/main.yml,sha256=p2e5YPdydCC2d3JdyXUwEiAOlAiOHAyaXrODFfbY4P0,9603
87
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/restart.yml,sha256=zf8ithYQvmSQMh2QdwIC2IMhBawsRuaGHKv1fnDVdhs,605
88
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/tasks/rollback.yml,sha256=cgeAOQNeIUqF6Xi7QhegA0bd_rWd248v1wpufo8mn6g,1782
89
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_deploy/templates/component.service.j2,sha256=DjGWt5JmlX_bd3ckSGkiLjYOHAR8zqsfzJJhCx6KKFA,876
90
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/defaults/main.yml,sha256=I5udNXnR5xghzDdAjiJtlbTO9_cUWtUphoCXk7n9C1U,62
91
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/handlers/main.yml,sha256=YtIPFM7NDj9DvB31Zm4S5DpDmU4Hgt309MD2548bCmw,110
92
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/argument_specs.yml,sha256=ovrS3oV5-uOs7aARObaDZJ0KjDX7OAI7L9MUlbrCmow,830
93
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/meta/main.yml,sha256=lxc7h7U-ItfIcNZQwR6m4LH0YKyCjyRwKZr6AXbwiuw,300
94
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/tasks/main.yml,sha256=l72_5dFUQTi4I1F0dD9p2juPV_ZJI-ewY9Brfb1447w,1562
95
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2,sha256=4MA4sF1i7whtdtOUwuv0NpXkmoowwaqLX4IdodslccI,818
96
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/defaults/main.yml,sha256=XxOS1DE6SCZUykOHx0RJi2d7Hat04THCo_ETXUufet4,186
97
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/argument_specs.yml,sha256=v0iu7HQWFgeyWQf_txTZi8ndEEHNQmvjbADfQlyZI20,606
98
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/meta/main.yml,sha256=SgT9WDZxjJ-yOY4VXEiq3emlq_PCghdiEJaE6v745dY,303
99
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_inspect/tasks/main.yml,sha256=kKGdTD7qVvbPNrKk737HxIBDhgUUcnTFzaevzQVH7lI,21219
100
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/defaults/main.yml,sha256=AQF6wFuc6kqc_heyPJIoi97rpw_TkujiJLDClxaXCjU,295
101
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/handlers/main.yml,sha256=vZbYiDdgxd8Mis5cQL7-guo_lPmvbbxSsV4jDfqKcjg,656
102
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml,sha256=lTctGmqhlA7ETE1QheebIL7byGQ3Nkx7l7eo8PoGxU0,6168
103
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/meta/main.yml,sha256=_Eyhjp4OVCjQETLWlqHSDS00_eoc1YRxXZ75eO9VzuA,250
104
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/tasks/main.yml,sha256=LL7sPV-2HmRqgK-Vatdv_HG2j_q8otHwvl9TZISBOgs,14933
105
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2,sha256=C1Cw_KeeJnvWWKZ537MmPOy6Q1FvA3B6WDqPgGuhkEE,310
106
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2,sha256=yR3Wh6wHxIpdcPy8T7B73KWjvbW7XgCT78N_f-4luZQ,1039
107
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2,sha256=_V4tY4OpA1_V-e4NHmGBqkHy_ftb9hLKj66q_RIEjCw,466
108
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2,sha256=yzjRsJSGBDg-gUyhSIZhMooPmh0i_rq29UJUf2MA0FE,294
109
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2,sha256=cgq1-i7YW5LFVnp6UINgwIxQaz1sMa7X-r_gQ2doTB0,124
110
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2,sha256=6DzaGQqQeOWMSeBc1igE4IjxBrO-rnBpgLXcANbbebc,1247
111
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2,sha256=w46NSjXqJjWQwbDhWZFmArz1U5djpwRVZ7gmkQFWpFk,1786
112
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2,sha256=6KnyE6Bldjy8Kkz3_nOov8FJJZzZMDbvWP79BB_RtbA,330
113
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2,sha256=oH1XBvET9cyUsqMJxvZ4b7gO0D85URl09CMvxijw7eY,480
114
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2,sha256=vjySx9MYZIT-UCGtzE95iLtZUshx4VzgHfAipSvjkxY,597
115
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2,sha256=V2aWBk5IZDg-RQmr69xYE1zZFW6F-gK7dGPtMEQdqkk,992
116
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/defaults/main.yml,sha256=ozTbaPBdYhMvVLebUWBLSctxLfSJkWTxKcfeu-uUbXE,351
117
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/handlers/main.yml,sha256=XkrHqSLK7Nho71JAyVSWkickbZ2t9GV1C-PTJ8CbIHY,130
118
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml,sha256=gCmGZp6gC117JE4rc3bvEMNRbWuTJyCmme-9xF_PFnc,3253
119
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/meta/main.yml,sha256=pG4KnP6F-9Wg2jZcntQiXAmAOFMFhDZwld7f5TjzwLY,245
120
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/tasks/main.yml,sha256=weagVWgxvbsN1ASWb6eaTKvq8o4Qo_nQybqpvGCCAXU,6570
121
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2,sha256=uzMfUGHm1FaaNWG2rU1S8qLCnOVSFqWLInHpgegjWk4,112
122
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2,sha256=gFqjAtKM3V0oRO20wqgw8wlgAqs3Gt-QJXaYsthtt4A,4130
123
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/defaults/main.yml,sha256=evebYt78wUUudKTXD0QVjy2_fvklyg_JA1cL-lqkIxs,149
124
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/handlers/main.yml,sha256=tspN45WKLzzCeN_SMNnD76nOjkpM-ZHn4uHteGU1AmU,104
125
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml,sha256=Uqq6Rw5sS5ndWnJXYGBwaXCxAtNEwTnMBtWZ24ABbNQ,1748
126
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/meta/main.yml,sha256=JG-KCc8cIDFSNsbIvs6mIXXkoLEwjAzGwxjIln0rEQA,305
127
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/tasks/main.yml,sha256=BcXD4f2amNJa4Mxbpw48cWT-ktd9n3YrnobGbaS6-9E,6435
128
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2,sha256=8eDse0kAqUs0wlkIvspaY2YpS2lqOgSNT6TOrbKqzlQ,1697
129
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/defaults/main.yml,sha256=GfZLyhibrsNohN2PyhRMrLW9CQROe4PbPd1oGeLvwj8,295
130
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/handlers/main.yml,sha256=tOpbBguY5YPKYQfcjnrELl95RrJ6JcAEnp7qPSNyOdg,227
131
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml,sha256=Y8MZ3MMHV2zP4KF5p5MDWHcMk5Pz68GxfsgTRRi06s0,2469
132
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/meta/main.yml,sha256=nDK_RbMK5c13V0W6QDBdUAFCKAUiVg6lxcPp-SoxOC0,306
133
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/tasks/main.yml,sha256=l5IExw3coWtfSeVyM-XlMGqP8i9zGhE_-ZOlTj4ai6E,8324
134
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.service.j2,sha256=HWYnbMzM4PElfkDI6zXg0wgey6qnOg71M8b-ZMHkBmU,436
135
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2,sha256=7nI6FPX08xhvsUQXE9GgkG3XBfuQCTybfH4N0HFnRt8,651
136
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2,sha256=adK6OXkBU0jPF83iiretIUKcnWpHWoieHEA24ILFbyw,280
137
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2,sha256=jHwNXzZPsAJFRcy-e9yHXMUJD0XRm9VMOSf18hd5dE4,180
138
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2,sha256=y__zh07uVf4YhfU1MdDWb0shGTpxQCmgv66liZOKAbY,450
139
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2,sha256=P0wlfimCyPjnh0xvowrEXsHT719-P4Q6pUBeHkOi-Nk,1130
140
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2,sha256=bKDkza_1rqSgRdBF_Q3BN1LwegTvs0OQ4P47IPylZzI,299
141
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/defaults/main.yml,sha256=Bc2YtaMF5fu7K3hcMXgLYDc9tHGlZx3xkljwY_lVsSY,32
142
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/handlers/main.yml,sha256=AlrgKjF4Pz81oW1ipNLZmoa4ed1IXeK6YiouM6MCR1E,137
143
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/argument_specs.yml,sha256=X5LixOwQCh_ljVEcPIz2ckICoaOjWDu_w8CQ1hMoEuI,1667
144
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/meta/main.yml,sha256=QNkET6LDIUlFnQSqHHWhdAnFONfucGpyvL9lTrHXfFs,286
145
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/tasks/main.yml,sha256=FRT17NNCb-rxFnpBHmILlHy9ymVa-YET7Ke4_hW6m74,4167
146
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.service.j2,sha256=oc2eObAE2oWZvGglJNH_llCOrgOHdoEW8CgnIL5x_zE,291
147
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.sh.j2,sha256=_8DCNfzW7H4pcW8FKnGBHvZ6jZGrbyanUbz27oo0uTA,508
148
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/backup.timer.j2,sha256=8oSTHAcXnxmikk_xIGO7kdYXMBXtAIyQureYppK6FJg,265
149
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2,sha256=ZKsTlSzpyLAFASTbmNRofyfEZCR904AxrZJgFm2ozIM,308
150
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.service.j2,sha256=yQewzJBGjil9_hrmvtSJR7Palsg6w4-P6tRyna-vDaM,305
151
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2,sha256=or4Ewh_vCa82AmSjrxOq1N6juGqy9aAoszy3OK0V0A0,528
152
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2,sha256=gdvd6fzHm6ZAFoUhcprfYZB-5bBzitSQhUcCD44sBlk,284
153
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_time/defaults/main.yml,sha256=1Gvzkq3e7QVdtIMp-Zp7z4jrUHVk2Ic7gM92VKA9Rlc,37
154
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/argument_specs.yml,sha256=WgoUCgoDRhCaLtJO-d4iNa52PUQmXAKaT3Ob5wdlTxw,215
155
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_time/meta/main.yml,sha256=_-PukSy4LBcH4lI4Eg1zd91_xxdCG8fB5a2hnqXRWsU,297
156
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_time/tasks/main.yml,sha256=pYvVY9pCx-wpXfAEgMNvXi9LApWIpS1jy0O3u0xZ4Gk,3424
157
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml,sha256=kGs_LjzdMQCHVf7i-iSoYHAipTnYYu7xJpSTipytaZA,95
158
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml,sha256=RypR59CQTkFJCmIp_WHkK4Ux0FWRAB9UwsdPXxQy40w,234
159
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml,sha256=lSeOfgPZHEIhf5S0M3iutnKeI0c1UXSZIdLh5jDmO58,297
160
+ cloudfall_engine/_bundled/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml,sha256=ULJlSnxbVfUSkFYpDx8fKtHL0uX1eYmZG8EwEttO0Sc,1063
161
+ cloudfall-0.2.0.dist-info/METADATA,sha256=liWr3fPegCkBc9308_cC5GPoBuA2zNip-xeutGU6XA0,19617
162
+ cloudfall-0.2.0.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
163
+ cloudfall-0.2.0.dist-info/entry_points.txt,sha256=ePlVxgx1_WbbfxQztbyKT1y1DQfyH1L12VtJ3g7ajtI,133
164
+ cloudfall-0.2.0.dist-info/licenses/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
165
+ cloudfall-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.3
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ cloudfall = cloudfall.cli:run
3
+ cloudfall-engine = cloudfall_engine.cli:run
4
+ cloudfall-mcp = cloudfall.mcp_server:run