Skip to content
Decisions

Decisions

Architecture decision records for the choices that would otherwise need re-litigating every time someone asks “why not X instead?” — what was chosen, what it was chosen over, and the trade-off that made the difference.

An ADR records the “why” behind a decision, not just the “what”: the context that forced a choice, the options considered, and the consequences accepted. Specs MUST comply with the platform constitution and MAY reference an ADR for context on a technology choice; a constitution amendment requires an ADR to document the change.

The principles behind the picks

Prefer the boring option, except where boring means unsupported. Most choices here are conventional. The exceptions are deliberate, and each has a record below.

Prefer an operator with a CRD to a Helm chart with a values file. A CRD is an API that other things can compose against; a values file is a configuration blob that only its own chart understands.

Prefer open source without a licence cliff. Several records below are about avoiding a rug-pull rather than about technical merit.

Pay for a choice once. Where a component is load-bearing it is worth being deliberate; where it is replaceable it is not worth agonising over.

When a choice needs a record

A technology choice with a rejected alternative requires an ADR before merge. If you can name what it was chosen over, write the record. If nothing credible competed, it is an installation and not a decision — say so in the pull request rather than leaving it unsaid. Version bumps, chart-value changes and single-file fixes never need one.

The records

ADRTitleStatusDate
0001Use KCL for Crossplane CompositionsAccepted2024-09-29
0002Use EKS Pod Identity over IRSAAccepted2024-04-15
0003Use vLLM Production Stack over KServe + llm-d for v1 LLM PlatformAccepted2026-04-30
0004Use Amazon S3 Files for LLM Model Weights StorageAccepted2026-05-01
0005GKE Standard with self-managed Cilium (not Dataplane V2, not Autopilot)Accepted2026-08-18
0006GKE node auto-provisioning (ComputeClass) over Karpenter on GCPAccepted2026-08-18
0007Cloud abstraction boundaries — cloud-shaped platform APIs, neutral developer APIsAccepted2026-08-18
0008Use Flux for GitOps reconciliationAccepted2026-08-21
0009Use Cilium instead of the AWS VPC CNIAccepted2026-08-21
0010Use VictoriaMetrics rather than PrometheusAccepted2026-08-21
0011Use OpenBao rather than HashiCorp VaultAccepted2026-08-21
0012Use Crossplane and OpenTofu, split at the Kubernetes boundaryAccepted2026-08-21
0013Use Tailscale for private access rather than a bastionAccepted2026-08-21
0014Use OpenTofu rather than TerraformAccepted2026-08-21
0015Use Gateway API rather than IngressAccepted2026-08-21
0016Use Kyverno for admission policyAccepted2026-08-21
0017Multi-cloud DNS naming — cloud-agnostic public, cloud-pinned privateAccepted2026-08-23
0018Per-cloud OpenTofu state — GCP state in GCS, AWS state in S3Accepted2026-08-25
0019Cross-cloud DNS federation — GKE workloads assume an AWS role for Route53Accepted2026-08-25
0020Harbor on GCS — native driver with Workload Identity, not S3-compatible HMACAccepted2026-08-26
0021Cloud Storage FUSE for LLM model weights on GCPAccepted2026-08-26
0022One identity provider across both clouds, hosted on AWS and named by a variableSuperseded by 00242026-08-27
0023Secret store keys use a name grammar both clouds acceptAccepted2026-08-27
0024The identity provider is deployable on either cloud, defaulting to AWSAccepted2026-08-27
0025Cloud-managed secret stores as the store of record, OpenBao scoped to the PKIAccepted2026-08-27
0026Headlamp authenticates behind an auth proxy on GKE, not against the clusterSuperseded by 00322026-08-28
0027AWS is the primary cloud, and cross-cloud singletons live thereAccepted2026-08-29
0028Harbor’s OIDC config is set declaratively via CONFIG_OVERWRITE_JSON, not a post-install scriptAccepted2026-08-29
0029RunLore and Slack over Grafana OnCallAccepted2026-08-30
0030Vector as the log shipperAccepted2026-08-30
0031Per-cluster observability panes; Slack and RunLore are the pagerAccepted2026-08-30
0032Workforce Identity Federation restores per-user Kubernetes RBAC on GKEAccepted2026-09-02
0033OpenBao is the store of record, durable as a snapshot lineage, active on the primary cloud with restore-based fallbackAccepted2026-09-02
0034Human access to OpenBao is ZITADEL OIDC, authorised by project roles, with userpass kept as break-glassAccepted2026-09-05
0035An in-house Headlamp plugin gives the App abstraction its own viewAccepted2026-09-09
0036An app’s secrets are owned by its own ZITADEL group, and the policy for each app is generated rather than templatedAccepted2026-09-10
0037Slack notifications are rendered by Alertmanager’s own templates, not by a Block Kit bridgeAccepted2026-09-12
0038Agent instructions and skills are authored once in the open formats, with the Claude-specific paths as symlinksAccepted2026-09-17
0039go-task is the entry point to the scripts, locally and in CI, and no script depends on itAccepted2026-09-17
0040Vendor kubernetes-event-exporter as plain manifests instead of a Helm chartAccepted2026-09-21

Starting a new one? Copy the template.