Explainable policies that run on Open Policy Agent and Rego.

ergo is an open source language for writing explainable policies. Instead of a binary result, every decision returns an audit report of what was checked, and why it passed or failed.

ergo the policy

requirements:
  prod_deploy:
    subject_type: deployment
    from: [deployments]
    id: [id]
    applies_to:
      is_prod:
        op: equals
        path: [environment]
        value: prod
    checks:
      approved:
        description: Someone approved the deployment
        op: non_empty_string
        path: [approved_by]

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

requirements:
  peer_reviewed:
    subject_type: pull request
    from: [pull_requests, {each_as: pr}]
    id: [number]
    checks:
      peer:
        description: Someone other than the author approved it
        op: any
        path: [approvers]
        check:
          op: compare
          left: [username]
          right: [$pr, author]
          cmp: ne

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
1peer$pr.author = "ann"
approvers: ann, bob
truesatisfied
2peer$pr.author = "bob"
approvers: bob
falsevalue
3peer$pr.author = null
approvers: bob
falseabsent

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

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.

In regulated settings, someone else has to be able to check that a policy behaved as it should. A yes or no can't be validated. You don't just tell the auditor 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 requirement 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

    subject_type, from and id say what the subjects are and where to find them.

  2. 2

    Filter the scope

    applies_to keeps only the subjects this requirement is about. The rest are recorded, not dropped.

  3. 3

    Test the values

    checks are the rules each subject must pass. ergo does the looping, the evaluation and the report.

# policy/requirements.yaml
requirements:
  prod_deploy:
    subject_type: deployment
    from: [deployments]
    id: [id]
    applies_to:
      is_prod:
        op: equals
        path: [environment]
        value: prod
    checks:
      approved:
        description: Someone approved the deployment
        op: non_empty_string
        path: [approved_by]

One report format.
Every policy.

A deployment policy. A code-review policy. A vulnerability 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

OPA is the engine.
ergo is a rego library.

ergo doesn't replace OPA. Copy a single rego file into your policies. The policy is expressed as YAML, JSON or rego and ergo provides a single evaluation and reporting model.

  1. inputs
    • inputdeployments.json
    • policyrequirements.yaml
    • library ergo.rego
  2. evaluation
    • engineOPA 1.19
  3. outputs
    • decisioncompliant
    • evidence report

Auditor says why
Computer says ergo

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

The bakery example

One file. No lock-in.

  1. 01

    Copy the library

    Copy ergo.rego next to your policies. That's the install.

    policy/
      ergo.rego
  2. 02

    Write a requirement

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

    report := ergo.report(input, data.requirements)
  3. 03

    Read the report

    Ask OPA for the report, or just the violations.

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

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.