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 asdata.rbac.allowor statements such assome 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 withcontains, partial objectsp[k] := v, functions with argument patterns,elsechains), the olderp { … }anddeny[msg] { … }forms too; -
some … in,every,not,:=and=(unification), iteration through refs such asinput.items[i]anddata.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_containsand 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.
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser — network requests go only to the services named on this page.