# ServiceDefinition L0 validation (R-GITOPS-02) Validates the shape of each `catalog/` **ServiceDefinition** against the canonical schema and invariants in the read-only spec `docs/13-service-catalog.md` (with `auth` from `docs/15`, `egress` from `docs/03`). Runs in CI **without a cluster**. ## Why kyverno-json (not the resource CLI) A ServiceDefinition is **arbitrary YAML** — its own `kind:` field means `container | vm`, not a Kubernetes Kind — so it cannot be validated by `kyverno apply`/`kyverno test` (which expect Kubernetes resources). Per [ADR-0011] the Kyverno-ecosystem tool for arbitrary YAML/JSON is **kyverno-json**. ## Invariants enforced (`validate-servicedefinition.yaml`) - `name` is a DNS-1123 label (→ `.home.keithjackson.dev`). - `kind` ∈ {`container`, `vm`}; container ⇒ `source.image`, vm ⇒ `source.vmTemplate`. - `exposure` ∈ {`internal`, `public`}. - `egress` ∈ {`internet`, `vpn`}. - `auth.mode` ∈ {`oidc`, `forward-auth`, `native`, `none`}. - **`public` exposure ⇒ `auth.mode` ≠ `none`** (no unauthenticated public UI, docs/15). - each `dependencies[]` has `to`, `port` ∈ 1–65535, `protocol` ∈ {`tcp`, `udp`}. - each `storage[].class` ∈ {`longhorn`, `bulk`}. ## Fixtures `payloads/valid-*.yaml` must PASS; `payloads/invalid-*.yaml` must FAIL — one failure reason each (bad egress, public+none auth, missing image, bad dependency). ## Run (CI entrypoint) ```bash # Valid payloads: scan must exit 0 kyverno-json scan \ --policy validate-servicedefinition.yaml \ --payload payloads/valid-plex.yaml \ --payload payloads/valid-sonarr.yaml # Invalid payloads: each scan must exit NON-zero (loop asserts failure) for f in payloads/invalid-*.yaml; do if kyverno-json scan --policy validate-servicedefinition.yaml --payload "$f"; then echo "FAIL: expected $f to be rejected"; exit 1 fi done ``` The `scripts/run-l0.sh` wrapper (see `../README.md`) runs both halves and is what CI invokes. ## Caveat (escalated in the T-0.5 report) kyverno-json is **pre-1.0**; its assertion syntax evolves. The **invariants above are the normative content**. The CI agent must pin an exact kyverno-json version and confirm the `check:` expressions parse under it; adjust syntax (never the invariants) if the pinned release differs. The overlapping JSON Schema from WS-GITOPS (T-0.3) is the complementary structural contract. [ADR-0011]: ../../../../home-network-k3s/decisions/adr-0011-test-tooling-kyverno-chainsaw.md