Significant architectural decisions are recorded here, one file per decision, numbered in order of acceptance. A decision is "significant" when reversing it later would be expensive: protocol shape, storage model, security boundaries, public contracts.
The rule: no decision of this class ships undocumented. The ADR lands in the same PR as the code, and may be written either before it (when the decision needs settling first) or from the finished implementation (when building is what reveals the right shape). What is never acceptable is a merged change of this class with no ADR, or an ADR that describes something other than what shipped.
Files are named NNNN-short-title.md and follow a three-section template:
# NNNN. Title
Date: YYYY-MM-DD
Status: Accepted | Superseded by NNNN
Amends: NNNN, NNNN §5 # optional — decisions this one changes
Amended by: NNNN # optional — later decisions that changed this one
## Context
What situation forces a decision. Constraints, not opinions.
## Decision
What we chose, stated in one place, unambiguously.
## Consequences
What becomes easier, what becomes harder, what we gave up.The body is immutable once accepted; the header is maintained. Context,
Decision and Consequences record what was decided when it was decided and are
never edited. A change of mind produces a new ADR: a whole replacement gives the
old file Status: Superseded by NNNN, a partial change declares Amends: in the
new file and adds Amended by: to the old one.
The distinction is that the two reference lines carry information that did not exist when the file was written, and that its author could not have written. A reader who opens an old ADR must be able to see, in that file, that the ground has moved — which is precisely what nine slices of one-directional references failed to give them (ADR 0022).
Status: is a closed vocabulary: Accepted or Superseded by NNNN, nothing
else. Partial amendment goes in Amends:, never in prose appended to the status.
docs/adr/adr_test.go enforces all of it — the graph must be symmetric, point
backwards, name only ADRs that exist, and every ADR must appear in the index
below. Forgetting the mirror line fails make test, and the failure prints the
line to paste.
- 0001. Agents are never controlled from the server
- 0002. Wire format: protobuf over plain HTTPS POST
- 0003. Local storage: spool files and a checkpoint, no embedded database
- 0004. Resource units are the truth; dollars are rendering
- 0005. Two-tier credentials with in-cluster key generation
- 0006. Usage collection: kubelet counters, mergeable windows, snapshot delivery
- 0007. Durability is optional: snapshot delivery bounds loss, disk is a knob
- 0008. Agent identity lives in a namespaced Secret — the product's one write grant
- 0009. The node role: an on-node Go-binary scanner with zero API access
- 0010. Node → controller inventory channel: node-initiated HTTP, projected-token auth, local JWKS validation
- 0011. On-node eBPF CPU profiling: embedded profiler, node-side symbolization, config-bounded targeting query
- 0012. Payload registry, provenance discriminator, and metadata delivery
- 0013. Observation completeness: measured payloads carry what the agent saw
- 0014. Facts of a different scope than the record's key are nested, not flattened
- 0015. The node asks the controller which pods it may scan, and fails closed
- 0016. Prometheus is not a data source — not as an option, not for backfill
- 0017. Immutable build facts ship once under the image digest; snapshots say when they were taken
- 0018. The Go inventory forgets: a record lives only while its workload does
- 0019. Build settings ship by allow-list, and the payload that carries them is named for the build
- 0020. The restart journal: windowed aggregates, exact counts, sampled reasons
- 0021. Pod lifecycle: the scheduler's reason goes where the shortfall already is, the cluster's removals become a journal
- 0022. The payload registry lives in code, and an amendment cannot stay silent
- 0023. A profile's key names the build; its provenance names the claim
- 0024.
security.mdstates what is true; the decision records state why - 0025. The node's configuration holds only what the node can enforce
- 0026. The controller needs no persistent volume, and therefore no StatefulSet
- 0027. No payload carries an ordering field: the spool holds one version of a key
- 0028. The opt-out annotation works on the workload, and the workload step fails open
- 0029. Finished Job runs ship as a windowed journal, and are not baselined
- 0030. Deployment revisions ship as current state, scoped by the pods already admitted
- 0031. Placement constraints are collected, reduced rather than copied
- 0032. What bounds a workload from outside its own spec, and the first widening of read access
- 0033. A permission the agent was not given degrades one payload, and says so
- 0034. The restart counter is shipped as a reading, so the day-one history is not lost
- 0035. A cache that stopped being fed is noticed: gating caches stop the agent, the rest degrade their payload
- 0036. The Helm chart is the only installer, and the install profile is the only switch
- 0037. The image runs as uid 65532; root is one role's exception, asked for by name
- 0038. Vulnerabilities are gated on reachability, not on the module graph
- 0039. A stated limit is a measured limit, or it is marked
- 0040. The channel authenticates a node, not just the node role
- 0041. The symbol allow-list bounds dependencies, not the customer's own code; source paths do not ship
- 0042. The spool is bounded, and its bound does not depend on the cluster being healthy
- 0043. Both halves of the counter reading come from one observation
- 0044. One fact in one place, and the places have ceilings
- 0045. A kubelet read is bounded in time and in size, and one node's silence costs one node
- 0046. The informer cache is the source, so the fields the agent promises not to collect are removed there
- 0047. Five environment variables are named, kept and shipped; every other one is still dropped
- 0048. Four first-hour findings name fields the agent did not collect
- 0049. Revisions belong to whatever controls the ReplicaSet, and the payload is renamed for it
- 0050. The GOMAXPROCS finding rests on a build fact, not on the toolchain version
- 0051. Topology-aware routing is a request the cluster can silently decline
- 0052. The peak the kernel remembers, and the second file the scanner opens
- 0053. The network counters are the pod's, and they say nothing about where the bytes went
- 0054. Coverage says how much was hidden, never what
- 0055. The installation is cluster-wide, and there is no narrower mode
- 0056. Whether a pprof endpoint exists is proved on the node, not probed over the network
- 0057. The controller confirms an endpoint once per build, and that is its first connection to a workload
- 0058. The pull starts a profiler, and the binary says whose code it is
- 0059. The node knows whose code it profiles, so the allow-list stops being a prerequisite
- 0060. The node says what its profiler did, on the channel that fires when it did nothing
- 0061. What the process holds, beside the peak it once reached
- 0062. The counters are subtracted on the node, and never leave without their interval
- 0063. A profile the collector dominates is still a profile
- 0064. The node is described, and its comings and goings recorded
- 0065. The vocabulary of a collected fact is a package, and it does not know Kubernetes
- 0066. An unverified caller does not set the refresh rate, and a value outside the enumeration does not load
- 0067. A record carries the instant its source last stated it
- 0068. The agent yields first, and two objects it does not ship
- 0069. Liveness is the process; readiness is what it has collected
- 0070. The agent says what it did, in the format the cluster already reads
- 0071. A workload can refuse the profiler without refusing everything else
- 0072. A restart resumes the open window the spool already holds
- 0073. A payload kind declares how often it is sent, and the ceiling is what makes that safe
- 0074. The payload schema is written for three kinds, and presence is declared field by field
- 0075. A payload leaves the spool only when the backend has it
- 0076. The payload travels encoded, and the encoding is not what it is
- 0077. A restart resumes every open window, not only the usage one
- 0078. A journal window says whether it is finished
- 0079. The last pass writes, and then ships what it wrote
- 0080. The index page already counts the goroutines
- 0081. A weakened security default is a build fact like any other
- 0082. A confirmed endpoint is named on the port it answered on
- 0083. The agent names itself in the request it makes
- 0084. What was excluded is named, inside the cluster and only there
- 0085. The image is built and scanned where the code is, and pinned to what it was built from
- 0086. A toolchain move is a review of what the toolchain reports
- 0087. A state says when it began
- 0088. A window says how long a state held
- 0089. A window says what changed