mayhem-cli 0.5.1__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 (107) hide show
  1. mayhem/agent/__init__.py +1 -0
  2. mayhem/agent/cli.py +36 -0
  3. mayhem/agents/__init__.py +1 -0
  4. mayhem/agents/capabilities.py +106 -0
  5. mayhem/agents/executors.py +430 -0
  6. mayhem/agents/impact.py +729 -0
  7. mayhem/agents/lease_client.py +141 -0
  8. mayhem/agents/probes.py +284 -0
  9. mayhem/agents/protocol.py +134 -0
  10. mayhem/agents/server.py +281 -0
  11. mayhem/agents/sinks.py +60 -0
  12. mayhem/agents/transports.py +134 -0
  13. mayhem/agents/watchdog.py +140 -0
  14. mayhem/cli/__init__.py +11 -0
  15. mayhem/cli/app.py +154 -0
  16. mayhem/cli/campaign.py +496 -0
  17. mayhem/cli/config_cmd.py +47 -0
  18. mayhem/cli/context.py +23 -0
  19. mayhem/cli/dependency.py +429 -0
  20. mayhem/cli/exit_codes.py +24 -0
  21. mayhem/cli/experiment.py +24 -0
  22. mayhem/cli/lifecycle.py +805 -0
  23. mayhem/cli/resolver.py +72 -0
  24. mayhem/cli/services.py +459 -0
  25. mayhem/cli/style.py +101 -0
  26. mayhem/cli/toolkit.py +41 -0
  27. mayhem/cli/topology.py +127 -0
  28. mayhem/config.py +208 -0
  29. mayhem/controller/__init__.py +1 -0
  30. mayhem/controller/compensation.py +2156 -0
  31. mayhem/controller/executor.py +1719 -0
  32. mayhem/controller/janitor.py +196 -0
  33. mayhem/controller/observability_collector.py +382 -0
  34. mayhem/controller/observations.py +102 -0
  35. mayhem/controller/planner.py +715 -0
  36. mayhem/controller/recovery.py +245 -0
  37. mayhem/controller/resilience_report.py +585 -0
  38. mayhem/controller/resource_manager.py +457 -0
  39. mayhem/controller/safety.py +392 -0
  40. mayhem/domain/__init__.py +6 -0
  41. mayhem/domain/campaigns.py +118 -0
  42. mayhem/domain/cancellation.py +110 -0
  43. mayhem/domain/candidates.py +101 -0
  44. mayhem/domain/capabilities.py +86 -0
  45. mayhem/domain/catalog.py +727 -0
  46. mayhem/domain/checks.py +173 -0
  47. mayhem/domain/common.py +104 -0
  48. mayhem/domain/coverage.py +106 -0
  49. mayhem/domain/decisions.py +57 -0
  50. mayhem/domain/errors.py +87 -0
  51. mayhem/domain/events.py +61 -0
  52. mayhem/domain/execution_context.py +120 -0
  53. mayhem/domain/execution_loci.py +94 -0
  54. mayhem/domain/experiments.py +370 -0
  55. mayhem/domain/faults.py +239 -0
  56. mayhem/domain/identity.py +200 -0
  57. mayhem/domain/k8s_adapter.py +132 -0
  58. mayhem/domain/leases.py +186 -0
  59. mayhem/domain/load_strategy.py +98 -0
  60. mayhem/domain/m5_campaign.py +120 -0
  61. mayhem/domain/maniac.py +93 -0
  62. mayhem/domain/observability.py +146 -0
  63. mayhem/domain/outcomes.py +92 -0
  64. mayhem/domain/remote_agent_interface.py +70 -0
  65. mayhem/domain/resources.py +245 -0
  66. mayhem/domain/risks.py +61 -0
  67. mayhem/domain/run_outcome.py +146 -0
  68. mayhem/domain/runtime_adapter.py +256 -0
  69. mayhem/domain/success.py +329 -0
  70. mayhem/domain/topology.py +452 -0
  71. mayhem/infra/__init__.py +1 -0
  72. mayhem/infra/campaign_engine.py +205 -0
  73. mayhem/infra/candidate_gates.py +124 -0
  74. mayhem/infra/candidate_generator.py +110 -0
  75. mayhem/infra/coverage_repository.py +101 -0
  76. mayhem/infra/lease_repository.py +129 -0
  77. mayhem/infra/maniac.py +103 -0
  78. mayhem/infra/migrations.py +596 -0
  79. mayhem/infra/migrator.py +149 -0
  80. mayhem/infra/report.py +227 -0
  81. mayhem/infra/store.py +200 -0
  82. mayhem/py.typed +0 -0
  83. mayhem/spec.py +52 -0
  84. mayhem/toolkit/__init__.py +1 -0
  85. mayhem/toolkit/fingerprint.py +69 -0
  86. mayhem/toolkit/hashing.py +32 -0
  87. mayhem/toolkit/manifests/docker.yaml +11 -0
  88. mayhem/toolkit/manifests/podman.yaml +11 -0
  89. mayhem/toolkit/manifests/stress-ng.yaml +11 -0
  90. mayhem/toolkit/manifests/tc-netem.yaml +11 -0
  91. mayhem/toolkit/manifests/toxiproxy.yaml +10 -0
  92. mayhem/toolkit/registry.py +185 -0
  93. mayhem/toolkit/tool_runner.py +129 -0
  94. mayhem/topology/__init__.py +10 -0
  95. mayhem/topology/providers/__init__.py +0 -0
  96. mayhem/topology/providers/adapter_registry.py +60 -0
  97. mayhem/topology/providers/base.py +31 -0
  98. mayhem/topology/providers/compose.py +207 -0
  99. mayhem/topology/providers/docker_adapter.py +277 -0
  100. mayhem/topology/providers/docker_runtime.py +461 -0
  101. mayhem/topology/providers/podman_adapter.py +328 -0
  102. mayhem/topology/resolve.py +196 -0
  103. mayhem/topology/service.py +158 -0
  104. mayhem_cli-0.5.1.dist-info/METADATA +555 -0
  105. mayhem_cli-0.5.1.dist-info/RECORD +107 -0
  106. mayhem_cli-0.5.1.dist-info/WHEEL +4 -0
  107. mayhem_cli-0.5.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,555 @@
1
+ Metadata-Version: 2.5
2
+ Name: mayhem-cli
3
+ Version: 0.5.1
4
+ Summary: General-purpose chaos engineering and resilience-testing framework: a chaos experiment orchestration engine.
5
+ Author: Mayhem Engineering
6
+ License: Proprietary
7
+ Requires-Python: >=3.12
8
+ Requires-Dist: click>=8.1
9
+ Requires-Dist: pydantic>=2.9
10
+ Requires-Dist: pyyaml>=6.0
11
+ Requires-Dist: structlog>=24.1
12
+ Requires-Dist: typer>=0.12
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Mayhem
16
+
17
+ **Chaos engineering for Docker & Podman (Kubernetes on the roadmap): discover
18
+ your system, break it on purpose, prove it recovers — every time, with
19
+ evidence.**
20
+
21
+ Mayhem is a drill engine, not a command library. It turns your
22
+ `docker-compose` blueprint into a live topology graph, compiles one declarative
23
+ `kind: drill` YAML file into a frozen, safety-gated execution plan, injects
24
+ faults through a capability-aware toolkit, and ends every run with a machine
25
+ verdict derived from the observations it actually recorded. Steps, probes,
26
+ criteria evaluations, and decisions all land in SQLite — nothing is "trust me,
27
+ it worked."
28
+
29
+ ```
30
+ compose blueprint ─▶ topology graph ─▶ compile drill spec ─▶ frozen plan
31
+ │
32
+ verdict ◀── machine criteria ◀── inject → observe → recover
33
+ │
34
+ SQLite evidence + decision trace
35
+ ```
36
+
37
+ **Contents**
38
+ - [Installation](#installation)
39
+ - [Why Mayhem](#why-mayhem)
40
+ - [What a Run Looks Like](#what-a-run-looks-like)
41
+ - [Quickstart](#quickstart)
42
+ - [Authoring a Drill](#authoring-a-drill)
43
+ - [CLI Reference](#cli-reference)
44
+ - [Configuration](#configuration)
45
+ - [Safety Model](#safety-model)
46
+ - [Exit Codes](#exit-codes)
47
+ - [Architecture](#architecture)
48
+ - [Development](#development)
49
+
50
+ ---
51
+
52
+ ## Installation
53
+
54
+ Mayhem publishes to PyPI **as `mayhem-cli`** — the bare `mayhem` name is
55
+ taken, so you install the CLI under that name. The console commands it puts on
56
+ your `PATH` are still `mayhem` (and `mayhem-agent`).
57
+
58
+ Requires **Python 3.12+**.
59
+
60
+ ```bash
61
+ pip install mayhem-cli
62
+ ```
63
+
64
+ That is it — `mayhem` is now on your `PATH`:
65
+
66
+ ```bash
67
+ mayhem --help
68
+ ```
69
+
70
+ **Isolated installs (recommended on macOS/Linux)** — keeps the CLI out of your
71
+ global Python:
72
+
73
+ ```bash
74
+ pipx install mayhem-cli # or: uv tool install mayhem-cli
75
+ ```
76
+
77
+ **Upgrading** — each git tag (`v0.5.0` or `0.5.0`) publishes that exact version:
78
+
79
+ ```bash
80
+ pip install -U mayhem-cli
81
+ ```
82
+
83
+ Need the source checkout instead? See [Development](#development).
84
+
85
+ ---
86
+
87
+ ## Why Mayhem
88
+
89
+ Most chaos tooling hands you a list of commands — "kill this container," "add
90
+ latency here" — and leaves you to figure out the blast radius, the cleanup, and
91
+ what "healthy" means. Mayhem treats a drill as a **planned, gated, evidenced
92
+ experiment** instead.
93
+
94
+ | | Hand-rolled chaos scripts | Mayhem |
95
+ |---|---|---|
96
+ | **Targeting** | You pick a container and hope | Faults plan against a discovered topology graph, so targets stay real |
97
+ | **Safety** | Your discipline | Risk ceiling, `max_faults` budget, and an impact gate that re-proves every fault before anything is injected |
98
+ | **Definition** | Throwaway shell one-liners | One `kind: drill` YAML: per-container faults, ordering, checks, success criteria, observability |
99
+ | **Cleanup** | You remember to | Every fault ships a compensation contract; the janitor sweeps dirty leases after crashes |
100
+ | **Verdict** | Eyeball the dashboards | `PASS`/`FAIL` from typed success criteria evaluated over recorded observations |
101
+ | **Evidence** | Shell history | Immutable SQLite journal: steps, probes, decisions, outcome |
102
+
103
+ Concretely:
104
+
105
+ 1. **Topology-aware planning.** Mayhem reads your compose blueprint and
106
+ discovers the live services, hosts, and dependencies to build a graph.
107
+ Faults are planned against that graph, not applied blind; compose project
108
+ filtering keeps unrelated stacks out.
109
+
110
+ 2. **Drill specs, not scripts.** One YAML file declares everything — faults,
111
+ cross-container ordering, checks, success criteria, observability sources —
112
+ and you define what "healthy" looks like *before* you break anything.
113
+
114
+ 3. **Machine verdicts.** Optional typed success criteria turn every run into a
115
+ `PASS`/`FAIL` verdict derived from the observations actually recorded — no
116
+ eyeballing.
117
+
118
+ 4. **Automatic recovery.** Every fault ships a compensation contract: an undo
119
+ op plus a verification probe generated from the same plan parameters, so
120
+ what was injected is exactly what gets removed and re-proven healthy. If a
121
+ round fails or the controller crashes, the janitor sweeps dirty leases and
122
+ reconciles the workload — no manual cleanup. The per-fault lifecycle is
123
+ documented in [`docs/compensation.md`](docs/compensation.md).
124
+
125
+ 5. **Honest evidence.** Runs persist an immutable event journal, criteria
126
+ evaluations, and a governing-decision trace in SQLite. No "trust me, it
127
+ worked."
128
+
129
+ 6. **Prefix-shortened CLI.** Type `mayhem r` instead of `mayhem run`;
130
+ `mayhem e v` means `mayhem experiment validate`. Every command abbreviates
131
+ to any unique prefix.
132
+
133
+ ---
134
+
135
+ ## What a Run Looks Like
136
+
137
+ A clean run needs no interpretation — the verdict is one line away. Truncated
138
+ for readability:
139
+
140
+ ```
141
+ $ mayhem run mayhem.yaml --compose docker-compose.yml
142
+
143
+ # Run r-process-drill-8f2a1c
144
+ **status**: completed
145
+ **verdict**: pass
146
+ **success criteria**: ALL PASS
147
+ - [PASS] status:api-up.status PASS (expected 200, got 200)
148
+ - [PASS] latency:api-up.latency_ms PASS (31.2 <= 500.0)
149
+ **observations**: 2/2 sources collected
150
+ **decisions**: 5 governing decision revisions (snapshot in the run row)
151
+ **wall**: 32.4s
152
+
153
+ run r-process-drill-8f2a1c — inspect with `mayhem history r-process-drill-8f2a1c`
154
+ ```
155
+
156
+ Reading the transcript, top to bottom:
157
+
158
+ 1. **Status** — the run's machine state: `completed` (or `failed`/`aborted`
159
+ with a non-zero exit and dirty-lease warnings).
160
+ 2. **Verdict** — `pass`/`fail` derived from the success criteria you declared
161
+ in the spec; `undecided` when a run aborts or the spec has no success block.
162
+ 3. **Success criteria** — every criterion evaluated against real observations,
163
+ one line each, so a failure tells you exactly what drifted.
164
+ 4. **Observations** — how many configured evidence sources actually delivered
165
+ data (probes, logs, metrics …).
166
+ 5. **Decisions** — the governing decision revisions that shaped this run; the
167
+ decision trace is queryable afterward via `mayhem history`.
168
+ 6. **Copy-paste handle** — the run id for the follow-up commands below.
169
+
170
+ From there: `mayhem status` lists recent runs, `mayhem status --run <run-id>`
171
+ shows full recorded metadata, and `mayhem history <run-id>` replays the
172
+ complete event journal (every step, probe sample, and lease for that run).
173
+ Add `--debug` to `mayhem run` to stream each step live as it happens (`[ok]
174
+ injected proc.pause 10s into testcase-api`, `[ok] recovered ... (compensation
175
+ ok)`).
176
+
177
+ ---
178
+
179
+ ## Quickstart
180
+
181
+ **Prerequisites**
182
+
183
+ - Python 3.12+
184
+ - Docker with Compose v2 (or Podman, used via the `--podman` flag)
185
+ - No host tooling required up front: `mayhem toolkit list` probes for the
186
+ capabilities (docker, podman, network tooling, `k6`, …) each fault needs,
187
+ and the impact gate refuses to run anything it cannot prove.
188
+
189
+ **Install**
190
+
191
+ ```bash
192
+ pip install mayhem-cli
193
+ ```
194
+
195
+ > The PyPI package is **`mayhem-cli`** (`mayhem` is taken); the console command
196
+ > remains `mayhem`. Full instructions in
197
+ > [Installation](#installation). For a source checkout use
198
+ > `pip install -e .` (see [Development](#development)).
199
+
200
+ **Run the bundled example**
201
+
202
+ A complete, self-contained example lives in `examples/testCase/` — a
203
+ six-service compose stack (API, web, load balancer, dual download builders, and
204
+ Postgres), its drill spec, and the fixtures it relies on.
205
+
206
+ ```bash
207
+ # 0. Bring the stack up.
208
+ cd examples/testCase
209
+ docker compose up -d
210
+
211
+ # 1. See the topology Mayhem will plan against (blueprint ─▶ live graph).
212
+ mayhem topology discover --compose docker-compose.yml
213
+
214
+ # 2. Compile the spec and run every safety gate — injects nothing.
215
+ mayhem validate mayhem.yaml --compose docker-compose.yml
216
+
217
+ # 3. Print the frozen execution plan as JSON.
218
+ mayhem plan mayhem.yaml --compose docker-compose.yml
219
+
220
+ # 4. Execute the drill and print the run summary.
221
+ mayhem run mayhem.yaml --compose docker-compose.yml
222
+
223
+ # (Variant) limit a run to ONE container — the pinned container's faults run,
224
+ # every other container's rounds are dropped from the frozen plan.
225
+ mayhem run mayhem.yaml --compose docker-compose.yml --ctr testcase-api
226
+
227
+ # (Variant) `maniac` draws its random rounds against a single container too —
228
+ # handy when you want to chaos-test one container without touching the others.
229
+ mayhem maniac --compose docker-compose.yml --ctr testcase-api
230
+
231
+ # 5. Replay any run's evidence.
232
+ mayhem status --run <run-id>
233
+ mayhem history <run-id>
234
+ ```
235
+
236
+ `mayhem run` prints a copy-paste `run <run-id> — inspect with mayhem history
237
+ <run-id>` line at the end; that id is all you need for the evidence commands.
238
+
239
+ The example spec exercises **38 distinct faults** (every catalog entry that
240
+ applies to a Docker/Podman compose service) across the `testcase-lb`
241
+ load-balancer — CPU/memory/fd/disk pressure, load spikes, protocol abuse,
242
+ network latency/bandwidth/packet-loss, dependency and database and DNS faults,
243
+ TLS failure, container kill/restart/pause, HTTP error injection —
244
+ capped at one concurrent fault (`max_faults: 1`, `risk_ceiling: critical`)
245
+ with auto-recovery off (`recovery: false`), so the downstream checks observe
246
+ whether the stack self-heals on its own. The remaining nine `k8s.*` catalog
247
+ faults are exercised against a Kubernetes blueprint in
248
+ [`examples/k8s`](examples/k8s) (planning-only until the M8 driver lands), so
249
+ **every fault in the catalog has an example**.
250
+
251
+ Omit `--compose` and Mayhem auto-detects `docker-compose.yml` (or
252
+ `compose.yml`) in the current directory.
253
+
254
+ > `validate` and `plan` work against the blueprint alone and never touch live
255
+ > containers. `run` re-proves every fault at the impact gate against the live
256
+ > graph and bypasses — or, without `--skip-gate`, refuses — anything it cannot
257
+ > prove injectable.
258
+
259
+ ---
260
+
261
+ ## Authoring a Drill
262
+
263
+ A drill is one `kind: drill` YAML file — the DSL reference lives in
264
+ [`docs/drill-spec.md`](docs/drill-spec.md). The shape:
265
+
266
+ ```yaml
267
+ apiVersion: "mayhem/v1"
268
+ kind: drill
269
+ name: checkout-recovery
270
+ hypothesis: "checkout stays available while cart writes are throttled"
271
+
272
+ config:
273
+ risk_ceiling: high # refuse faults riskier than this (policy ceilings only tighten)
274
+ max_faults: 1 # never inject more than one fault at once
275
+ timeout: 30m
276
+
277
+ containers:
278
+ cart-api: # keys ARE compose container_name values
279
+ faults:
280
+ - fault: net.latency
281
+ duration: 10s
282
+ params: { seconds: 5s, jitter_ms: 10 }
283
+
284
+ execution:
285
+ - parallel: [cart-api]
286
+ - check_spec: # locus-aware checks
287
+ - id: cart-health
288
+ probe: { type: http, url: http://cart-api:8080/_health, expected_status: 200 }
289
+ execution: service
290
+ target: cart-api
291
+
292
+ success: # machine verdict
293
+ require_all: true
294
+ criteria:
295
+ - type: status
296
+ source_id: cart-health.status
297
+ expected: 200
298
+ - type: latency
299
+ source_id: cart-health.latency_ms
300
+ lt_ms: 800
301
+
302
+ observability: # evidence sources
303
+ sources:
304
+ - kind: logs
305
+ source_id: cart-logs
306
+ container: cart-api
307
+ tail: 200
308
+ - kind: probe
309
+ source_id: cart-probe
310
+ probe: { type: http, url: http://cart-api:8080/_health }
311
+ cadence: 2s
312
+ ```
313
+
314
+ Validate with `mayhem validate mayhem.yaml`; unknown parameters, out-of-range
315
+ durations, untargetable node kinds, and capability gaps are all compile-time
316
+ errors — before anything is injected.
317
+
318
+ ### Campaigns
319
+
320
+ A campaign groups drill specs under one execution umbrella (ADR-0022/0023):
321
+ experiments run sequentially in priority order, and the campaign's failure
322
+ policy and time window govern the whole run.
323
+
324
+ ```bash
325
+ mayhem campaign create black-friday \
326
+ --description "BFCM chaos" --hypothesis "checkout survives every single-fault failure"
327
+ mayhem campaign add-experiment black-friday mayhem.yaml
328
+ mayhem campaign add-experiment black-friday checkout-recovery.yaml
329
+ mayhem campaign start black-friday # draft -> running
330
+ mayhem campaign run black-friday --compose docker-compose.yml
331
+ ```
332
+
333
+ A campaign is born `draft` and moves through
334
+ `scheduled → running → paused → completed / aborted` (`archive` closes a
335
+ finished campaign). Per-experiment results land in the observations table
336
+ under the campaign id, and the failure policy selects the next action when
337
+ one experiment fails:
338
+
339
+ | `on_experiment_failure` | Meaning |
340
+ |-------------------------|---------|
341
+ | `abort_campaign` (default) | Stop the remaining experiments. |
342
+ | `skip_and_continue` | Record the failure and run the next experiment. |
343
+ | `retry_then_abort` | Retry the failed experiment once, then abort the campaign. |
344
+
345
+ Window fields (`window_json`): `start_epoch_s` / `end_epoch_s`,
346
+ `max_duration_s` (hard stop), and `cooldown_between_experiments_s` between
347
+ successive experiments.
348
+
349
+ ---
350
+
351
+ ## CLI Reference
352
+
353
+ Global options (accepted at any level, before or after the command):
354
+
355
+ | Option | Meaning |
356
+ |--------|---------|
357
+ | `--db PATH` | SQLite database path (default `mayhem.db`). |
358
+ | `--config PATH` | Path to `mayhem.yaml` (overrides auto-detection). |
359
+ | `--profile NAME` | Configuration profile to merge. |
360
+ | `--allow-critical` | Acknowledge `critical`-risk faults (e.g. `k8s.node_drain`). |
361
+ | `--skip-gate` | Run even when the impact gate proved some faults inert. |
362
+ | `--podman` | Use Podman instead of Docker. |
363
+ | `--debug` | Re-raise errors instead of rendering them. |
364
+
365
+ Commands:
366
+
367
+ | Command | Description |
368
+ |---------|-------------|
369
+ | `mayhem topology discover` | Discover live services/hosts and the dependency edges from the blueprint. |
370
+ | `mayhem validate SPEC` | Compile a drill spec and run every safety gate without injecting. |
371
+ | `mayhem plan SPEC` | Compile against the topology and print the frozen plan JSON. |
372
+ | `mayhem run SPEC` | Compile and execute a drill; print the run summary. `--ctr CONTAINER` scopes execution to one container (any `container_name:` value or runtime container name from the blueprint). |
373
+ | `mayhem maniac SPEC` | Compile and execute a random-injection drill — draws `run_level` single-fault rounds governed by the maniac seed/level (spec `config.maniac`, falling back to the `maniac:` layer of `mayhem.yaml`). `--ctr CONTAINER` narrows every draw (and the zero-config synthesized pool) to one container. |
374
+ | `mayhem status` | Show runs recorded in the database (`--json` supported). |
375
+ | `mayhem history RUN_ID` | Replay steps, events, and leases recorded for one run. |
376
+ | `mayhem recover RUN_ID` | Recover every orphaned fault lease belonging to a run. |
377
+ | `mayhem janitor` | Sweep leases past their TTL; expire pending runs; compensate. |
378
+ | `mayhem toolkit faults` | List the fault catalog with risk and compensatability. |
379
+ | `mayhem toolkit list` | Probe the host for the tools/capabilities faults require. |
380
+ | `mayhem experiment show SPEC` | Print the parsed drill spec as JSON. |
381
+ | `mayhem experiment validate` | Alias of `validate`. |
382
+ | `mayhem config show` / `mayhem config validate` | Inspect / validate the effective layered configuration (alias `cfg`). `show --json` also reports each section's provenance. |
383
+ | `mayhem campaign list` | List campaigns (draft/scheduled/running/…; `--json`). |
384
+ | `mayhem campaign create NAME` | Create a draft campaign (`--description`, `--hypothesis`). |
385
+ | `mayhem campaign show / status / delete ID` | Inspect, poll, or delete a campaign. |
386
+ | `mayhem campaign add-experiment ID SPEC` | Append a drill spec file to a campaign. |
387
+ | `mayhem campaign start / abort / archive ID` | Move a campaign through its lifecycle. |
388
+ | `mayhem campaign run ID` | Execute every experiment sequentially against the compose topology, honoring the campaign policy and window (`--no-gate` bypasses the impact gate). |
389
+
390
+ Every command (and the whole tree) abbreviates to any unique prefix: `mayhem
391
+ ex valid`, `mayhem t f`.
392
+
393
+ ---
394
+
395
+ ## Configuration
396
+
397
+ Runtime policies live in `mayhem.yaml` — the *configuration* file, distinct
398
+ from a `kind: drill` spec — auto-detected in the cwd or given with
399
+ `--config`. The effective view is one command away: `mayhem config show`
400
+ (alias `cfg`) prints the resolved configuration and the provenance of every
401
+ section; `mayhem config validate` refuses unknown keys, a missing or wrong
402
+ `apiVersion`, and out-of-range sections before anything runs.
403
+
404
+ ```yaml
405
+ apiVersion: mayhem/v1 # required; anything else is rejected
406
+ policy:
407
+ allow_faults: null # null = whole catalog; set to restrict
408
+ deny_faults: [] # fault ids never injectable
409
+ risk_ceiling: null # tightened by the drill ceiling at plan time
410
+ allow_critical: false # config-side half of the critical opt-in
411
+ blast_radius:
412
+ max_services_pct: 50.0
413
+ max_hosts: 2
414
+ max_concurrent_faults: 3
415
+ max_duration_per_fault_s: 300.0
416
+ forbidden_fault_pairs: [] # e.g. ["net.packet_loss", "net.bandwidth"]
417
+ storage:
418
+ path: mayhem.db # SQLite database (same default as --db)
419
+ artifacts_dir: .mayhem/artifacts
420
+ toolkit:
421
+ binaries: {} # pin a named tool's binary, keyed by fault backend
422
+ runtime: docker # docker | podman (CLI: --podman)
423
+ target:
424
+ containers: [] # explicit targets when no compose file is used
425
+ log_level: INFO # DEBUG | INFO | WARNING | ERROR
426
+ maniac: # fallback for `mayhem maniac` when the spec omits config.maniac
427
+ level: 2
428
+ run_level: 10
429
+ seed: null # null = fresh random seed each run
430
+ ```
431
+
432
+ Layering, in increasing precedence: **built-in defaults → `mayhem.yaml` →
433
+ `mayhem.{profile}.yaml` → environment variables → CLI flags**. Profile
434
+ overlays are separate per-profile files (selected with `--profile NAME`) —
435
+ there is no `profiles:` key inside `mayhem.yaml`. The environment layer only
436
+ honours allowlisted variables: `MAYHEM_STORAGE_PATH`,
437
+ `MAYHEM_ARTIFACTS_DIR`, `MAYHEM_LOG_LEVEL`.
438
+
439
+ ```bash
440
+ mayhem config show # YAML + sourced-from comments
441
+ mayhem config show --json # {"config": …, "sources": …}
442
+ mayhem config validate # exit 0 / 3 on invalid layers
443
+ ```
444
+
445
+ Drill-level `config.risk_ceiling` composes with the policy ceiling and can
446
+ only tighten it.
447
+
448
+ ---
449
+
450
+ ## Safety Model
451
+
452
+ - **Risk ceilings.** Every catalog fault carries a risk level; injection is
453
+ refused when either the policy or the drill ceiling is exceeded.
454
+ `--allow-critical` is the operator-side acknowledgment.
455
+ - **Concurrency budget.** `max_faults` caps simultaneously-injected faults;
456
+ a wider `parallel:` step queues into rounds.
457
+ - **Duration caps.** Per-fault `duration` beyond the catalog maximum is a
458
+ compile error.
459
+ - **Capability gating.** Faults declare the capabilities they need
460
+ (docker engine, net_admin, process control, …); the plan is proven against
461
+ the live graph by the impact gate before run — never assumed.
462
+ - **Compensation contracts.** Reversible faults run their declared inverse;
463
+ irreversible ones are followed by workload reconciliation. A failed round
464
+ aborts-and-recovers its own faults first, then propagates.
465
+ - **Lease hygiene.** Fault rounds hold leases with a TTL; `mayhem janitor`
466
+ sweeps orphaned leases and expires pending runs, `mayhem recover` repairs a
467
+ run's orphans on demand.
468
+
469
+ ---
470
+
471
+ ## Exit Codes
472
+
473
+ Stable contract for scripts and CI (definition:
474
+ [`src/mayhem/cli/exit_codes.py`](src/mayhem/cli/exit_codes.py)):
475
+
476
+ | Code | Meaning |
477
+ |------|---------|
478
+ | 0 | Success |
479
+ | 1 | General failure |
480
+ | 2 | Usage error (bad flags/arguments) |
481
+ | 3 | Configuration layering/validation failed |
482
+ | 4 | Spec/target validation failed |
483
+ | 5 | A safety gate refused the operation |
484
+ | 6 | Experiment ran and did not complete |
485
+ | 7 | Recovery/janitor left dirty state behind |
486
+ | 8 | Agent transport/runtime failure |
487
+ | 9 | External tool invocation failed structurally |
488
+ | 10 | Command prefix matched multiple commands |
489
+
490
+ ---
491
+
492
+ ## Architecture
493
+
494
+ The pipeline is staged so everything expensive is done up front and execution
495
+ is as small as possible:
496
+
497
+ 1. **Discover** — the topology provider builds a graph (services, hosts,
498
+ dependency edges) from the compose blueprint and live containers.
499
+ 2. **Prepare** — `mayhem config` layering (defaults → `mayhem.yaml` → profile →
500
+ env → flags) plus topology, drift detection, and target revalidation.
501
+ 3. **Compile & plan** — the drill spec becomes a frozen `ExecutionPlan` with
502
+ step sequences, per-fault compensations, success criteria, and observability
503
+ sources; every fault, target kind, capability, and duration is validated
504
+ against the catalog.
505
+ 4. **Execute** — the engine runs rounds (inject → observe → compensate) through
506
+ the runtime adapter, evaluates criteria, collects observability, and writes
507
+ step/event/lease rows with the governing-decision trace.
508
+ 5. **Recover & report** — the janitor sweeps orphaned leases; `status`,
509
+ `history`, and run summaries replay the evidence.
510
+
511
+ ### Documentation
512
+
513
+ | Document | Contents |
514
+ |----------|----------|
515
+ | [README.md](README.md) | This file. |
516
+ | [docs/drill-spec.md](docs/drill-spec.md) | **The drill DSL reference** — config, containers, execution, checks, success criteria, observability, and the full fault catalog. |
517
+ | [docs/compensation.md](docs/compensation.md) | **Fault compensation lifecycle** — inject / undo / verify contracts, executor routing, marker conventions, and the per-fault template table. |
518
+
519
+ ### Status
520
+
521
+ | Area | Status |
522
+ |------|--------|
523
+ | Domain models, configuration system | Complete |
524
+ | Topology discovery (Docker/Podman) + compose project filtering + drift detection | Complete |
525
+ | Fault catalog + registry + capability probing (`toolkit`) | Complete |
526
+ | Drill spec DSL (config / containers / execution / checks) | Complete |
527
+ | Deterministic + random planners, frozen plans | Complete |
528
+ | Success criteria + machine verdict | Complete |
529
+ | Declarative observability sources | Complete |
530
+ | Run engine with compensation + leases + janitor + recover | Complete |
531
+ | Safety gates + impact gate | Complete |
532
+ | CLI with prefix abbreviation, stable exit codes | Complete |
533
+ | SQLite persistence + restart, migrations (schema freeze) | Complete |
534
+ | Campaigns (multi-spec runs) | Complete |
535
+ | Tests (859 collected: 752 unit + 95 e2e + 12 integration), ruff, mypy (per-file strict) | Complete |
536
+ | Kubernetes execution | Planned (interface-only; see [`examples/k8s`](examples/k8s)) |
537
+ | Web UI / REST API | Planned |
538
+
539
+ ---
540
+
541
+ ## Development
542
+
543
+ ```bash
544
+ # Clone, then sync the dev dependency group (requires https://docs.astral.sh/uv/):
545
+ uv sync --group dev
546
+
547
+ # Run all tests
548
+ uv run pytest
549
+
550
+ # Lint
551
+ uv run ruff check src/
552
+
553
+ # Type check (strict is configured per module)
554
+ uv run mypy
555
+ ```
@@ -0,0 +1,107 @@
1
+ mayhem/config.py,sha256=dJhQ4oLFL57imYs3aU2QUhcL1qhZv_gH7oFsC6yuslQ,7715
2
+ mayhem/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ mayhem/spec.py,sha256=QxhE90OzkUBJX03WGFkdBRGmN7PkO2_FzPQJb2NNLnU,1876
4
+ mayhem/agent/__init__.py,sha256=GFXICPnXKO0n3rUtwCHDLdBa_wLlcVEBs_Aczu8jS-w,65
5
+ mayhem/agent/cli.py,sha256=LIA6jGn-PkR3vMNZGUJXiegwYp0gHWFmKsPqJQUxLRk,1040
6
+ mayhem/agents/__init__.py,sha256=HLHzcJE2hy4NSKvS9GL_mv5LPS-b16_RzYS2hBdf8Ro,71
7
+ mayhem/agents/capabilities.py,sha256=bS2kF6xJSs-8EAYPuX4ctxQZS6yJteCx2YwPx8YdYG0,4045
8
+ mayhem/agents/executors.py,sha256=I6A7j5xm00Fxnx8qc0uQ1rsnsTU9cL2lxnAJpeVsAq8,17745
9
+ mayhem/agents/impact.py,sha256=VswHeBaUDtgaap0M1EVjWXleNXQPjemRme_cFM5JDpE,29834
10
+ mayhem/agents/lease_client.py,sha256=G0x7PXHAaO9S1VJ8Hs_VQvVs0TywXq_Thlx_C7H4uIA,5289
11
+ mayhem/agents/probes.py,sha256=RtLYSbcpqzNBiQQhKjM1bERZbPohvcHyFg9FIJ0Kt_E,11377
12
+ mayhem/agents/protocol.py,sha256=0m-yevK1ymNlFfm16o3N8L2sXIA9Jqdksop0UJXtSCA,4212
13
+ mayhem/agents/server.py,sha256=uW1Lmh7XWSItYYB3wiaJ2cyJaiW3WH-95GnJIJsigSQ,10530
14
+ mayhem/agents/sinks.py,sha256=MmueFAXYr-h1xJ_D7CxC3atejw8_uMVnxck2FNxMi-0,1907
15
+ mayhem/agents/transports.py,sha256=nPqvmhQd4MMAKLyvGTIoTosJp4-2bE7pWHitcjqJ6AQ,4863
16
+ mayhem/agents/watchdog.py,sha256=15TawWyU5K7F71bI026LsPXXVJpY85fqI5dLvaMWxZM,4888
17
+ mayhem/cli/__init__.py,sha256=8YnPhmrUa_FXrXGf_59KzLlKEu68o0AU3kD2Ogmnwus,315
18
+ mayhem/cli/app.py,sha256=0K-pb2P1Vo5lPIBBYi0dSmRRgha1RN_U2am3_ZsJPNU,5086
19
+ mayhem/cli/campaign.py,sha256=40AgPX1Oab9TllVSxZGd2Fje3Jobz1woOUSp3fIZl9o,18951
20
+ mayhem/cli/config_cmd.py,sha256=slnw6gmB2wFt7tkeXNrTkqjfNGNrjG4jfjvrGRW3bOc,1567
21
+ mayhem/cli/context.py,sha256=-rXsb6UxEH6K4GrF2InbmK6NvpbmzrTEo3e1QK2-Nxs,704
22
+ mayhem/cli/dependency.py,sha256=U0eCPy6_M0xbzvIFhW7NgjE8iokLssCtUqcR8p1WFqc,16917
23
+ mayhem/cli/exit_codes.py,sha256=954KstzNhKGNFPq9i3OOSra5v99poHZRRYzzQhj7RaM,1014
24
+ mayhem/cli/experiment.py,sha256=sOEuQZt3NZTFxEdhBPyFvS_VJ9fJ0Rt-hl718XftnnM,730
25
+ mayhem/cli/lifecycle.py,sha256=bTmd66FGjhAIjl7100x00b4i8TjNqc_Ius_AxBu3pgM,30767
26
+ mayhem/cli/resolver.py,sha256=pJLhbiLKuAZfk5yoJMuMZjCuWVf0XwWbbcBa4POB2Rw,2783
27
+ mayhem/cli/services.py,sha256=FqHPbraiJDVbM34bL4Luw_eypGiDNRQ0Y4tW0uBUkK0,15418
28
+ mayhem/cli/style.py,sha256=Jb5DDvbVCQrKX-Mk61RTmWdJJOSd1x0EUPieZrM4CbA,3227
29
+ mayhem/cli/toolkit.py,sha256=2DGjNfQyTfB-K4n_zpjEphInFZoBHpCh4UkOF65De7o,1649
30
+ mayhem/cli/topology.py,sha256=xUXn27wR_7-2igPyT0Ksw4jymFed-djVMkuEljM9K2o,4217
31
+ mayhem/controller/__init__.py,sha256=JV0geUIkFwcZZRgUnVi-1bd5RlBKq4yyo-aTwF8IxpM,74
32
+ mayhem/controller/compensation.py,sha256=ZSBeLMGZbPZmEgj8jneedGCk8q1cxYsuhQokBxJPBu0,77435
33
+ mayhem/controller/executor.py,sha256=3-eorfOLlVsnLswvpFIVD04nlBd9n-i3nKFKwARsqmU,73628
34
+ mayhem/controller/janitor.py,sha256=rmOfOjhp6Ut_9txhS1LjLBGOLxEA8zk9UEBALZYciX4,7590
35
+ mayhem/controller/observability_collector.py,sha256=W83v4JMyKLXLn01pHhVGGXkeZUUZdXKEkgAh8_yZibw,13331
36
+ mayhem/controller/observations.py,sha256=lIUkUnnmxhS370433rOYDHhQpqLkTfHyqd-a4yKUa-U,3543
37
+ mayhem/controller/planner.py,sha256=J30hm-kynaqDJeAy2PfKuFhDyP2GNqUp0BueO5DgeR0,28776
38
+ mayhem/controller/recovery.py,sha256=pSZq2gLsUXqJT7XzXWqv7p7PLgMKKvM289VxAIVYuiw,9313
39
+ mayhem/controller/resilience_report.py,sha256=9I3UTHY_N637bekMowQBqM2kKl2BnDV57Yx09YTSZTs,21702
40
+ mayhem/controller/resource_manager.py,sha256=deyOrA82079-8h6HMQn-QyA6lk1t9xrYXAHIhaxKP64,18590
41
+ mayhem/controller/safety.py,sha256=4DtQfKHthh_fpbQNoEVLE_iorRge8y4UM-SPJNcHhdk,14644
42
+ mayhem/domain/__init__.py,sha256=RuJsWAUWymDh8uoK0cjR4FlDRsV1ERphhdM1z6Aok9Y,254
43
+ mayhem/domain/campaigns.py,sha256=DhcIW81sIDk11Z4G8a5uw5ZMs96XKaLWW5cSSa3Yzz0,3773
44
+ mayhem/domain/cancellation.py,sha256=HHou6pHbPZHo1x7adgWwTpQahPLc-Glv7xRjrlpzDBI,3524
45
+ mayhem/domain/candidates.py,sha256=7g7t5s0O0vsN7aqFVQMeg_eTHN4WHxsp4TnMj0kuw_8,3172
46
+ mayhem/domain/capabilities.py,sha256=gqVNUO0PTPy2theCuigPHPI29fYU2oTjDHrIIdEGYko,2723
47
+ mayhem/domain/catalog.py,sha256=YqRErJLqCXmEUx4m9A8iJFsVHapi9zaOp_lKPC2BMK0,27770
48
+ mayhem/domain/checks.py,sha256=gcU9gBfPx3rhibp112XE1W3N_8BgJvKJ9DYeKkBrA3w,4810
49
+ mayhem/domain/common.py,sha256=j4G1OzKU4Mr4pfSU4GKaUgL2sON6PMH9VUFaoZEDAa0,3601
50
+ mayhem/domain/coverage.py,sha256=cEJKCBVBDK5aavTyh9ealnGZ1KFfK6NT-vjR8n6ONtQ,3355
51
+ mayhem/domain/decisions.py,sha256=IRLrrNqwVP_LucxzQYMGHnc54GkbEn8phTGFikHw3Lw,1754
52
+ mayhem/domain/errors.py,sha256=dCaLQ5Hzc2qzjbk1n5leM8eCNAt5_nWh1k-CJk84mbU,2779
53
+ mayhem/domain/events.py,sha256=xBGMBXfC4eFKvKBCkqY7OGwAE_R2xfNx4JkgwR5q7To,2039
54
+ mayhem/domain/execution_context.py,sha256=GvLTYRzzaTCgia0MJc1nUmuhvdSjv1GsJ2xK7G8YeTE,4576
55
+ mayhem/domain/execution_loci.py,sha256=zQWLUP_MRd8ik736Zk3MzFWqOhlrS43mJ5YjSRXichw,3360
56
+ mayhem/domain/experiments.py,sha256=4sVfvcIk5deUdvIorbHopfi_7luKzMc9pEF5GGqeo44,13914
57
+ mayhem/domain/faults.py,sha256=Ls-w8K1Gm1EM6f1dHfPXS3PURVWdfRldCrRuFZRrDmw,8308
58
+ mayhem/domain/identity.py,sha256=G4hP-wBsi2JIzBg-qiNJe00ITB-_9_sdR48Xrv0yBXM,7540
59
+ mayhem/domain/k8s_adapter.py,sha256=5JfhR1l8LHcHJ1JR1aGIfaAncnFS8qUs398nyh1p-D0,4919
60
+ mayhem/domain/leases.py,sha256=uMqM9BowwrTKA8bn1T2Z-mw550jWubLiW-uFTeWfDN0,7371
61
+ mayhem/domain/load_strategy.py,sha256=HyEBm4kPTOevS9zULeQG3_bGm04IrMzW_Bnc90o0XJ8,3461
62
+ mayhem/domain/m5_campaign.py,sha256=7i3fsT1fNxh4EzVswqv1ssrBA6C_lt0Ncz8adaaKrTI,3740
63
+ mayhem/domain/maniac.py,sha256=JU61KL92gcx9ZMNnlqEpptg6uO_3QvwGGWJhYTaL5o8,3548
64
+ mayhem/domain/observability.py,sha256=8IboJIIuifCWrzyGkGYjL7_nOkAxntaZBtUmn9M1iAM,5281
65
+ mayhem/domain/outcomes.py,sha256=K5m-NAv59Cs6We2uFXUbwvL6GYkD3eoesxu4BD9pFHo,3269
66
+ mayhem/domain/remote_agent_interface.py,sha256=xAJbeTJWhjg2azDLwXEDDtsgsR-s1ftugSZUKxwFQoQ,2216
67
+ mayhem/domain/resources.py,sha256=Nem2hibXe3-nfUWGsIkdD34fM7Urleg7tSpIyvJ497w,9035
68
+ mayhem/domain/risks.py,sha256=FgkVP-tFjz2bkAECwY57PuiSXJV-CwhyGM3AQKvXGhY,1585
69
+ mayhem/domain/run_outcome.py,sha256=ZHZCsTXvmFvkkAQxN2WMTM5N1lIVOEtKW89O1Axwv8E,4521
70
+ mayhem/domain/runtime_adapter.py,sha256=1-TMz9nkR0RvM7Sk67nUc37mavuEJZmeQw7skfNgnRw,9003
71
+ mayhem/domain/success.py,sha256=dlStGb1csg_QvlzvsGkyyeiOx8YEHLdrh04MCL_Q1xA,11614
72
+ mayhem/domain/topology.py,sha256=a0F8pwi9GkE9X9h26QwxTMt-mt_NdN3MOXuLPP_O-sE,16787
73
+ mayhem/infra/__init__.py,sha256=Jx4vcGjUyoY73TtMU3rHb10hO7ZS9zPK5pB7dnP3CqA,84
74
+ mayhem/infra/campaign_engine.py,sha256=7TgXYxuB2-dJxEAqwE90qqL1mbS1XgKRQ8r-Wk4MrOY,8215
75
+ mayhem/infra/candidate_gates.py,sha256=7XcwhtNXP_t-I1qf89nxfrOXEqI47v9yq83mCrmEktE,4489
76
+ mayhem/infra/candidate_generator.py,sha256=yT_7jTRgqD2ZB40qMYqPmQrJwq9TGWqCSflKbnz1NAY,3914
77
+ mayhem/infra/coverage_repository.py,sha256=ONOqLcTh28ZRJXOUuwiCOLYET1u3kOKOCyoa1DpUD5Y,3582
78
+ mayhem/infra/lease_repository.py,sha256=cot8vrNQEWM0SheaMmlr_dIyjt_UK_XIzK24JgesKvw,5147
79
+ mayhem/infra/maniac.py,sha256=RSt8H60_Vs6SEHj6OBsj_rEuOFsNK2uabdDbIXmwJns,3552
80
+ mayhem/infra/migrations.py,sha256=kvLSq9zapCYCXI-QYr_xfN51f-e-sY757l_Zs5S6NwI,22669
81
+ mayhem/infra/migrator.py,sha256=F5A-PPXHqEObETLqsFXpKxk2mkU-kOOLBLVFZlsK6yY,5884
82
+ mayhem/infra/report.py,sha256=AlWvAsLmIFd8PASC3UBfa9NMkYwCuLDGrlW73uKC1xw,7709
83
+ mayhem/infra/store.py,sha256=5zhb2s2KVAb2Ki6NIHZbBc5HYfqoErpML_g5rN1u9ao,7389
84
+ mayhem/toolkit/__init__.py,sha256=FWrlzKn-WTsGvnWGV4dygf0HW2CVQXxumNSYQ5WsgWg,83
85
+ mayhem/toolkit/fingerprint.py,sha256=4bK80htc7bOg5MNh7FscWyyDmcJqWpEEySVXNTGuJvk,2411
86
+ mayhem/toolkit/hashing.py,sha256=Of2_uekYbs5D14YgScBoUv5ObCEA9h0Dw_srB_k_COI,811
87
+ mayhem/toolkit/registry.py,sha256=5hcAldr058fTRpB_LGp5DOqc4s7XyOuOTwmIMeKcBbw,6830
88
+ mayhem/toolkit/tool_runner.py,sha256=ZN3pFsbEs_Hac9L97ngq6tZeJu52r0IEpEia3QwR5Us,4108
89
+ mayhem/toolkit/manifests/docker.yaml,sha256=ESmpaHX0ZEw59uCd8AQp8wTujippzttYrMPO5obnAeo,293
90
+ mayhem/toolkit/manifests/podman.yaml,sha256=dRiqQkahxso7cOKfIqRskec1c6R58rcCWhQqw-OHwcQ,281
91
+ mayhem/toolkit/manifests/stress-ng.yaml,sha256=kkWbIJerjV-KGMdQ6QEcoy8CGDYePKedfMAdc4wwuhY,261
92
+ mayhem/toolkit/manifests/tc-netem.yaml,sha256=MZte3N9F3NGQQKnCl2IEga6oa4o_NCBpe9vqz9Z1cQc,258
93
+ mayhem/toolkit/manifests/toxiproxy.yaml,sha256=FeKYlD2uSOSNzlR_XpLSftavA2wxUE9tqP6Q0e5sTqM,233
94
+ mayhem/topology/__init__.py,sha256=Z8CgpWmlNmI6RgJC_FfuB4Kg8dvUszliBB8kXtTO-T0,355
95
+ mayhem/topology/resolve.py,sha256=u_cuhamktbn-Iao5sVv1VeGxESaRbvDL57CVRtEbw0w,7283
96
+ mayhem/topology/service.py,sha256=upu-9J23z_lWueJkIWf_M6dwO0Opt6R5upBjuNe5EHw,6130
97
+ mayhem/topology/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
+ mayhem/topology/providers/adapter_registry.py,sha256=gUxoiVNXAehhyGERzUidf474i-XFRKEsDyvuhUXoBJ0,2146
99
+ mayhem/topology/providers/base.py,sha256=yh1sJHX8NkHZbBls59tntEXePEEMfTyQ5qJ_0_7Emko,931
100
+ mayhem/topology/providers/compose.py,sha256=yf-SnOW1jyYycsxu2w2M57Dd7Uq3HluBjz23-tQKsBs,7341
101
+ mayhem/topology/providers/docker_adapter.py,sha256=A829Uj8wKj4mVHwpyKiVjToPs2v02P7pwO4a3dDhjiw,9473
102
+ mayhem/topology/providers/docker_runtime.py,sha256=g15opOT_QIchKl1_4tp6tp92noXfq3OXJaDo9aaDvRs,17183
103
+ mayhem/topology/providers/podman_adapter.py,sha256=5yqaDaMH2ElJSgoFeIDF60ScAtgAW8ohLCk-m0ornIU,11525
104
+ mayhem_cli-0.5.1.dist-info/METADATA,sha256=35wD1r-CzSLYES5N_iXRznzXyTXTIo-Isdsh0C9G8nU,22966
105
+ mayhem_cli-0.5.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
106
+ mayhem_cli-0.5.1.dist-info/entry_points.txt,sha256=pCxl4SlE4VmN5Hl34n9Yyb5EFFGKGzWnSAnQb-KI9Bw,83
107
+ mayhem_cli-0.5.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ mayhem = mayhem.cli.app:main
3
+ mayhem-agent = mayhem.agent.cli:app