ADR-0029: Drop v1alpha1 entirely — hard cut, no soft-migration window¶
- Status: Accepted
- Date: 2026-07-30
- Supersedes: ADR-0026 §Deprecation-policy
item 4 (
v1alpha1"stays served for at least one minor release") - Closes: ADR-0027 §Served-versions & conversion sequencing question
- Governs: Phase 8e removal slices (8e-1 onward)
Context¶
Phase 8d graduated all six kinds
(QueueManagerConnection, Queue, Topic, Channel, ChannelAuthRule,
AuthorityRecord) to messaging.mkurator.dev/v1beta1 with a hub-spoke
conversion webhook per ADR-0026. As of
2026-07-30 the CRDs ship both versions with v1beta1 as etcd storage
(config/crd/bases/messaging.mkurator.dev_*.yaml: every kind has v1beta1
served: true + storage: true; v1alpha1 served: true + storage: false).
The storage migration ADR-0026 gated on is therefore already complete.
ADR-0026 §Deprecation-policy item 4
(docs/adr/0026-v1beta1-graduation-plan.md:99-101) planned to keep v1alpha1
served for at least one minor release after v1beta1 shipped, so GitOps
repos could migrate apiVersion gradually with conversion-on-read covering
stored v1alpha1 objects. That soft-migration window presumed a productive user
base pinned to v1alpha1.
Two facts collapse that presumption:
- No productive users. MKurator has no known cluster or GitOps repo pinned
to
v1alpha1; the operator (2026-07-29) confirmed a hard cut with no users to protect. Keeping a served spoke buys migration safety nobody needs while carrying real cost (below). - The conversion spoke is a proven data-loss surface. The union-auth e2e
went red because a conversion-webhook round trip through the
v1alpha1spoke wipedspec.authenticationon finalizer-add — a data-loss bug, not a race (fixed in PR #168, commitd744a6f, "preserve authentication union across v1alpha1 spoke round trip"). Every field added tov1beta1thatv1alpha1cannot represent reopens this class of bug for as long as the spoke is served.
This ADR is a governance record: it closes the deprecation-window plan and the conversion-sequencing question so the 8e removal slices do not silently contradict ADR-0026, and the sequencing constraint ADR-0027 left open is formally resolved. 8e-0 is docs-only; no CRD or code changes here — those land in 8e-1 onward.
Decision¶
We will remove messaging.mkurator.dev/v1alpha1 entirely — a hard cut with
no soft-migration window. Removal deletes the v1alpha1 API types, its CRD
spec.versions entry for all six kinds, and the hub-spoke conversion webhook
(v1beta1 becomes the single served and stored version).
Supersession of ADR-0026 §Deprecation-policy item 4¶
ADR-0026 item 4 ("v1alpha1 stays served for
at least one minor release … conversion on read handles stored v1alpha1
objects") is superseded. Rationale: no productive users exist to migrate
gradually, and the served spoke is a live data-loss surface (PR #168). The rest
of ADR-0026 (the graduation plan, conversion scope, and spec.attributes
deprecation policy items 1–3) stands unchanged — only the served-window
commitment in item 4 is withdrawn.
Closing the ADR-0027 conversion-sequencing question¶
ADR-0027 §Served-versions & conversion
(docs/adr/0027-mqweb-authentication-modes.md:88-98) made the auth-union type
change a hard prerequisite: no auth-union change may land while v1alpha1 is
storage, because a v1beta1-only union makes v1beta1 → v1alpha1 down-conversion
lossy.
- Constraint satisfied.
v1beta1is already etcd storage on all six kinds (verified above), and the auth union shipped on it. The prerequisite was honoured. - Now moot. Removing
v1alpha1deletes the down-conversion direction altogether, so the losslessness concern that motivated the sequencing no longer exists. - Rejected mitigation stays rejected. ADR-0027 option 3 — "preserve via
conversion annotations on the
v1alpha1spoke" — must NOT be reintroduced by any removal slice. It was rejected as fragile; the hard cut removes the spoke it would have annotated, so there is nothing left to mitigate.
Migration instruction for readers¶
A reader holding an existing v1alpha1 manifest migrates by rewriting the
apiVersion from messaging.mkurator.dev/v1alpha1 to
messaging.mkurator.dev/v1beta1. On the first v1beta1 cut the spec is
identical to v1alpha1 (ADR-0026: "spec/status shapes on v1beta1 mirror
v1alpha1, apiVersion bump only"), so no field edits are needed for manifests
that predate any v1beta1-only field.
Consequence — no conversion on read after removal. Once v1alpha1 is gone
from spec.versions, the API server can no longer convert a stored v1alpha1
object on read: any latent object still persisted as v1alpha1 becomes
unreadable / unservable, not silently upgraded. This is why the precondition
below is mandatory, not advisory.
Precondition — stored-version cleanliness (operator decision C, 2026-07-30)¶
Per the operator's assert-clean, no stored-version migration decision, the hard cut carries a hard precondition:
- Removing
v1alpha1from a CRD'sspec.versionsrequires that the CRD'sstatus.storedVersionsno longer listv1alpha1. The API server refuses to drop a version still named instoredVersions. - The hard cut assumes no target cluster has any object stored as
v1alpha1. This assumption is verified by a guard e2e in 8e-8 (a cluster with av1alpha1-stored object must fail the guard, not silently lose data). - Any cluster that ran ≤ v0.12 (before
v1beta1became storage) may still holdv1alpha1-stored objects. Such a cluster must first complete the stored-object rewrite +storedVersionsprune documented indocs/UPGRADE.md— read every object and re-persist it underv1beta1(e.g.kubectl get … -o yaml | kubectl apply -f -or a storage- version migrator), then patchstatus.storedVersionsto[v1beta1]— before upgrading to the release that removesv1alpha1.
This precondition is documentation only in 8e-0; the UPGRADE.md prose and the guard e2e are separate slices (UPGRADE prose in 8e-9, guard e2e in 8e-8).
Consequences¶
- The conversion-webhook data-loss surface (PR #168 class) is eliminated — no served spoke means no lossy round trip to guard against; per-version CEL duplication (ADR-0026 §Consequences) also goes away.
- Upgrades become gated, not gradual: an operator must land on a
v1beta1- stored,v1alpha1-pruned cluster before taking the removal release. A cluster that skips the precondition and still storesv1alpha1will find those objects unreadable after upgrade — hence the 8e-8 guard e2e and the UPGRADE.md runbook (8e-9). - GitOps repos still pinned to
apiVersion: …/v1alpha1break at admission after removal (the version no longer exists) — acceptable given no productive users; the one-lineapiVersionrewrite above is the whole migration. docs/API_STABILITY.mdanddocs/UPGRADE.mdneed removal-cut prose; that is out of scope for 8e-0 (owned by 8e-9). This ADR only records the decision and preconditions.- Follow-on removal slices (8e-1…8e-10) may now delete
api/v1alpha1, the conversion webhook wiring, and thev1alpha1CRDspec.versionsentries without contradicting ADR-0026 or ADR-0027.
Alternatives considered¶
- Soft / gradual deprecation window (keep
v1alpha1served one minor release per original ADR-0026 item 4) — rejected. It protects a migration path no productive user needs and keeps the conversion spoke — the exact surface that produced the red union-auth e2e / PR #168 data-loss bug — alive for another release. The trigger for this ADR is precisely that this window is all cost, no benefit here. - Keep both versions served indefinitely (no removal) — rejected. Permanently duplicates CEL and conversion code, and permanently carries the down-conversion losslessness burden ADR-0027 sequenced around. Convention favours retiring the alpha once the beta is storage and unused.
- Automated stored-object migration on upgrade (operator ships a migrator that
rewrites
v1alpha1-stored objects transparently) — rejected for 8e per operator decision C: assert-clean instead. With no productive users, a guard e2e (8e-8) that fails loudly on anyv1alpha1-stored object plus a manual UPGRADE.md runbook is simpler and safer than shipping and maintaining migrator code. - Reintroduce ADR-0027 option 3 (conversion annotations on the
v1alpha1spoke) — rejected then and rejected now; the hard cut removes the spoke, so the mitigation has nothing to attach to. Recorded here so no removal slice revives it.
References¶
- ADR-0026 —
v1beta1graduation plan; §Deprecation-policy item 4 superseded by this ADR - ADR-0027 — mqweb auth modes; §Served-versions & conversion sequencing closed by this ADR
- ADR-0009 — validating-admission posture
- PR #168 (
d744a6f) — auth-union spoke round-trip data-loss fix (the trigger) docs/UPGRADE.md— stored-object rewrite +storedVersionsprune runbook (populated in 8e-9)docs/API_STABILITY.md— API stability & deprecation policy (removal-cut prose in 8e-9)