0130. No free-text reason is asked for anywhere

Status: Accepted · Date: 2026-11-05 · Design section: Trust, Validation, UI · Touches: 0018, 0021, 0065, 0085, 0089, 0109, 0110, 0115, 0122

Decided: nowhere in Ichor does a person have to, or get asked to, type a reason. The user decided on 2026-11-05 that every place that asks for a reason goes. What gates a dangerous action does not change: the RBAC verbs (override, start, approve), the typed cluster or machine name, the --yes prompt, and the Events and audit records that say who did what. Only the free text goes: the CLI has no --reason and no --override-reason, --skip-signature-check is a bare boolean flag, the UI dialogs have no reason box, no CRD rule requires a reason, and no controller or backend code reads or prints text a person typed as a reason. Every reason field of a CRD or a Connect message stays (append-only) as a deprecated, unused field.

Context

ADR 0065 made the override of a safety check carry a reason, ADR 0110 made the reason optional, ADR 0021 added an optional reason to approve and reject, ADR 0089 required one on an etcd restore, and ADR 0122 required one to skip the installer image's signature check. The user asked for all of it to go. The reasons were never the gate: the verb is (ADR 0110), and a typed reason pushed people to type placeholders. The records that say who acted, when, and on what, stay; they do not need a sentence from the actor.

Inventory

A reason a person typed, before this change:

WhereWhatNow
IchorInstallation.spec.installerSignature.reason, CEL "reason is required when mode is skip" (ADR 0122)required with mode: skipCEL rule removed; field optional, deprecated, never read
IchorInstallation.status.installerImage.reasonthe reason copied from the specdeprecated, no longer written
TalosOperation.spec.etcdRestore.reason, MinLength=1, required (ADR 0089, 0110)no reason given stored when none was typedoptional (omitempty), deprecated, no longer written
TalosOperation.spec.node.overrideReason (ADR 0110)the override's optional reasondeprecated, never set or read; a value written by an older build still counts as an override
TalosOperation.spec.node.breakGlassReason (ADR 0109)the earlier nameas above
TalosMachine.spec.approvalDecision.reason (ADR 0021)approve and reject reasondeprecated, no longer written, no longer sent to clients
Connect Override.reason, ApproveMachineRequest.reason, RejectMachineRequest.reason, SetMachineInstallRequest.reason, the four discovery requests' reason, MachineApprovalDecision.reason, IchorInstallation response's installer_skip_reasonthe wire side of the above[deprecated = true], never read, never set
Connect IchorInstallerOptions.skip_signature_reasonskip with a reasondeprecated; new bool skip_signature = 3; a non-empty old value still means skip, its text is dropped
CLI --override-reason, machine approve --reason, machine reject --reason, --skip-signature-check REASONflagsremoved; --skip-signature-check is a boolean
UI approve, reject, node action, etcd restore and override dialogs, the skip-signature fieldtextareas and an inputremoved, with the i18n keys only they used; the Rejected list drops its Reason column
Override and restore Events (... under override: <reason>), the audit record's reason, the etcd restore marker's reason, the InstallerSignatureSkipped Eventtext that came from the fieldthe Events stay and say who did what; the marker has no reason
internal/webhook settings gate: spec.installerSignature.reasona gated settingremoved from the gate; mode is still gated by the version subresource

Machine-generated text is not touched: condition reason (status.conditions[].reason), status.failureReason, unavailable_reason, the reason of an Event, the cni OwnedKey.reason, the Ready reasons, the AuditRecord's reason when the backend itself adds a remark. AuditRecord.reason is kept without a deprecation mark: it is empty, or says that the Event of an override could not be recorded.

Decision

  1. CRDs. No field is removed. etcdRestore.reason becomes optional and loses MinLength=1; the CEL rule on installerSignature is dropped. Each field's comment says "deprecated: unused, kept so the schema never drops a field". Overrides() keeps counting overrideReason and breakGlassReason as an override, so an operation an older build wrote keeps its gate. EffectiveOverrideReason() is deleted.
  2. Proto. The reason fields keep their numbers and are marked deprecated; nothing sets or reads them. The one exception: skip_signature_reason is read as "skip", because an older CLI or UI sends it; its text is dropped. The new skip_signature carries the skip.
  3. CLI. The flags above are removed without an alias (as ADR 0109 did for the earlier flag). --override and --skip-signature-check are booleans. --yes, the typed names and --confirm stay.
  4. UI. The dialogs lose the reason inputs; the override checkbox, the typed name and the skip checkbox stay, with the hint that the override is recorded in an Event.
  5. Events and audit. The override Event says who asked and what, with no reason clause. The audit record's action stays (create-override).
  6. Docs. The guides, the CHANGELOG and the older ADRs (by amendment) no longer tell anyone to give a reason.

Alternatives considered

  • Keep the reason optional and only drop the requirements. Rejected: the user asked for the inputs to go, and an optional box still invites a typed placeholder.
  • Remove the fields from the CRDs and the proto. Rejected: CRD fields are append-only (installed builds, including the demo, have them), and a removed proto field number could be reused by mistake.
  • Keep the reason in the audit trail for the dangerous operations only (reset, restore, skip). Rejected: it is the same free text; the verb and the Event already record who and when.

Compatibility

  • Existing objects. A TalosOperation or IchorInstallation written with a reason still validates; the reason is ignored. A TalosMachine's old approvalDecision.reason stays in the object and is not shown.
  • Old clients, new server. An older CLI or UI that still sends Override{reason} or skip_signature_reason works: the override is the message's presence, a non-empty skip reason means skip. An older client's approve or reject with a reason is accepted and the reason ignored.
  • New clients, old server. A server built before this change refuses an etcd restore without a reason (CRD) and a skip without one. Update the server first.
  • Scripts. --override-reason, --reason and --skip-signature-check "<text>" (cobra reads the text as an argument, which the boolean flag rejects) stop working. Dropping them is the one change needed.
  • Restore marker. A restore marker written by an older controller holds a reason; the newer one ignores it.

Consequences

  • The safety of an override rests on the verb and the typed name alone, as ADR 0110 already said.
  • The Events and the audit record say who acted and when; they cannot say why.
  • status.installerImage.verification: Skipped and the InstallerSignatureSkipped Event are the record of a skipped check; the digest is in both.
  • The deprecated fields clutter the schema forever. That is the cost of append-only CRDs.

Hardware verification (later)

None of the change touches a node. A smoke check on the integrated build: ichor upgrade ichor <demo version> --installer-digest $D --skip-signature-check runs the installer Job and ichor version says "signature NOT checked"; ichor node reset NODE --override --confirm NODE is accepted by a user with the override verb and refused without it; the UI's reset and restore dialogs show the checkbox and the typed name and no reason box.

Tests

TestIchorUpgradeWritesTheInstallerSettingsAndClearsThemNextTime and the installer specs in internal/controller/ichor_upgrade_image_test.go (a skip with no reason is admitted and recorded); the etcd-restore specs create the operation with no reason; TestNodeResetOverrideRecordsNoReason, TestIchorUpgradeReadsAnOlderClientsSkipReasonAsASkip, TestSkipSignatureCheckIsABareFlag and TestOverrideIsABareFlagWithNoReason (no command has --reason or --override-reason).

Search the docs

Search pages and headings