Using Moonway

Custom rules

Your policy as CEL over cached objects — the same expressions as a ValidatingAdmissionPolicy, so rules port both ways.

The built-in checks cover what's true everywhere. Custom rules cover what's true at your place: the registry images must come from, the labels every workload carries, the annotation your on-call tooling requires. A rule is a YAML document with a CEL expression; the engine runs it over the objects the daemon already caches.

id: no-latest-tag
title: Container image uses :latest
severity: warn        # crit | warn | info
kind: Deployment      # Deployment | StatefulSet | DaemonSet | Job | CronJob | Pod | Service | Ingress
cel: object.spec.template.spec.containers.exists(c, c.image.endsWith(":latest"))
message: pulls whatever :latest happens to be today
suggestion: pin the tag

Any number of documents per file, any number of files in the directory. Findings land on the Checks page beside the built-ins — same severities, same suppressions, same jump-to-object.

The CEL you already know

object is the object as kubectl sees it — the same shape, and the same CEL, as a Kubernetes ValidatingAdmissionPolicy. That's deliberate: an expression you've deployed as an admission policy drops into a Moonway rule unchanged, and a rule you've proven here graduates to enforcement without a rewrite. Moonway is the observing half of that pair — it tells you what already violates the policy before you turn on the half that refuses things.

id: require-team-label
title: Workload has no team label
severity: info
kind: Deployment
cel: "!has(object.metadata.labels) || !('team' in object.metadata.labels)"
message: nobody knows who owns this when it pages
suggestion: set metadata.labels.team

Loading rules

Rules load from a directory passed to the daemon at start:

moonwayd --rules ~/moonway-rules

That makes them a feature of daemons whose command line you own — a headless or fleet daemon, or the in-cluster Deployment, where the rules directory is typically a mounted ConfigMap and one policy serves the whole team.

A malformed rule fails loudly at load, one rule failing doesn't take down the rest, and rules only ever read the cache — a custom rule can no more touch your cluster than any other part of Moonway can.