Skip to content

ADR-0003: Go operator with the App CRD as the single source of truth

Status: Accepted — 001-walking-skeleton (2026-06) Principles served: IV, V Source: specs/001-walking-skeleton/research.md D-1, D-2; docs/design.md D5; .specify/memory/constitution.md Principle IV

Context

The Tend operator owns the app lifecycle and is the largest custom build item — the actual product core. It needs a real state machine (sandbox, promotion-pending, production, frozen, retired), CRD types, and admission integration. Every platform behavior — provisioning, policy enforcement, lifecycle, cost attribution, estate views — must reconcile from one authoritative representation of each app, or the governance plane silently rots through divergent state.

Decision

The operator is written in Go with Kubebuilder / controller-runtime and tested with envtest. Each app is exactly one App custom resource (tendtech.io/v1alpha1) carrying owner, expiry, declared data scopes, purpose classification, environment state, and references to its git repo and attestation chain. The App CR in etcd is the only authoritative state; no platform database exists. The evidence/estate API is a stateless projection assembled at read time, and access logs are gateway telemetry — neither holds authoritative app state.

Alternatives considered

  • Metacontroller / shell-based operators — rejected: too thin for a real state machine.
  • Python (kopf) — rejected: weaker CRD/webhook ecosystem and a second toolchain beside the Go platform services.
  • A backing Postgres for app metadata — rejected: duplicate source of truth and sync rot, the exact failure Principle IV guards against; the skeleton has no scale pressure that would justify a read store.

Consequences

The operator stays authoritative and the estate stays answerable from one place. Go keeps the operator and the platform services in one toolchain. The cost is that anything needing durable per-app facts must either live on the CR (as status.costStatus does) or be an explicitly non-authoritative projection or log; later features re-affirmed this doctrine — cost suspension is a status condition, never a new state value, and access logs live in the gateway's store, never on the CR. Operator scope creep remains the recorded product risk, bounded by the lifecycle state machine.