explorer.testnet.lux.network (502), explorer.devnet.lux.network (404) and both
per-env bridge UIs (TLS handshake failure) are now 200 and render their own app,
verified through a real browser, not a status code.
ROOT CAUSE 1 — `certResolver: letsencrypt` is a no-op on this cluster.
The lux-system hanzo-ingress DaemonSet is started with no
--certificatesresolvers.* argument, so the named resolver does not exist and
every host whose only TLS source was a certResolver was served the self-signed
"INGRESS DEFAULT CERT":
openssl s_client -servername bridge.lux-dev.network
subject=CN=INGRESS DEFAULT CERT issuer=CN=INGRESS DEFAULT CERT
The four bridge hosts had a correct IngressRoute and a healthy 1/1 backend the
whole time — `curl -k` returned 200 and <title>Bridge</title> — so every probe
that skipped verification called them fine. cert-manager (ClusterIssuer
letsencrypt-prod, Cloudflare DNS-01) is the one mechanism here that issues real
certificates, and IngressRoute tls.secretName consumes it (precedent:
lux-ns/exchange-proxy). So: one Certificate per route, tls.secretName on the
route, certResolver deleted.
subject=CN=bridge.lux-test.network issuer=C=US, O=Let's Encrypt, CN=YR2
ROOT CAUSE 2 — explorer.{testnet,devnet}.lux.network were routed to the API.
Neither host appeared in any IngressRoute. Their only route was the legacy k8s
Ingress lux-explorer/explorer-{testnet,devnet} -> ExternalName
explorer-{testnet,devnet}-v2 -> explorer.lux-{testnet,devnet}.svc:8090 — the Go
indexer, which has no `/` handler. Devnet therefore 404'd with the app 1/1 and
"routing fine"; testnet 502'd because that Service additionally had zero
endpoints. Mainnet looked healthy only because a SECOND route,
lux-mainnet/lux-explorer-mainnet, claims explorer.lux.network for the frontend
and outranks the Ingress.
Both hosts are added to their network's existing IngressRoute — the same
frontend-plus-data-path shape mainnet already uses — and the two legacy
Ingresses are deleted. One host, one route, all three networks identical.
ROOT CAUSE 3 — lux-testnet explorer + explore-fe-lux were scaled to 0.
git declared replicas: 1 for explorer and said nothing at all about
explore-fe-lux, so nothing put either back. Scaled to 1; both reached Ready on
the first attempt with no restarts, so the zero was drift, not a crashloop
being papered over. explore-fe.yaml commits the frontend (and its
explore-env-lux ConfigMap) so this cannot silently recur.
Also certified the api-explore.* hosts on the same two routes: they answered
`curl -k .../health` 200 but failed verification for the same reason, and
leaving them out would have been a half-fix.
Rendered, not just 200 (Playwright, page inner text):
explorer.testnet.lux.network "LUX TESTNET Total blocks 981 Total
transactions 971 Wallet addresses 44"
explorer.devnet.lux.network "LUX DEVNET Total blocks 174 Total
transactions 173 Wallet addresses 10"
bridge.lux-test.network "Teleport / From / Source / Connect Wallet"
bridge.lux-dev.network same
No regression: bridge.lux.network, bridge-api/zoo/pars, explore.lux.network,
explorer.lux.network, faucet.lux-test.network all still 200 with valid TLS.
All five files are `kubectl diff -f` clean against live, and lux-testnet's
kustomization now tracks ingress.yaml (its exclusion note described a strip-csp
middleware the live object does not reference) plus the new explore-fe.yaml.
Known, NOT fixed here: the zoo/hanzo/pars devnet+testnet explorer routes carry
the same dead certResolver and their hosts still fail TLS; api-explore.lux-dev.network
is claimed by two routes and the losing one serves it.
Co-authored-by: Hanzo Dev <[email protected]>
162 lines
5.5 KiB
YAML
162 lines
5.5 KiB
YAML
---
|
|
###############################################################################
|
|
# Per-env Bridge UI (testnet + devnet) — frontend layer.
|
|
#
|
|
# The bridge FRONTEND (Next.js, ghcr.io/luxfi/bridge-ui) is deployed per env
|
|
# pointed at that env's chain. Cross-chain bridging additionally needs a
|
|
# per-env BACKEND (bridge-server + MPC cluster + bridge contracts) — those run
|
|
# only for mainnet today (bridge-api.lux.network → mainnet MPC mpc.lux.network).
|
|
# So testnet/devnet bridge UIs load + connect wallets to the testnet/devnet
|
|
# chains, but cross-chain transfers require the per-env backend to be stood up.
|
|
#
|
|
# Hosts (canonical scheme, matching explorers):
|
|
# bridge.lux-test.network (testnet) bridge.lux-dev.network (devnet)
|
|
###############################################################################
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bridge-ui-testnet
|
|
namespace: lux-bridge
|
|
labels: { app: bridge-ui-testnet, env: testnet }
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels: { app: bridge-ui-testnet }
|
|
template:
|
|
metadata:
|
|
labels: { app: bridge-ui-testnet, env: testnet }
|
|
spec:
|
|
containers:
|
|
- name: bridge-ui
|
|
image: ghcr.io/luxfi/bridge-ui@sha256:71e0bbe79a37613887748eae2fec3a0c0caae50cded0eec29439a32c12e6f9d4
|
|
imagePullPolicy: IfNotPresent
|
|
ports: [{ containerPort: 3000, name: http }]
|
|
env:
|
|
- { name: NEXT_PUBLIC_NETWORK_NAME, value: "testnet" }
|
|
# Per-env bridge backend not yet deployed — points at mainnet API as a
|
|
# placeholder so the UI renders; cross-chain calls require a testnet
|
|
# bridge-server. TODO: stand up bridge-api.lux-test.network.
|
|
- { name: NEXT_PUBLIC_BRIDGE_API, value: "https://bridge-api.lux.network" }
|
|
- { name: NEXT_PUBLIC_BACKEND_API, value: "https://bridge-api.lux.network" }
|
|
- { name: NEXT_PUBLIC_IDENTITY_API, value: "https://bridge-api.lux.network" }
|
|
- { name: NEXT_PUBLIC_EXPLORER_API, value: "https://api-explore.lux-test.network" }
|
|
resources:
|
|
requests: { cpu: "100m", memory: "128Mi" }
|
|
limits: { cpu: "500m", memory: "512Mi" }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: bridge-ui-testnet
|
|
namespace: lux-bridge
|
|
labels: { app: bridge-ui-testnet }
|
|
spec:
|
|
type: ClusterIP
|
|
selector: { app: bridge-ui-testnet }
|
|
ports: [{ name: http, port: 3000, targetPort: http }]
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bridge-ui-devnet
|
|
namespace: lux-bridge
|
|
labels: { app: bridge-ui-devnet, env: devnet }
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels: { app: bridge-ui-devnet }
|
|
template:
|
|
metadata:
|
|
labels: { app: bridge-ui-devnet, env: devnet }
|
|
spec:
|
|
containers:
|
|
- name: bridge-ui
|
|
image: ghcr.io/luxfi/bridge-ui@sha256:71e0bbe79a37613887748eae2fec3a0c0caae50cded0eec29439a32c12e6f9d4
|
|
imagePullPolicy: IfNotPresent
|
|
ports: [{ containerPort: 3000, name: http }]
|
|
env:
|
|
- { name: NEXT_PUBLIC_NETWORK_NAME, value: "devnet" }
|
|
- { name: NEXT_PUBLIC_BRIDGE_API, value: "https://bridge-api.lux.network" }
|
|
- { name: NEXT_PUBLIC_BACKEND_API, value: "https://bridge-api.lux.network" }
|
|
- { name: NEXT_PUBLIC_IDENTITY_API, value: "https://bridge-api.lux.network" }
|
|
- { name: NEXT_PUBLIC_EXPLORER_API, value: "https://api-explore.lux-dev.network" }
|
|
resources:
|
|
requests: { cpu: "100m", memory: "128Mi" }
|
|
limits: { cpu: "500m", memory: "512Mi" }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: bridge-ui-devnet
|
|
namespace: lux-bridge
|
|
labels: { app: bridge-ui-devnet }
|
|
spec:
|
|
type: ClusterIP
|
|
selector: { app: bridge-ui-devnet }
|
|
ports: [{ name: http, port: 3000, targetPort: http }]
|
|
---
|
|
# TLS. `certResolver: letsencrypt` was a NO-OP here and served every one of
|
|
# these four hosts the self-signed "INGRESS DEFAULT CERT" — the lux-system
|
|
# hanzo-ingress DaemonSet is started with no --certificatesresolvers.* args at
|
|
# all, so the named resolver does not exist. Every host on this cluster that
|
|
# actually presents a real certificate gets it from cert-manager. So do these:
|
|
# one Certificate per env, referenced by the route's tls.secretName.
|
|
apiVersion: cert-manager.io/v1
|
|
kind: Certificate
|
|
metadata:
|
|
name: bridge-testnet-tls
|
|
namespace: lux-bridge
|
|
spec:
|
|
secretName: bridge-testnet-tls
|
|
issuerRef: { group: cert-manager.io, kind: ClusterIssuer, name: letsencrypt-prod }
|
|
dnsNames:
|
|
- bridge.lux-test.network
|
|
- bridge-test.lux.network
|
|
---
|
|
apiVersion: cert-manager.io/v1
|
|
kind: Certificate
|
|
metadata:
|
|
name: bridge-devnet-tls
|
|
namespace: lux-bridge
|
|
spec:
|
|
secretName: bridge-devnet-tls
|
|
issuerRef: { group: cert-manager.io, kind: ClusterIssuer, name: letsencrypt-prod }
|
|
dnsNames:
|
|
- bridge.lux-dev.network
|
|
- bridge-dev.lux.network
|
|
---
|
|
# Per-env IngressRoutes (separate from the mainnet lux-bridge route).
|
|
apiVersion: hanzo.ai/v1alpha1
|
|
kind: IngressRoute
|
|
metadata:
|
|
name: bridge-testnet
|
|
namespace: lux-bridge
|
|
labels: { app: bridge-ui-testnet }
|
|
spec:
|
|
entryPoints: [websecure]
|
|
routes:
|
|
- kind: Rule
|
|
match: Host(`bridge.lux-test.network`) || Host(`bridge-test.lux.network`)
|
|
services:
|
|
- name: bridge-ui-testnet
|
|
port: 3000
|
|
tls:
|
|
secretName: bridge-testnet-tls
|
|
---
|
|
apiVersion: hanzo.ai/v1alpha1
|
|
kind: IngressRoute
|
|
metadata:
|
|
name: bridge-devnet
|
|
namespace: lux-bridge
|
|
labels: { app: bridge-ui-devnet }
|
|
spec:
|
|
entryPoints: [websecure]
|
|
routes:
|
|
- kind: Rule
|
|
match: Host(`bridge.lux-dev.network`) || Host(`bridge-dev.lux.network`)
|
|
services:
|
|
- name: bridge-ui-devnet
|
|
port: 3000
|
|
tls:
|
|
secretName: bridge-devnet-tls
|