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 tagAny 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.teamLoading 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.