An open language for explainable policies.

Ergo is an open source language for writing policies that explain themselves. Instead of a yes or no, every decision returns an audit report of what was checked, and why it passed or failed. Run your policies on Open Policy Agent with ergo.rego, or with the ergo CLI.

Latest release: ergo.rego 0.6.0, ergo CLI 0.4.0, spec 0.6

Ergo the policy

ergo_spec: v0.6
subjects:
  deployment:
    from: deployments whose environment is "prod"
    identified_by: id
requirements:
  prod_deploy:
    subject: deployment
    checks:
      approved:
        description: Someone approved the deployment
        assert: approved_by is a non-empty string

Ergo the evaluation, reading this input

{
  "deployments": [
    {"id": "d-1", "environment": "prod",
     "approved_by": "alice"},
    {"id": "d-2", "environment": "prod"},
    {"id": "d-3", "environment": "staging"}
  ]
}

Three deployments. d-2 has no approved_by field at all, and d-3 is in staging.

Ergo the report

subjectcheckvalue readpassedcause
$well_formedcount(checks) = 1truesatisfied
$min_subjectsin-scope deployment count = 2truesatisfied
$unique_idsrepeated deployment ids = []truesatisfied
d-1$appliesenvironment = "prod"truesatisfied
d-2$appliesenvironment = "prod"truesatisfied
d-3$appliesenvironment = "staging"falsevalue
d-1approvedapproved_by = "alice"truesatisfied
d-2approvedapproved_by = nullfalseabsent

d-3 is out of scope. d-2 was never approved… and the report says so.

Ergo the policy

ergo_spec: v0.6
subjects:
  pull request:
    from: pull_requests
    identified_by: number
requirements:
  peer_reviewed:
    subject: pull request
    checks:
      peer:
        description: Someone other than the author approved it
        for: some approvers as approver
        assert: approver.username does not equal author

Ergo the evaluation, reading this input

{
  "pull_requests": [
    {"number": 1, "author": "ann",
      "approvers": [{"username": "ann"}, {"username": "bob"}]},
    {"number": 2, "author": "bob",
      "approvers": [{"username": "bob"}]},
    {"number": 3,
      "approvers": [{"username": "bob"}]}
  ]
}

Three pull requests. PR 2 was approved only by its author, and PR 3 has no author recorded.

Ergo the report

subjectcheckvalue readpassedcause
$well_formedcount(checks) = 1truesatisfied
$min_subjectsin-scope pull request count = 3truesatisfied
$unique_idsrepeated pull request ids = []truesatisfied
1peerauthor = "ann"
approvers: ann, bob
truesatisfied
2peerauthor = "bob"
approvers: bob
falsevalue
3peerauthor = null
approvers: bob
falseabsent

PR 2 was only approved by its author. PR 3 has no author recorded. Different problems, different fixes.

Ergo the policy

ergo_spec: v0.6
subjects:
  expense:
    from: expenses
    identified_by: id
requirements:
  expense_policy:
    subject: expense
    checks:
      within_limit:
        description: The meal costs at most €50
        assert: amount is at most 50
      receipt:
        description: A receipt is attached
        assert: receipt is a non-empty string

Ergo the evaluation, reading this input

{
  "expenses": [
    {"id": "e-1", "amount": 42,
     "receipt": "r-881.pdf"},
    {"id": "e-2", "amount": 68,
     "receipt": "r-902.pdf"},
    {"id": "e-3", "amount": 35}
  ]
}

Three meal expenses. e-2 is over the €50 limit, and e-3 has no receipt.

Ergo the report

subjectcheckvalue readpassedcause
$well_formedcount(checks) = 2truesatisfied
$min_subjectsin-scope expense count = 3truesatisfied
$unique_idsrepeated expense ids = []truesatisfied
e-1receiptreceipt = "r-881.pdf"truesatisfied
e-1within_limitamount = 42truesatisfied
e-2receiptreceipt = "r-902.pdf"truesatisfied
e-2within_limitamount = 68falsevalue
e-3receiptreceipt = nullfalseabsent
e-3within_limitamount = 35truesatisfied

e-2 costs too much and e-3 has no receipt, so whoever claimed them knows exactly what to fix.

A decision nobody can inspect
isn't evidence.

With Rego you can block a deployment, but you can't show later why. When you write policies with Ergo you have records of why decisions were made.

When a decision matters, someone else has to be able to check that the policy behaved as it should. A yes or no can't be validated. You don't just tell an auditor, a customer or a colleague the decision. You hand over the workings.

Rego provides decisions

{
  "allow": false
}

Useful. But why was it false?

With Ergo you get explanations

subject  check      value read               passed  cause
d-1      approved   approved_by = "alice"    true    satisfied
d-2      approved   approved_by = null       false   absent
d-3      $applies   environment = "staging"  false   value (out of scope, recorded)

Same decision. Now you can see why.

Write the policy.
Not the sorcery.

Defined in YAML or Rego

Write the policy as a YAML file or as a Rego object. Ergo reads both the same way and gives the same report.

  1. 1

    Define the subjects

    Each subject gets a name. from says where the subjects are in the input, and identified_by names the field that tells them apart.

  2. 2

    Filter the scope

    whose keeps only the subjects the policy is about, here the production deployments. The rest are recorded, not dropped.

  3. 3

    Test the values

    A requirement names its subject, and its checks are the rules each one must pass, each written as a sentence. Ergo does the looping, the evaluation and the report.

# policy/policy.yaml
ergo_spec: v0.6
subjects:
  deployment:
    from: deployments whose environment is "prod"
    identified_by: id
requirements:
  prod_deploy:
    subject: deployment
    checks:
      approved:
        description: Someone approved the deployment
        assert: approved_by is a non-empty string

One report format.
Every policy.

A deployment policy. An expense policy. A loan eligibility policy. Different checks, same evidence: one row per subject and check. Passing and failing rows have the same fields.

  1. requirement

    Which requirement produced the row.

  2. subject

    The thing that was judged, with its type and id.

  3. check

    The rule that ran.

  4. inputs

    The values the check actually read.

  5. passed

    The result. Always true or false.

  6. cause

    Why it reached that result.

Same policy + same input = the same report, byte for byte. Hash it. Diff it. Store it.

  • Nothing disappears.
  • Nothing is implied.
  • Everything is reproducible.
  • Everything has a reason.
  • Ergo

One language.
Two engines.

Two engines can run your policies, and both follow the same spec. ergo.rego runs on Open Policy Agent, so it runs wherever OPA does. The Rust engine runs in the ergo CLI and in the playground in your browser, and is still experimental. Both pass every case in the spec.

  1. inputs
    • inputdeployments.json
    • policypolicy.yaml
  2. one engine, following the Ergo spec
    • engineergo.regoruns on OPA 1.20
    • or
    • engineRust engine (experimental)runs in the ergo CLI and the playground
  3. outputs
    • decisioncompliant
    • evidence report

Auditor says why
Computer says Ergo

Learn how to write self-explaining automated policies in this tutorial, in your browser:

Learn Ergo · Read the bakery example

No lock-in.

  1. 01

    Write a policy

    Say what you're judging, what's in scope and what must hold, in policy.yaml.

    ergo_spec: v0.6subjects:  deployment:    from: deployments whose environment is "prod"    identified_by: idrequirements:  prod_deploy:    subject: deployment    checks:      approved:        description: Someone approved the deployment        assert: approved_by is a non-empty string
  2. 02

    Pick an engine

    Either one runs the same policy.

    OPA with ergo.rego

    Copy it next to the policy, with a few lines of Rego that hand the policy to it. Get started shows how to run it.

    policy/
      policy.yaml
      ergo.rego
      deploy.rego

    or

    ergo CLI

    Build it with Cargo, or download it for Linux (x86_64) or macOS (arm64) from the latest release of the CLI.

    cargo install --locked ergo-policy-cli
  3. 03

    Read the report

    Every check, with what it read and why it passed or failed.

    OPA with ergo.rego

    Ask OPA for the report, or for data.deploy.violations to see only what failed.

    opa eval -d policy -i deployments.json \
      'data.deploy.report'

    or

    ergo CLI

    Run ergo report, or ergo violations to see only what failed.

    ergo report --policy policy/policy.yaml \
      --input deployments.json

Or try it in the browser, with nothing to install: the playground (opens in a new tab) (experimental)

Ergo /ˈɜː.ɡəʊ/

Therefore. As a result. A conclusion that comes with its premises.

Not computer says no. Here is the decision, and here is what produced it.

Rego says no. Ergo says why.Show the working.