Every provenance line and manifest still named a DigitalOcean context, and the ARC values described a pool that died with it. A cluster gets a name here; the address it answers on belongs to the private record. Co-authored-by: Hanzo Dev <[email protected]>
6.2 KiB
CRD group finalization: lux.network/Lux* → lux.cloud/*
Runbook + recorded applies for finishing the half-done CRD group migration on
lux-k8s. This is the gate that must close before Wave 1C
(luxfi/operator shim conversion). It is an operational runbook, not a session
summary.
Why
A prior session began moving the live operator CRs off the legacy group
lux.network (kinds LuxNetwork/LuxGateway/LuxChain/LuxMPC) onto the
canonical group lux.cloud. It applied new-group copies but:
- left the
LuxNetworkcopies lossy (droppedtenantImports, renamedpluginSource.vmPlugins[].vmID→vmIdwhich the operator Go struct does not read — tag isvmID), - never annotated provenance on either side,
- never confirmed parity.
If Wave 1C strips luxfi/operator to a shim owning only lux.cloud/*, any
legacy CR without a faithful lux.cloud equivalent orphans. This runbook makes
every legacy CR faithfully present on lux.cloud and records the provenance
annotations so the legacy CRs/CRDs can be deleted later under one more check.
Reconciliation reality at migration time
lux-system/lux-operator is 0/0 replicas (dormant). No
controller-manager pod runs for either group. Consequence:
- The legacy
phase: Running/Readyvalues are frozen literals — every legacyLuxNetworkhasobservedGeneration: nulland emptyconditionswhilemetadata.generationis 22–29. Nothing reconciled them; nothing keeps them Ready. - The live
luxdStatefulSets (5/5, 113d) and theapi-gatewayworkloads run independently of any reconcile loop. Applying corrected desired-state CRs therefore touches no live workload. - A live
status.phase == Readyon the new-group CRs is not obtainable until Wave 1C activates the shim operator. The verification gate used here is spec parity (every legacy field present on the new-group CR with matching value), and live-Ready confirmation is explicitly deferred to Wave 1C via thelux.network/migration-statusannotation. No Ready value was fabricated.
Canonical-kind decision
luxfi/operator go/api/v1/luxnetwork_types.go:642 — LuxNetwork is the
canonical Kind and its controller (network_lux.go, For(&LuxNetwork{}))
owns the luxd StatefulSet + EVM plugin lifecycle. Network
(plural networks, network.go) is a separate unified kind, empty in
cluster, not the luxd driver. So the migration target for the live luxd CRs
is luxnetworks.lux.cloud (kind LuxNetwork), not networks.lux.cloud.
Both CRDs were created together in the 2026-06-02 "29-kind union" regeneration.
Inventory (BEFORE → AFTER)
| Family (legacy → new) | Count | Legacy phase (frozen) | Migration |
|---|---|---|---|
luxnetworks.lux.network → luxnetworks.lux.cloud |
3 | Running | re-applied corrected (restored tenantImports, fixed vmID, kept forward exportSchedules/quasarTimestamp/v1.30.3/adoptExisting) |
luxgateways.lux.network → gateways.lux.cloud |
3 | Ready | already byte-identical; annotated |
luxchains.lux.network → chains.lux.cloud |
11 | Pending (never reconciled) | already byte-identical; annotated |
luxmpcs.lux.network → mpcs.lux.cloud |
1 | (no status) | already migrated (image string→struct, lossless); annotated |
luxindexers/luxexplorers legacy CRDs exist but hold zero CRs — nothing
to migrate. luxsubnets has no CRD on this cluster.
The 11 LuxChain "Pending"
They are declarative shadows of plugins the luxd StatefulSet already
serves (aivm, bridgevm, dexvm, graphvm, identityvm, keyvm, thresholdvm,
oraclevm, quantumvm, relayvm, zkvm — one per pluginSource.vmPlugins entry).
Created 2026-04-30 after the operator was already dormant, so they were never
reconciled (observedGeneration: null, no owner refs, empty conditions). They
drive no live workload. They are NOT controller-orphans (the controller was
never up for them). Resolution: faithfully present on chains.lux.cloud (specs
byte-identical) and annotated. Not stale, not deleted — kept until Wave 1C can
reconcile or formally retire them.
Actions taken (recorded)
- Captured BEFORE state →
before/. - Built corrected
LuxNetwork.lux.cloudmanifests = full legacy spec (production source of truth) + additive forward fields from the prior edit. →manifests/network-*.yaml. kubectl apply --server-side --force-conflicts --field-manager=crd-group-migration -f manifests/network-*.yaml(adopted the fields owned by the priorkubectl-client-side-apply/kubectl-patchmanagers). Dormant operator ⇒ no live workload change.- Verified parity: 18/18 CRs — every legacy field present on the new-group CR with matching value (mpc image string↔struct ignored as documented forward migration). Gate passed before annotating legacy.
- Annotated new-group CRs:
lux.network/migrated-from,lux.network/migration-date. - Annotated legacy CRs:
lux.network/superseded-by,lux.network/migration-status(records deferred Ready-check),lux.network/migration-date. Specs untouched — legacymetadata.generationunchanged (27/22/29).
Reproduce / re-apply
ctx=lux-k8s
kubectl --context $ctx apply --server-side --force-conflicts \
--field-manager=crd-group-migration -f manifests/network-lux-mainnet.yaml \
-f manifests/network-lux-testnet.yaml -f manifests/network-lux-devnet.yaml
The gateway/chain/mpc manifests in manifests/ are the live new-group objects
as applied by the prior session (cleaned of server fields); they are recorded
for the audit trail and are idempotent to re-apply.
NEXT — Wave 1C cutover
When the shim operator (lux.cloud → bootno.de) is live:
- Confirm each
luxnetworks.lux.cloud/luxdreachesstatus.phase: Readyand stays Ready across one reconcile + one rollout. Same for gateways/mpc. Decide whether the 11chains.lux.cloudshould reconcile or be retired. - Only then
kubectl deletethe legacylux.networkCRs (they carrysuperseded-by) and finally the legacy CRDs (luxnetworks/luxgateways/luxchains/luxmpcs.lux.network). - Do not delete before the Ready-persistence check — that is the CTO mitigation this runbook is built around.