ERDL Language Specification

Declarative semantic specification for AI governance · v2.1

One spec, one canonical tree, one hash — the same rule and input produce byte-for-byte identical results and hashes on any conforming implementation.

Core numbers

34
node expression-tree kernel (arithmetic / quantifier / aggregate / time / stateful operators)
13
decision types (ALLOW / DENY / CORRECT / REQUEST_HUMAN / ESCALATE …)
30
operators in the Simple projection (pure conditions, highest audit grade)
3·
projection surfaces — Simple / Expression / Decision Table, compiled to one semantic tree
2·
formats — YAML / JSON, implementation-neutral, cross-platform

Why ERDL

ProblemHow ERDL solves it
LLM outputs are probabilisticDeterministic when → then guardrails, evaluated outside the model — the prompt never holds the safety boundary
Rules drift across implementations318 JCS + SHA-256 vectors enforce byte-for-byte consistency
Compliance needs audit trailsEvery evaluation produces a cryptographically verifiable hash
Business users can't read codeThree projection surfaces, compiled to one semantic tree

Three projections: one rule, three ways

Business users write a decision table, integrators write Simple conditions, the kernel uses the expression tree — all compile to the same canonical tree and hash.

# Simple projection (30 operators, highest audit grade)
when:
  logic: AND
  conditions:
    - field: "tool.name"   operator: eq   value: "issue_refund"
    - field: "tool.args.amount"  operator: gt  value: 5000
then: REQUEST_HUMAN

One rule, three languages — which can a non-engineer actually read?

“Employees must not send customer data to any recipient outside the company’s domain list.”

Now, three languages turn it into an executable rule.


Version 1: ERDL (Simple form)

protocol: "erdl/v2"
metadata:
  name: "customer-data-egress"
  decision: ALLOW                        # allow when no rule matches
rules:
  - name: "SEC-001"
    description: "customer data must not be exfiltrated"
    when:
      logic: AND
      conditions:
        - field: "data.classification"
          operator: eq
          value: "customer_data"
        - field: "recipient.domain"
          operator: not_in
          value: ["internal.example.com", "partner.example.com"]
    then: DENY
    message: "customer data must not be sent to external domains"

The engine-generated natural language (gloss projection):

When “data classification” equals “customer data”, and “recipient domain” is not in [internal.example.com, partner.example.com], deny.


Version 2: Rego

package egress

import rego.v1

default allow := false                                    # ← forget this line and it fails open

internal_domains := {"internal.example.com", "partner.example.com"}

internal_recipient if {                                   # ← a "query", not a "verdict"
    input.recipient.domain in internal_domains
}

deny if {                                                 # ← negate a query
    input.data.classification == "customer_data"
    not internal_recipient
}

allow if {                                                # ← negate once more
    not deny
}

Version 3: Cedar

forbid(
  principal,                                             # ← authorization triple, requires the entity model
  action == Action::"send",                              # ← typed action, requires a schema
  resource
)
when {
  resource.classification == "customer_data" &&
  !(resource.recipient.domain in ["internal.example.com", "partner.example.com"])
};

What must a non-engineer “extra-learn” to read each one correctly?

ERDLRegoCedar
Verdict vs queryVerdict (DENY)Query (allow is a set)Verdict (forbid)
Default valueNo (decision: ALLOW is written in the open)Yes — omit default allow := false and it fails openNo (forbid is natively denying)
Double negationNoYesnot internal_recipient + allow if { not deny }A little (!)
Entity model / schemaNo (a field is a field)NoYes — principal/action/resource + typed
Three-kinds-of-equals trapNoneYes (:= / == / =)None

The conclusion, at a glance:


Why ERDL “speaks human” and Rego/Cedar don’t

The root is not “syntactic sugar”; it is the semantic model:

So “a layperson gets it after a brief intro” isn’t marketing — it’s a choice of semantic model: when the rule’s semantic model is isomorphic to the policy’s semantic model, reading the rule ≈ reading the policy; when it isn’t, reading the rule = first learning an engineer’s abstraction language.


Note: in this comparison ERDL is real spec syntax (Simple form + gloss projection); Rego/Cedar are the “correct standard forms a proficient engineer would write”, with no straw-manning.

Quick Start (30 seconds)

bash
$ npm install @openoba/erdl
import { loadErdlFile, Evaluator } from '@openoba/erdl'

const { rules, metadata } = loadErdlFile('refund.erdl.yaml')
const result = new Evaluator().evaluate(
  rules,
  { tool: { name: 'issue_refund', args: { amount: 8000 } } },
  { fallbackDecision: metadata.decision },
)
console.log(result.decision)  // 'REQUEST_HUMAN'

Full specification

Full spec: English (on-site) · 站内全文(中文) · npm @openoba/erdl