A deterministic pre-deployment infrastructure governance engine
Project description
ObsidianWall Verdict
Pre-deployment infrastructure governance. Evaluate Terraform plans against governance policies before deployment executes — catching budget overruns, policy violations, and compliance failures before they become incidents.
What it does
Verdict runs as a CLI command or a GitHub Actions step. It takes a Terraform plan and a policy file, evaluates the plan deterministically against the policy conditions, and produces a governance decision with a full audit trail.
$ verdict evaluate \
--plan terraform_plan.json \
--policy policies/cost/basic_budget.yaml \
--role engineer
policy basic_budget_verdict
condition budget_check ✗ FAILED
expression (current_spend + estimated_cost) <= budget.amount
evaluated (0 + 100) <= 50 → false
risk score 75 / 100 (critical)
findings cost_analysis: 2 topology: 1
notified budget_owner (email) engineering_lead (slack)
decision DENY_WITH_OVERRIDE
override budget_owner may authorize
decision_id abc3a13b-83d5-4fad-87d8
✗ Deployment blocked by governance policy.
No AI guessing. No approximations. Every decision is deterministic, reproducible, and attributable to a human-authored policy.
Quickstart
Install
pip install obsidianwall-verdict
Write a policy
# policies/cost/budget.yaml
apiVersion: obsidianwall.io/v1
kind: Policy
metadata:
name: team_budget
version: "0.1"
owner: your-team
spec:
inputs:
- estimated_cost
- current_spend
parameters:
budget:
amount: 5000
period: monthly
flexibility: soft
conditions:
- id: budget_check
expression: "(current_spend + estimated_cost) <= budget.amount"
description: "Monthly spend must not exceed budget"
decision:
allow: ALLOW
deny: DENY_WITH_OVERRIDE
warn: ALLOW_WITH_NOTIFICATION
governance:
severity: medium
notifications:
- role: budget_owner
channel: email
- role: engineering_lead
channel: slack
override:
roles:
- budget_owner
requires_approval: false
Evaluate a plan
# Generate your Terraform plan
terraform plan -out=tfplan
terraform show -json tfplan > terraform_plan.json
# Run governance evaluation
verdict evaluate \
--plan terraform_plan.json \
--policy policies/cost/budget.yaml \
--role engineer
Verdict returns exit code 0 on ALLOW and non-zero on DENY,
blocking CI/CD pipelines automatically.
GitHub Actions
Add Verdict as a governance gate in your CI/CD pipeline in one step:
# .github/workflows/governance.yml
name: Infrastructure Governance
on:
pull_request:
paths: ["**.tf", "**.tfvars"]
jobs:
governance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate Terraform plan
run: |
terraform init
terraform plan -out=tfplan
terraform show -json tfplan > terraform_plan.json
- name: ObsidianWall Verdict
id: verdict
uses: obsidianwall/obsidianwall-verdict@main
with:
plan: terraform_plan.json
policy: policies/cost/budget.yaml
role: engineer
fail_on_deny: "true"
- name: Governance outcome
if: always()
run: |
echo "Decision: ${{ steps.verdict.outputs.decision }}"
echo "Risk score: ${{ steps.verdict.outputs.risk_score }}/100"
echo "Severity: ${{ steps.verdict.outputs.effective_severity }}"
Verdict posts a governance summary table to the workflow step summary on every run. Budget owners and engineering leads receive notifications through the channels defined in the policy.
Action outputs
| Output | Description |
|---|---|
decision |
ALLOW / ALLOW_WITH_NOTIFICATION / ALLOW_WITH_APPROVAL_REQUIRED / DENY_WITH_OVERRIDE / DENY |
conditions_passed |
true or false |
risk_score |
Integer 0–100 |
effective_severity |
informational / low / medium / high / critical |
decision_id |
UUID for audit trail correlation |
How it works
Verdict runs a deterministic evaluation pipeline on every invocation:
Terraform plan
↓
Context builder Parses resources, estimates cost
↓
Policy loader Loads and validates the policy YAML
↓
Runtime normalizer Flattens policy parameters into evaluation context
↓
Condition evaluator Evaluates each condition deterministically
↓
Analyzer framework Cost, topology, architecture, utilization analysis
↓
Risk scorer Aggregates findings into risk score (0–100)
↓
Decision resolver 5-level governance decision with override routing
↓
Explainability Reasoning chain, trace graph, remediation steps
↓
Notification manifest Stakeholder routing — never dispatched automatically
↓
Audit artifact Immutable JSON record of the complete evaluation
Every stage is deterministic. Analyzers are advisory — they inform the risk score but never override the condition evaluation. The condition evaluation alone determines the governance decision.
Enforcement modes
| Mode | How | What it blocks |
|---|---|---|
| CI/CD pipeline | GitHub Actions with fail_on_deny: true |
terraform apply never runs on DENY |
| IAM access controls | Azure Entra ID / AWS IAM restricts engineer credentials to read-only | Direct deployment from local machines impossible |
| Standalone manual | Run verdict evaluate before terraform apply |
Governance guidance, audit trail, budget owner notification |
For hard technical enforcement, integrate Verdict into your CI/CD pipeline and restrict cloud credentials so only the pipeline's service principal can apply infrastructure. Engineers with read-only credentials cannot deploy directly even if they skip Verdict locally.
Governance decisions
Verdict produces one of five decisions on every evaluation:
| Decision | Meaning |
|---|---|
ALLOW |
All conditions passed. Deployment authorized. |
ALLOW_WITH_NOTIFICATION |
Conditions passed but high severity — stakeholders notified. |
ALLOW_WITH_APPROVAL_REQUIRED |
Conditions passed but formal approval required before deployment. |
DENY_WITH_OVERRIDE |
Conditions failed. An authorized role may override. |
DENY |
Conditions failed. No override permitted. Hard block. |
Policy DSL
Policies are YAML files that define governance constraints for an infrastructure change. The schema is versioned and validated on load.
Policy structure
apiVersion: obsidianwall.io/v1 Protocol version
kind: Policy Always Policy for now
metadata:
name: string Policy identifier
version: string Semver string
owner: string Responsible team
description: string Optional description
spec:
inputs: list[string] Runtime context keys required
parameters: dict Policy parameters (flattened at runtime)
conditions: list[Condition] Evaluated deterministically
decision: Decision allow / deny / warn mappings
governance: GovernanceConfig Severity, notifications, approvals
override: Override Roles and approval requirements
actions: list[Action] notify / log actions
Condition expressions
Expressions use a restricted grammar — no eval(), no dynamic code:
conditions:
- id: budget_check
expression: "(current_spend + estimated_cost) <= budget.amount"
description: "Monthly spend must not exceed budget"
- id: instance_size_check
expression: "estimated_cost <= max_instance_cost"
description: "No single instance may exceed cost threshold"
Supported operators: <=, >=, <, >, ==
Supported arithmetic: +
Context resolution: dot-notation for nested parameters (budget.amount)
Audit artifact
Every evaluation produces a complete audit artifact:
{
"decision_id": "abc3a13b-83d5-4fad-87d8-bbe77e4b8075",
"timestamp": "2026-05-20T04:05:28Z",
"policy": "basic_budget_verdict",
"decision": "DENY_WITH_OVERRIDE",
"override_possible": true,
"override_required": false,
"conditions_passed": false,
"effective_severity": "critical",
"risk_summary": {
"overall_risk_score": 75,
"risk_severity": "critical",
"highest_risk_analyzer": "cost_analysis",
"total_findings": 3
},
"trace": [...],
"explanation": {
"governance_reasoning": {...},
"policy_reasoning": {...},
"trace_graph": {...}
},
"notification_manifest": {...}
}
The artifact is written to output/result.json and printed to stdout.
It is suitable for storage in an audit log, S3 bucket, or compliance system.
Doctrine
AI may advise.
AI may explain.
AI may optimize.
AI may correlate.
AI may recommend.
AI may NOT authoritatively govern.
Every governance decision in ObsidianWall Verdict is produced by deterministic evaluation of human-authored policy conditions — never by a probabilistic model.
Decisions are reproducible, explainable, and attributable to a named policy and a named human who wrote it.
This is not an anti-AI position. The analyzer framework, recommendation engine, and explainability pipeline all use intelligence to inform the governance process. The boundary is authority: intelligence informs, policy governs.
Architecture
Verdict is the first executable of the ObsidianWall programmable assurance platform.
┌─────────────────────────────────────────────────────┐
│ Open Governance Core (this repo — open source) │
│ │
│ engine/ deterministic evaluation pipeline │
│ schemas/ policy DSL and typed contracts │
│ context/ Terraform plan parsing │
│ audit/ structured audit logging │
│ cli/ command-line interface │
└─────────────────────────────────────────────────────┘
↓ telemetry (opt-in, anonymous)
┌─────────────────────────────────────────────────────┐
│ Intelligence Layer (future — private) │
│ │
│ Derived optimization intelligence │
│ Workload pattern recognition │
│ Pricing behavior intelligence │
│ Predictive governance scoring │
└─────────────────────────────────────────────────────┘
↓ enterprise workflows
┌─────────────────────────────────────────────────────┐
│ Platform Layer (future — paid) │
│ │
│ Hosted policy management │
│ Approval workflow persistence │
│ Governance dashboards │
│ RBAC, SSO, multi-tenant │
│ Compliance exports │
└─────────────────────────────────────────────────────┘
Development
Requirements: Python 3.13+, Git
git clone https://github.com/obsidianwall/obsidianwall-verdict
cd obsidianwall-verdict
pip install -r requirements.txt
# Run the full test suite
pytest tests/ -v
# Run a sample evaluation
verdict evaluate \
--plan samples/terraform_plan.json \
--policy policies/cost/basic_budget.yaml \
--role engineer
Test suite: 101 tests — unit, integration, and pipeline.
pytest tests/unit/ # 82 tests
pytest tests/integration/ # 11 tests
pytest tests/ # 101 tests
Policy examples
Sample policies are in policies/cost/:
| Policy | Enforcement | Use case |
|---|---|---|
basic_budget.yaml |
Soft — override available | Engineering teams with budget owner oversight |
strict_budget.yaml |
Hard — dual approval required | Production environments, finance-controlled budgets |
Telemetry
Verdict collects anonymous usage telemetry to improve the optimization catalog and governance intelligence. Telemetry is opt-in and disabled by default.
# Enable in .env
OW_TELEMETRY_ENABLED=true
What is collected: evaluation counts, resource type distributions, decision outcomes, condition failure patterns.
What is never collected: plan contents, cost amounts, resource names, organization identifiers, policy file contents.
License
Verdict is released under the Apache License 2.0.
Free to use, modify, and distribute for any purpose — commercial or non-commercial. Attribution required.
Built on ObsidianWall — the programmable assurance platform. obsidianwall.com
Built by
Aisha I. — obsidianwall.com
"Organizations that design for programmable assurance now will not need to retrofit later."
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file obsidianwall_verdict-0.2.1.tar.gz.
File metadata
- Download URL: obsidianwall_verdict-0.2.1.tar.gz
- Upload date:
- Size: 63.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b982cbdf5490d8797a2b3876de26e68076c9248497f7b06e70b519cf510e8077
|
|
| MD5 |
0a16f003cc7d4afe22a991679366ebd7
|
|
| BLAKE2b-256 |
6f7f293975b0730b74f20d5dc58984597854147db3d3baa1ccc9ec102617f25f
|
File details
Details for the file obsidianwall_verdict-0.2.1-py3-none-any.whl.
File metadata
- Download URL: obsidianwall_verdict-0.2.1-py3-none-any.whl
- Upload date:
- Size: 76.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94daf701b4fd087ecd7969ed95cd65b596ca9ca64dd18c90398a49400514c1dd
|
|
| MD5 |
5bbe44b572f80c00fa6734e549e0f8ac
|
|
| BLAKE2b-256 |
3ad1e34fc51e2a28baa80c2874127e7289693943bfa721aee959dda146f2f1a4
|