Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
4.0 KiB
schemas — the ServiceDefinition contract
This directory holds the ServiceDefinition interface — the highest-traffic contract in the
platform (spec agents/coordination-protocol.md §2). Onboarding a service is adding/editing one
ServiceDefinition under ../catalog/; the platform compiles it into concrete
resources. Operators declare what; the platform provides the how.
Source of truth: the read-only spec
docs/13-service-catalog.md. This schema matches
docs/13 exactly. Do not diverge from it here — escalate spec problems, don't fork the contract.
Files
| File | Purpose |
|---|---|
servicedefinition.schema.json |
Formal JSON Schema (draft 2020-12) for a ServiceDefinition. Enforces enums, required fields, patterns, and cross-field invariants. |
renderer-contract.md |
Authoritative field → rendered-resource mapping, with requirement IDs. The contract other workstreams consume. |
Schema version: 1.0.0 (in the schema description). Treat any change as an interface change:
propose on the task board, update dependent renderers/tests in the same change, never silently break a
consumer (coordination-protocol §2).
Required vs. defaulted fields
- Required:
name,source(withsource.imageforkind: container,source.vmTemplateforkind: vm). - Everything else is optional and carries a default matching docs/13's "default posture"
(
kind: container,exposure: internal,egress: internet,auth.mode: forward-auth,update.channel: stable,update.autoRollbackOnFailedHealth: true,observability.metrics: true,dependencies: []⇒ DNS-only). A renderer MUST materialize defaults before rendering — seerenderer-contract.md§"Default posture".
Cross-field rules the schema enforces
kind: vm⇒source.vmTemplaterequired; otherwisesource.imagerequired.auth.mode: oidc | forward-auth⇒auth.groupspresent and non-empty (R-IDP-06 — access is "in an authorized group," not merely authenticated).ingressRoutes[].hostmust match<label>.home.keithjackson.dev.storage[].class ∈ {longhorn, bulk};exposure ∈ {internal, public};egress ∈ {internet, vpn};auth.mode ∈ {oidc, forward-auth, native, none}.additionalProperties: falseeverywhere — unknown keys are rejected (catches typos in review/CI).
Validate examples against the schema — L1, no cluster (R-TEST-02)
Reproducible, single command, no cluster required. Canonical tool: check-jsonschema (YAML-native, pins the schema file, pin the tool version for determinism):
# one-time (pin the version for reproducibility)
pip install check-jsonschema==0.38.0
# validate every catalog entry against the contract
check-jsonschema --schemafile schemas/servicedefinition.schema.json catalog/*.yaml
# -> "ok -- validation done"
Fallback with no extra tool (Python jsonschema + pyyaml only):
python3 - <<'PY'
import json, yaml, glob, sys
from jsonschema import Draft202012Validator
schema = json.load(open('schemas/servicedefinition.schema.json'))
Draft202012Validator.check_schema(schema)
v = Draft202012Validator(schema); bad = False
for f in sorted(glob.glob('catalog/*.yaml')):
errs = sorted(v.iter_errors(yaml.safe_load(open(f))), key=lambda e: list(e.path))
print(('PASS ' if not errs else 'FAIL ') + f)
for e in errs: print(' ', list(e.path), e.message); bad = True
sys.exit(1 if bad else 0)
PY
Both are pure manifest/schema checks (L0/L1 in spec docs/14, table row "L1 Rendering") — they run in
CI with no cluster and are wired into the .gitea/ schema-validation gate by WS-TEST.
See also
renderer-contract.md— what each field renders into (Deployment/VM, PVC, CiliumNetworkPolicy, egress steering, HTTPRoute/Certificate/DNS, ServiceMonitor/alerts, ImagePolicy, Keycloak/oauth2-proxy).- Example ServiceDefinitions:
../catalog/pihole.yaml,../catalog/keycloak.yaml.