Chris Collins

Tools · dev

Policy playground: Rego and CEL

Write policies in Rego and CEL and test them against JSON inputs in the browser — a Rego interpreter with a trace of which rules fired and why the others didn't, real CEL with type errors, examples for RBAC, Kubernetes admission, API authorisation from JWT claims and Terraform plan checks, and a decision log of every input and result.

Added

How to use it

Rego (the language of Open Policy Agent) and CEL (Google’s Common Expression Language, used by Kubernetes, Envoy and cloud IAM) are the two policy languages you are most likely to meet. This page lets you write either, feed it a JSON input, and see the decision — and, for Rego, the reasoning.

How to use it

Rego. Pick an example: Rego basics, Role-based access control (role bindings in a data document, wildcards, a reason for every decision), Kubernetes admission (an AdmissionReview for a Deployment: no privileged containers, required labels, trusted registries, memory limits, and the response the webhook would send), API authorisation with JWT claims (decode a bearer token, check issuer, audience and expiry, allow routes by scope, role and ownership) and Terraform plan checks (public buckets, missing tags, destroyed databases, instance types, SSH open to the world, run over terraform show -json). The policy is evaluated as you type.

  • Trace lists every rule body in the order of the source: the ones that fired, with the values they produced and the variable bindings, and the ones that did not fire, with the line they stopped at and the values that made it fail (input.user.role = "viewer"; "admin" = "admin"). Click one to jump to the line.

  • Result is the whole package as OPA prints data.<package>. Query takes a ref such as data.rbac.allow or statements such as some m in data.k8s.admission.deny.

  • Warnings & print shows built-in errors (which make an expression undefined, as OPA does by default) and print() output.

  • The input and data panes take JSON or YAML; Open file… loads one. Some examples have buttons for other inputs (another user, another route) to compare.

CEL. Examples: a Kubernetes ValidatingAdmissionPolicy (the same admission checks as expressions over object, oldObject, request and params), HTTP request rules and JWT claims. Each expression shows true or false — with its message when false, as an admission policy would — or its value and CEL type, or the parse, type or evaluation error with a caret under the position. Types from the context declares each variable with types inferred from the context JSON, so a misspelt field is a type error before anything runs, as Kubernetes does with an object’s schema. Load claims from a JWT reads a token’s payload into claims (make one with the JWT decoder and signer).

⛶ Full screen gives the editors and the output the whole screen; Escape leaves.

Decision log keeps every evaluation in this browser — engine, example, input, result and a one-line summary — so you can compare inputs and replay one; Export JSON lines saves it.

Objects for the admission examples are easy to make with the Kubernetes manifest builder; for plans, the Terraform plan visualiser shows what the plan does before you write rules about it.

The Rego interpreter

OPA can run in a browser only as WebAssembly compiled ahead of time from a particular policy (opa build -t wasm); there is no in-browser Rego compiler on npm. I looked for an evaluator that runs Rego directly: Microsoft’s Regorus (Rust) has WebAssembly bindings in its repository but no browser package on npm that I could find, and the other Rego packages there are syntax highlighters, AST tools, or wrappers that call an OPA server. So this page has its own interpreter for a well-defined subset of Rego v1, written for it and tested against OPA’s documented semantics. The Reference tab lists exactly what it supports; in short:

  • rules (complete, default, partial sets with contains, partial objects p[k] := v, functions with argument patterns, else chains), the older p { … } and deny[msg] { … } forms too;

  • some … in, every, not, := and = (unification), iteration through refs such as input.items[i] and data.roles[r][_], array/set/object comprehensions, in, set operators;

  • undefined, conflicts and built-in errors behaving as in OPA;

  • 73 built-ins: count, sum, max, sort, startswith, endswith, contains, sprintf, lower, upper, split, concat, trim*, replace, substring, regex.match, glob.match, object.get, object.keys, object.union, union, time.now_ns, time.parse_rfc3339_ns, io.jwt.decode, json., base64, net.cidr_contains and more.

Not supported: with, several modules at once, dotted rule heads, compile-time type checking, signature verification (io.jwt.decode_verify), http.send and most of OPA’s larger built-in families. A policy that uses something outside the subset gets a parse or compile error, not a quietly different answer. Check anything that matters with opa eval before you rely on it.

The CEL engine

CEL runs in @marcbachmann/cel-js 8.0.0 (MIT), loaded from jsDelivr the first time you open the CEL tab (about 110 KB, 32 KB compressed). It implements most of the CEL specification: operators, macros (has, all, exists, exists_one, map, filter), strings, lists, maps, bytes, timestamps and durations, matches, and optional types (.?field, [?key], orValue). Kubernetes' extra libraries (quantity(), url(), ip(), authorizer, the list and regex extensions) are not available. Whole JSON numbers become int, as Kubernetes treats integer fields.

Browser support

Any current browser. The CEL engine is an ES module and needs a browser from the last few years; Rego needs nothing beyond JavaScript.

Privacy

Policies, inputs and the decision log stay in this browser. The only network requests are for the CEL engine and, if an input isn’t JSON, a YAML parser (js-yaml), both from jsDelivr; nothing you type is sent. JWTs are decoded in the page and never verified or sent anywhere — but don’t paste production tokens into share links.

Runs entirely in your browser — network requests go only to the services named on this page.