obsidianwall-verdict 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. obsidianwall_verdict-0.2.0/PKG-INFO +503 -0
  2. obsidianwall_verdict-0.2.0/README.md +470 -0
  3. obsidianwall_verdict-0.2.0/audit/__init__.py +0 -0
  4. obsidianwall_verdict-0.2.0/audit/audit_logger.py +41 -0
  5. obsidianwall_verdict-0.2.0/cli/__init__.py +0 -0
  6. obsidianwall_verdict-0.2.0/cli/main.py +268 -0
  7. obsidianwall_verdict-0.2.0/context/__init__.py +0 -0
  8. obsidianwall_verdict-0.2.0/context/context_builder.py +46 -0
  9. obsidianwall_verdict-0.2.0/context/terraform_parser.py +104 -0
  10. obsidianwall_verdict-0.2.0/engine/__init__.py +0 -0
  11. obsidianwall_verdict-0.2.0/engine/analyzers/__init__.py +17 -0
  12. obsidianwall_verdict-0.2.0/engine/analyzers/architecture_analyzer.py +291 -0
  13. obsidianwall_verdict-0.2.0/engine/analyzers/cost_analyzer.py +235 -0
  14. obsidianwall_verdict-0.2.0/engine/analyzers/topology_analyzer.py +261 -0
  15. obsidianwall_verdict-0.2.0/engine/analyzers/utilization_analyzer.py +306 -0
  16. obsidianwall_verdict-0.2.0/engine/condition_evaluator.py +195 -0
  17. obsidianwall_verdict-0.2.0/engine/cost_estimator.py +376 -0
  18. obsidianwall_verdict-0.2.0/engine/decision_resolver.py +210 -0
  19. obsidianwall_verdict-0.2.0/engine/evaluator.py +105 -0
  20. obsidianwall_verdict-0.2.0/engine/explainability/__init__.py +22 -0
  21. obsidianwall_verdict-0.2.0/engine/explainability/explanation_builder.py +292 -0
  22. obsidianwall_verdict-0.2.0/engine/explainability/governance_reasoning_chain.py +834 -0
  23. obsidianwall_verdict-0.2.0/engine/explainability/policy_reasoning.py +302 -0
  24. obsidianwall_verdict-0.2.0/engine/explainability/recommendation_explainer.py +361 -0
  25. obsidianwall_verdict-0.2.0/engine/explainability/trace_graph.py +371 -0
  26. obsidianwall_verdict-0.2.0/engine/lint_validator.py +47 -0
  27. obsidianwall_verdict-0.2.0/engine/optimization_catalog.py +213 -0
  28. obsidianwall_verdict-0.2.0/engine/orchestrator.py +343 -0
  29. obsidianwall_verdict-0.2.0/engine/policy_loader.py +13 -0
  30. obsidianwall_verdict-0.2.0/engine/policy_normalizer.py +220 -0
  31. obsidianwall_verdict-0.2.0/engine/recommender.py +438 -0
  32. obsidianwall_verdict-0.2.0/engine/replay/__init__.py +9 -0
  33. obsidianwall_verdict-0.2.0/engine/replay/replay_engine.py +266 -0
  34. obsidianwall_verdict-0.2.0/engine/replay/replay_schema.py +183 -0
  35. obsidianwall_verdict-0.2.0/engine/replay/simulation_engine.py +336 -0
  36. obsidianwall_verdict-0.2.0/engine/risk_scorer.py +322 -0
  37. obsidianwall_verdict-0.2.0/engine/validator.py +90 -0
  38. obsidianwall_verdict-0.2.0/engine/workflows/__init__.py +10 -0
  39. obsidianwall_verdict-0.2.0/engine/workflows/approval_resolver.py +421 -0
  40. obsidianwall_verdict-0.2.0/engine/workflows/notification_router.py +619 -0
  41. obsidianwall_verdict-0.2.0/obsidianwall_verdict.egg-info/PKG-INFO +503 -0
  42. obsidianwall_verdict-0.2.0/obsidianwall_verdict.egg-info/SOURCES.txt +52 -0
  43. obsidianwall_verdict-0.2.0/obsidianwall_verdict.egg-info/dependency_links.txt +1 -0
  44. obsidianwall_verdict-0.2.0/obsidianwall_verdict.egg-info/entry_points.txt +2 -0
  45. obsidianwall_verdict-0.2.0/obsidianwall_verdict.egg-info/requires.txt +12 -0
  46. obsidianwall_verdict-0.2.0/obsidianwall_verdict.egg-info/top_level.txt +6 -0
  47. obsidianwall_verdict-0.2.0/pyproject.toml +101 -0
  48. obsidianwall_verdict-0.2.0/schemas/__init__.py +0 -0
  49. obsidianwall_verdict-0.2.0/schemas/policy_schema.py +223 -0
  50. obsidianwall_verdict-0.2.0/scripts/__init__.py +0 -0
  51. obsidianwall_verdict-0.2.0/scripts/parse_verdict_output.py +131 -0
  52. obsidianwall_verdict-0.2.0/setup.cfg +4 -0
  53. obsidianwall_verdict-0.2.0/tests/test_engine.py +58 -0
  54. obsidianwall_verdict-0.2.0/tests/test_policy.py +93 -0
@@ -0,0 +1,503 @@
1
+ Metadata-Version: 2.4
2
+ Name: obsidianwall-verdict
3
+ Version: 0.2.0
4
+ Summary: A deterministic pre-deployment infrastructure governance engine
5
+ Author-email: ObsidianWall <hello@obsidianwall.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://obsidianwall.com
8
+ Project-URL: Documentation, https://obsidianwall.dev
9
+ Project-URL: Repository, https://github.com/obsidianwall/obsidianwall-verdict
10
+ Project-URL: Bug Tracker, https://github.com/obsidianwall/obsidianwall-verdict/issues
11
+ Keywords: governance,infrastructure,terraform,policy,devops,devsecops,programmable-assurance,pre-deployment
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: Topic :: Software Development :: Build Tools
16
+ Classifier: Topic :: System :: Systems Administration
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Operating System :: OS Independent
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: pyyaml>=6.0
24
+ Requires-Dist: click>=8.0
25
+ Requires-Dist: typer>=0.9
26
+ Requires-Dist: python-json-logger>=2.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=7.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
30
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
31
+ Requires-Dist: bandit>=1.7; extra == "dev"
32
+ Requires-Dist: build>=1.0; extra == "dev"
33
+
34
+
35
+ # ObsidianWall Verdict
36
+
37
+ **Pre-deployment infrastructure governance.**
38
+ Evaluate Terraform plans against governance policies before deployment executes —
39
+ catching budget overruns, policy violations, and compliance failures
40
+ before they become incidents.
41
+
42
+ [![CI](https://github.com/obsidianwall/obsidianwall-verdict/actions/workflows/verdict-ci.yml/badge.svg)](https://github.com/obsidianwall/obsidianwall-verdict/actions/workflows/verdict-ci.yml)
43
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13+-blue.svg)](https://www.python.org/downloads/)
44
+ [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
45
+ [![obsidianwall.com](https://img.shields.io/badge/platform-obsidianwall.com-5ac4f0.svg)](https://obsidianwall.com)
46
+
47
+ ---
48
+
49
+ ## What it does
50
+
51
+ Verdict runs as a CLI command or a GitHub Actions step. It takes a Terraform
52
+ plan and a policy file, evaluates the plan deterministically against the policy
53
+ conditions, and produces a governance decision with a full audit trail.
54
+
55
+ ```
56
+ $ verdict evaluate \
57
+ --plan terraform_plan.json \
58
+ --policy policies/cost/basic_budget.yaml \
59
+ --role engineer
60
+
61
+ policy basic_budget_verdict
62
+ condition budget_check ✗ FAILED
63
+ expression (current_spend + estimated_cost) <= budget.amount
64
+ evaluated (0 + 100) <= 50 → false
65
+
66
+ risk score 75 / 100 (critical)
67
+ findings cost_analysis: 2 topology: 1
68
+ notified budget_owner (email) engineering_lead (slack)
69
+
70
+ decision DENY_WITH_OVERRIDE
71
+ override budget_owner may authorize
72
+ decision_id abc3a13b-83d5-4fad-87d8
73
+
74
+ ✗ Deployment blocked by governance policy.
75
+ ```
76
+
77
+ No AI guessing. No approximations. Every decision is deterministic,
78
+ reproducible, and attributable to a human-authored policy.
79
+
80
+ ---
81
+
82
+ ## Quickstart
83
+
84
+ **Install**
85
+
86
+ ```bash
87
+ pip install obsidianwall-verdict
88
+ ```
89
+
90
+ **Write a policy**
91
+
92
+ ```yaml
93
+ # policies/cost/budget.yaml
94
+ apiVersion: obsidianwall.io/v1
95
+ kind: Policy
96
+
97
+ metadata:
98
+ name: team_budget
99
+ version: "0.1"
100
+ owner: your-team
101
+
102
+ spec:
103
+ inputs:
104
+ - estimated_cost
105
+ - current_spend
106
+
107
+ parameters:
108
+ budget:
109
+ amount: 5000
110
+ period: monthly
111
+ flexibility: soft
112
+
113
+ conditions:
114
+ - id: budget_check
115
+ expression: "(current_spend + estimated_cost) <= budget.amount"
116
+ description: "Monthly spend must not exceed budget"
117
+
118
+ decision:
119
+ allow: ALLOW
120
+ deny: DENY_WITH_OVERRIDE
121
+ warn: ALLOW_WITH_NOTIFICATION
122
+
123
+ governance:
124
+ severity: medium
125
+ notifications:
126
+ - role: budget_owner
127
+ channel: email
128
+ - role: engineering_lead
129
+ channel: slack
130
+
131
+ override:
132
+ roles:
133
+ - budget_owner
134
+ requires_approval: false
135
+ ```
136
+
137
+ **Evaluate a plan**
138
+
139
+ ```bash
140
+ # Generate your Terraform plan
141
+ terraform plan -out=tfplan
142
+ terraform show -json tfplan > terraform_plan.json
143
+
144
+ # Run governance evaluation
145
+ verdict evaluate \
146
+ --plan terraform_plan.json \
147
+ --policy policies/cost/budget.yaml \
148
+ --role engineer
149
+ ```
150
+
151
+ Verdict returns exit code `0` on ALLOW and non-zero on DENY,
152
+ blocking CI/CD pipelines automatically.
153
+
154
+ ---
155
+
156
+ ## GitHub Actions
157
+
158
+ Add Verdict as a governance gate in your CI/CD pipeline in one step:
159
+
160
+ ```yaml
161
+ # .github/workflows/governance.yml
162
+ name: Infrastructure Governance
163
+
164
+ on:
165
+ pull_request:
166
+ paths: ["**.tf", "**.tfvars"]
167
+
168
+ jobs:
169
+ governance:
170
+ runs-on: ubuntu-latest
171
+ steps:
172
+ - uses: actions/checkout@v4
173
+
174
+ - name: Generate Terraform plan
175
+ run: |
176
+ terraform init
177
+ terraform plan -out=tfplan
178
+ terraform show -json tfplan > terraform_plan.json
179
+
180
+ - name: ObsidianWall Verdict
181
+ id: verdict
182
+ uses: obsidianwall/obsidianwall-verdict@main
183
+ with:
184
+ plan: terraform_plan.json
185
+ policy: policies/cost/budget.yaml
186
+ role: engineer
187
+ fail_on_deny: "true"
188
+
189
+ - name: Governance outcome
190
+ if: always()
191
+ run: |
192
+ echo "Decision: ${{ steps.verdict.outputs.decision }}"
193
+ echo "Risk score: ${{ steps.verdict.outputs.risk_score }}/100"
194
+ echo "Severity: ${{ steps.verdict.outputs.effective_severity }}"
195
+ ```
196
+
197
+ Verdict posts a governance summary table to the workflow step summary
198
+ on every run. Budget owners and engineering leads receive notifications
199
+ through the channels defined in the policy.
200
+
201
+ ### Action outputs
202
+
203
+ | Output | Description |
204
+ |--------|-------------|
205
+ | `decision` | `ALLOW` / `ALLOW_WITH_NOTIFICATION` / `ALLOW_WITH_APPROVAL_REQUIRED` / `DENY_WITH_OVERRIDE` / `DENY` |
206
+ | `conditions_passed` | `true` or `false` |
207
+ | `risk_score` | Integer 0–100 |
208
+ | `effective_severity` | `informational` / `low` / `medium` / `high` / `critical` |
209
+ | `decision_id` | UUID for audit trail correlation |
210
+
211
+ ---
212
+
213
+ ## How it works
214
+
215
+ Verdict runs a deterministic evaluation pipeline on every invocation:
216
+
217
+ ```
218
+ Terraform plan
219
+
220
+ Context builder Parses resources, estimates cost
221
+
222
+ Policy loader Loads and validates the policy YAML
223
+
224
+ Runtime normalizer Flattens policy parameters into evaluation context
225
+
226
+ Condition evaluator Evaluates each condition deterministically
227
+
228
+ Analyzer framework Cost, topology, architecture, utilization analysis
229
+
230
+ Risk scorer Aggregates findings into risk score (0–100)
231
+
232
+ Decision resolver 5-level governance decision with override routing
233
+
234
+ Explainability Reasoning chain, trace graph, remediation steps
235
+
236
+ Notification manifest Stakeholder routing — never dispatched automatically
237
+
238
+ Audit artifact Immutable JSON record of the complete evaluation
239
+ ```
240
+
241
+ Every stage is deterministic. Analyzers are advisory — they inform
242
+ the risk score but never override the condition evaluation.
243
+ The condition evaluation alone determines the governance decision.
244
+
245
+
246
+ ---
247
+
248
+ ## Enforcement modes
249
+
250
+ | Mode | How | What it blocks |
251
+ |------|-----|----------------|
252
+ | **CI/CD pipeline** | GitHub Actions with `fail_on_deny: true` | `terraform apply` never runs on DENY |
253
+ | **IAM access controls** | Azure Entra ID / AWS IAM restricts engineer credentials to read-only | Direct deployment from local machines impossible |
254
+ | **Standalone manual** | Run `verdict evaluate` before `terraform apply` | Governance guidance, audit trail, budget owner notification |
255
+
256
+ For hard technical enforcement, integrate Verdict into your CI/CD pipeline
257
+ and restrict cloud credentials so only the pipeline's service principal
258
+ can apply infrastructure. Engineers with read-only credentials cannot
259
+ deploy directly even if they skip Verdict locally.
260
+
261
+
262
+ ---
263
+
264
+ ## Governance decisions
265
+
266
+ Verdict produces one of five decisions on every evaluation:
267
+
268
+ | Decision | Meaning |
269
+ |----------|---------|
270
+ | `ALLOW` | All conditions passed. Deployment authorized. |
271
+ | `ALLOW_WITH_NOTIFICATION` | Conditions passed but high severity — stakeholders notified. |
272
+ | `ALLOW_WITH_APPROVAL_REQUIRED` | Conditions passed but formal approval required before deployment. |
273
+ | `DENY_WITH_OVERRIDE` | Conditions failed. An authorized role may override. |
274
+ | `DENY` | Conditions failed. No override permitted. Hard block. |
275
+
276
+ ---
277
+
278
+ ## Policy DSL
279
+
280
+ Policies are YAML files that define governance constraints for an
281
+ infrastructure change. The schema is versioned and validated on load.
282
+
283
+ ### Policy structure
284
+
285
+ ```
286
+ apiVersion: obsidianwall.io/v1 Protocol version
287
+ kind: Policy Always Policy for now
288
+
289
+ metadata:
290
+ name: string Policy identifier
291
+ version: string Semver string
292
+ owner: string Responsible team
293
+ description: string Optional description
294
+
295
+ spec:
296
+ inputs: list[string] Runtime context keys required
297
+ parameters: dict Policy parameters (flattened at runtime)
298
+ conditions: list[Condition] Evaluated deterministically
299
+ decision: Decision allow / deny / warn mappings
300
+ governance: GovernanceConfig Severity, notifications, approvals
301
+ override: Override Roles and approval requirements
302
+ actions: list[Action] notify / log actions
303
+ ```
304
+
305
+ ### Condition expressions
306
+
307
+ Expressions use a restricted grammar — no `eval()`, no dynamic code:
308
+
309
+ ```yaml
310
+ conditions:
311
+ - id: budget_check
312
+ expression: "(current_spend + estimated_cost) <= budget.amount"
313
+ description: "Monthly spend must not exceed budget"
314
+
315
+ - id: instance_size_check
316
+ expression: "estimated_cost <= max_instance_cost"
317
+ description: "No single instance may exceed cost threshold"
318
+ ```
319
+
320
+ Supported operators: `<=`, `>=`, `<`, `>`, `==`
321
+ Supported arithmetic: `+`
322
+ Context resolution: dot-notation for nested parameters (`budget.amount`)
323
+
324
+ ---
325
+
326
+ ## Audit artifact
327
+
328
+ Every evaluation produces a complete audit artifact:
329
+
330
+ ```json
331
+ {
332
+ "decision_id": "abc3a13b-83d5-4fad-87d8-bbe77e4b8075",
333
+ "timestamp": "2026-05-20T04:05:28Z",
334
+ "policy": "basic_budget_verdict",
335
+ "decision": "DENY_WITH_OVERRIDE",
336
+ "override_possible": true,
337
+ "override_required": false,
338
+ "conditions_passed": false,
339
+ "effective_severity": "critical",
340
+ "risk_summary": {
341
+ "overall_risk_score": 75,
342
+ "risk_severity": "critical",
343
+ "highest_risk_analyzer": "cost_analysis",
344
+ "total_findings": 3
345
+ },
346
+ "trace": [...],
347
+ "explanation": {
348
+ "governance_reasoning": {...},
349
+ "policy_reasoning": {...},
350
+ "trace_graph": {...}
351
+ },
352
+ "notification_manifest": {...}
353
+ }
354
+ ```
355
+
356
+ The artifact is written to `output/result.json` and printed to stdout.
357
+ It is suitable for storage in an audit log, S3 bucket, or compliance system.
358
+
359
+ ---
360
+
361
+ ## Doctrine
362
+
363
+ ```
364
+ AI may advise.
365
+ AI may explain.
366
+ AI may optimize.
367
+ AI may correlate.
368
+ AI may recommend.
369
+
370
+ AI may NOT authoritatively govern.
371
+ ```
372
+
373
+ Every governance decision in ObsidianWall Verdict is produced by
374
+ deterministic evaluation of human-authored policy conditions —
375
+ never by a probabilistic model.
376
+
377
+ Decisions are reproducible, explainable, and attributable to a named
378
+ policy and a named human who wrote it.
379
+
380
+ This is not an anti-AI position. The analyzer framework, recommendation
381
+ engine, and explainability pipeline all use intelligence to inform the
382
+ governance process. The boundary is authority: intelligence informs,
383
+ policy governs.
384
+
385
+ ---
386
+
387
+ ## Architecture
388
+
389
+ Verdict is the first executable of the ObsidianWall programmable
390
+ assurance platform.
391
+
392
+ ```
393
+ ┌─────────────────────────────────────────────────────┐
394
+ │ Open Governance Core (this repo — open source) │
395
+ │ │
396
+ │ engine/ deterministic evaluation pipeline │
397
+ │ schemas/ policy DSL and typed contracts │
398
+ │ context/ Terraform plan parsing │
399
+ │ audit/ structured audit logging │
400
+ │ cli/ command-line interface │
401
+ └─────────────────────────────────────────────────────┘
402
+ ↓ telemetry (opt-in, anonymous)
403
+ ┌─────────────────────────────────────────────────────┐
404
+ │ Intelligence Layer (future — private) │
405
+ │ │
406
+ │ Derived optimization intelligence │
407
+ │ Workload pattern recognition │
408
+ │ Pricing behavior intelligence │
409
+ │ Predictive governance scoring │
410
+ └─────────────────────────────────────────────────────┘
411
+ ↓ enterprise workflows
412
+ ┌─────────────────────────────────────────────────────┐
413
+ │ Platform Layer (future — paid) │
414
+ │ │
415
+ │ Hosted policy management │
416
+ │ Approval workflow persistence │
417
+ │ Governance dashboards │
418
+ │ RBAC, SSO, multi-tenant │
419
+ │ Compliance exports │
420
+ └─────────────────────────────────────────────────────┘
421
+ ```
422
+
423
+ ---
424
+
425
+ ## Development
426
+
427
+ **Requirements:** Python 3.13+, Git
428
+
429
+ ```bash
430
+ git clone https://github.com/obsidianwall/obsidianwall-verdict
431
+ cd obsidianwall-verdict
432
+
433
+ pip install -r requirements.txt
434
+
435
+ # Run the full test suite
436
+ pytest tests/ -v
437
+
438
+ # Run a sample evaluation
439
+ verdict evaluate \
440
+ --plan samples/terraform_plan.json \
441
+ --policy policies/cost/basic_budget.yaml \
442
+ --role engineer
443
+ ```
444
+
445
+ **Test suite:** 101 tests — unit, integration, and pipeline.
446
+
447
+ ```bash
448
+ pytest tests/unit/ # 82 tests
449
+ pytest tests/integration/ # 11 tests
450
+ pytest tests/ # 101 tests
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Policy examples
456
+
457
+ Sample policies are in `policies/cost/`:
458
+
459
+ | Policy | Enforcement | Use case |
460
+ |--------|------------|---------|
461
+ | `basic_budget.yaml` | Soft — override available | Engineering teams with budget owner oversight |
462
+ | `strict_budget.yaml` | Hard — dual approval required | Production environments, finance-controlled budgets |
463
+
464
+ ---
465
+
466
+ ## Telemetry
467
+
468
+ Verdict collects anonymous usage telemetry to improve the optimization
469
+ catalog and governance intelligence. Telemetry is **opt-in** and
470
+ **disabled by default**.
471
+
472
+ ```bash
473
+ # Enable in .env
474
+ OW_TELEMETRY_ENABLED=true
475
+ ```
476
+
477
+ What is collected: evaluation counts, resource type distributions,
478
+ decision outcomes, condition failure patterns.
479
+
480
+ What is **never** collected: plan contents, cost amounts,
481
+ resource names, organization identifiers, policy file contents.
482
+
483
+ ---
484
+
485
+ ## License
486
+
487
+ Verdict is released under the
488
+ [Apache License 2.0](LICENSE).
489
+
490
+ Free to use, modify, and distribute for any purpose —
491
+ commercial or non-commercial. Attribution required.
492
+
493
+ Built on ObsidianWall — the programmable assurance platform.
494
+ [obsidianwall.com](https://obsidianwall.com)
495
+
496
+ ---
497
+
498
+ ## Built by
499
+
500
+ Aisha I. — [obsidianwall.com](https://obsidianwall.com)
501
+
502
+ > *"Organizations that design for programmable assurance now
503
+ > will not need to retrofit later."*