0012. Kubernetes: the operator as an API client, one image, three charts
- Status: Accepted, 2026-10-06 (Phase 4)
Decision
Section titled “Decision”- The operator is a client of the evalsid API, not a second control plane.
EvalRunandOnlineEvalPolicyare reconciled throughRunServiceandMonitorService, with the operator’s own service-account token. Runs it creates are authorized and audited like any other.- Their
specis the API message as JSON (runtime.RawExtension), validated on admission with the CLI’s rules. A run file and a resource are the same document, sokubectl apply -fandevalsi run -ftake the same files; the project comes from theevals.si/projectlabel, else the namespace. EvalRunspecs are immutable; deleting the resource cancels the run and keeps its results. Policy names stay global on the server, and a name taken by another project is aConflict.EvaluatorandSandboxClassrender Deployments (and KEDAScaledObjects), because they are workloads, not API objects.
- One image.
evalsid,evalsi-guest,evalsi-operatorand the Python worker ship together. The pod rung injectsevalsi-guestfrom it, and plugin images buildFROMit. - Three charts.
evalsiinstalls into one namespace and needs no cluster permissions;evalsi-crdsholds everything cluster-scoped (CRDs, webhooks, cluster roles);evalsi-sandboxdholds the privileged node pools. A platform team installs the last two once; teams installevalsiper namespace. - Kubernetes identity is OIDC. The cluster’s service-account issuer is one more JWT provider (
kubernetes:), with keys read from the API server; workloads sign in with projected tokens. Internal hops use mutual TLS from an internal CA (or cert-manager). - The admission webhook records the creator.
evals.si/created-bycomes from the authenticated admission request and cannot be set or changed by the creator.
Consequences
Section titled “Consequences”- The server stays the single source of truth for runs, results and policies; the operator holds no state the server lacks, and losing it loses nothing.
- Because the operator is only an API client, it is optional: on clusters where no cluster-admin step is possible, the
evalsichart installs alone (namespace-only, Pod Securityrestricted, its own pod-rung or Landlock sandbox pool), and runs go through the CLI and API instead ofkubectl apply. - Specs are not typed in the CRD’s OpenAPI schema (they are preserved as is), so
kubectl explaincannot describe them; the webhook gives the errors instead. - User-namespaced sandbox pools need Kubernetes 1.33+ and a kernel and runtime that support them; elsewhere (including kind) the bubblewrap pool runs privileged.
- The pod rung cannot snapshot, so environment setup runs once per trial on it.