Files
zeekay 51460827d4 Merge the forge's lux/universe into GitHub's, the one main from here
The two mains shared no history, so this is a per-file three-way merge:
for each file the 65 forge-only commits touched (by patch-id), the base is
the newest version both histories held. 105 files merged or took one side
cleanly, 48 were already identical, and the 45 true conflicts and 31
modify/delete pairs were resolved by what runs on the cluster:

- The forge wins where it is live: explorer 1.5.56 (digest 45db11ff) and
  its archive readers, explore-fe-hanzo's ConfigMap env (AI coin,
  /v1/indexer/hanzo, /v1/chain/hanzo/rpc), exchange-api reading
  luxd-archive, and explore-fe-{lux,zoo}-testnet, pinned to what they run
  (v2.8.5-lux / v2.8.4-lux).
- GitHub wins where it is live: explore-fe-lux v2.8.5-lux, explore-fe-zoo
  v2.8.4-lux, bankd v1.0.18, lux-build's routes.
- The forge's retirement of workloads that run nowhere stands: lux-mainnet
  dex, lux-hb2, lux-market, traffic, wallet-web; the lux-testnet frontends,
  indexer, exchange and gateways; lux-operator. deploy/lux-testnet/luxd.yaml
  stays, since roll_luxd.sh reads it.
- GitHub's deletions stand: lux-finance and lux-financial (static sites
  now), the k8s/ operator manifests, .hanzo/workflows/cicd.yml.
- Paths are /v1/chain/C/rpc where the forge had written /v1/chain/c. All
  four spellings answer; this one is the documented form. The forge's
  /{network}/v1 doc lines are dropped: a network is a host, and the prefix
  404s.

Dry-run against the running objects (server-side, as CD applies), the
merged explorer shows no change where GitHub's showed 152 lines, and no
other live workload moves further from what runs. Every deploy/*/*.yaml
renders with charts/app; price.yaml, which failed on GitHub, now renders.

Claude-Session: https://claude.ai/code/session_01TzirYkGmD88w4GipqivUtV
2026-09-23 20:20:03 -07:00
..

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 LuxNetwork copies lossy (dropped tenantImports, renamed pluginSource.vmPlugins[].vmID → vmId which the operator Go struct does not read — tag is vmID),
  • 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/Ready values are frozen literals — every legacy LuxNetwork has observedGeneration: null and empty conditions while metadata.generation is 22–29. Nothing reconciled them; nothing keeps them Ready.
  • The live luxd StatefulSets (5/5, 113d) and the api-gateway workloads run independently of any reconcile loop. Applying corrected desired-state CRs therefore touches no live workload.
  • A live status.phase == Ready on 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 the lux.network/migration-status annotation. 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)

  1. Captured BEFORE state → before/.
  2. Built corrected LuxNetwork.lux.cloud manifests = full legacy spec (production source of truth) + additive forward fields from the prior edit. → manifests/network-*.yaml.
  3. kubectl apply --server-side --force-conflicts --field-manager=crd-group-migration -f manifests/network-*.yaml (adopted the fields owned by the prior kubectl-client-side-apply/kubectl-patch managers). Dormant operator ⇒ no live workload change.
  4. 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.
  5. Annotated new-group CRs: lux.network/migrated-from, lux.network/migration-date.
  6. Annotated legacy CRs: lux.network/superseded-by, lux.network/migration-status (records deferred Ready-check), lux.network/migration-date. Specs untouched — legacy metadata.generation unchanged (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:

  1. Confirm each luxnetworks.lux.cloud/luxd reaches status.phase: Ready and stays Ready across one reconcile + one rollout. Same for gateways/mpc. Decide whether the 11 chains.lux.cloud should reconcile or be retired.
  2. Only then kubectl delete the legacy lux.network CRs (they carry superseded-by) and finally the legacy CRDs (luxnetworks/luxgateways/luxchains/luxmpcs.lux.network).
  3. Do not delete before the Ready-persistence check — that is the CTO mitigation this runbook is built around.