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:
| Where | What | Now |
|---|---|---|
IchorInstallation.spec.installerSignature.reason, CEL "reason is required when mode is skip" (ADR 0122) | required with mode: skip | CEL rule removed; field optional, deprecated, never read |
IchorInstallation.status.installerImage.reason | the reason copied from the spec | deprecated, no longer written |
TalosOperation.spec.etcdRestore.reason, MinLength=1, required (ADR 0089, 0110) | no reason given stored when none was typed | optional (omitempty), deprecated, no longer written |
TalosOperation.spec.node.overrideReason (ADR 0110) | the override's optional reason | deprecated, never set or read; a value written by an older build still counts as an override |
TalosOperation.spec.node.breakGlassReason (ADR 0109) | the earlier name | as above |
TalosMachine.spec.approvalDecision.reason (ADR 0021) | approve and reject reason | deprecated, 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_reason | the wire side of the above | [deprecated = true], never read, never set |
Connect IchorInstallerOptions.skip_signature_reason | skip with a reason | deprecated; 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 REASON | flags | removed; --skip-signature-check is a boolean |
| UI approve, reject, node action, etcd restore and override dialogs, the skip-signature field | textareas and an input | removed, 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 Event | text that came from the field | the Events stay and say who did what; the marker has no reason |
internal/webhook settings gate: spec.installerSignature.reason | a gated setting | removed 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
- CRDs. No field is removed.
etcdRestore.reasonbecomes optional and losesMinLength=1; the CEL rule oninstallerSignatureis dropped. Each field's comment says "deprecated: unused, kept so the schema never drops a field".Overrides()keeps countingoverrideReasonandbreakGlassReasonas an override, so an operation an older build wrote keeps its gate.EffectiveOverrideReason()is deleted. - Proto. The reason fields keep their numbers and are marked deprecated; nothing sets or reads them. The one exception:
skip_signature_reasonis read as "skip", because an older CLI or UI sends it; its text is dropped. The newskip_signaturecarries the skip. - CLI. The flags above are removed without an alias (as ADR 0109 did for the earlier flag).
--overrideand--skip-signature-checkare booleans.--yes, the typed names and--confirmstay. - 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.
- Events and audit. The override Event says who asked and what, with no reason clause. The audit record's action stays (
create-override). - 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
TalosOperationorIchorInstallationwritten with a reason still validates; the reason is ignored. A TalosMachine's oldapprovalDecision.reasonstays in the object and is not shown. - Old clients, new server. An older CLI or UI that still sends
Override{reason}orskip_signature_reasonworks: 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,--reasonand--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: Skippedand theInstallerSignatureSkippedEvent 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).