Files
universe/k8s/exchange/overlays/zoo-mainnet/deployment.yaml
T

97 lines
4.4 KiB
YAML

# zoo.exchange — hanzoai/spa serving the @zooai/exchange SPA bundle.
#
# DNS: zoo.exchange / www.zoo.exchange / exchange.zoo.network / trade.zoo.exchange
# → lux-k8s LB 134.199.138.27 → ingress → this Service.
#
# This overlay does NOT inherit from ../../base because the Zoo image is
# hanzoai/spa-architected (config.json templated from env), not the
# Uniswap-fork init-container sed pattern that the Lux brand uses.
apiVersion: apps/v1
kind: Deployment
metadata:
name: zoo-exchange
labels: { app: zoo-exchange, brand: zoo }
spec:
replicas: 2
selector:
matchLabels: { app: zoo-exchange }
strategy:
type: RollingUpdate
rollingUpdate: { maxSurge: 1, maxUnavailable: 0 }
template:
metadata:
labels: { app: zoo-exchange, brand: zoo }
spec:
imagePullSecrets:
- name: ghcr-zoo
containers:
- name: spa
image: ghcr.io/luxfi/exchange:5.149.66@sha256:1e2e1ca2526e2b4c66f14571d4ca50d432870eb65234888269bb90dc99580ab0
imagePullPolicy: Always
ports:
- { containerPort: 3000, name: http }
# Secrets sourced from KMS — single source of truth.
envFrom:
- secretRef: { name: zoo-exchange-secrets }
# Public, non-secret runtime config templated into /config.json on pod start.
env:
- { name: PORT, value: "3000" }
- { name: ROOT, value: /public }
- { name: SPA_BRAND_NAME, value: "Zoo Exchange" }
- { name: SPA_DEFAULT_CHAIN, value: "200200" }
- { name: SPA_RPC_HOST, value: "https://api.zoo.network" }
# Cross-chain RPC: each org answers on its OWN gateway at /v1/chain/C/rpc.
# Not api.zoo.exchange/<anything> — that host is a zoo-branded alias of
# the Lux mainnet gateway, it serves no org chain and sends no CORS
# headers, so a browser on zoo.exchange cannot read its responses.
- { name: SPA_RPC_HOST_LUX, value: "https://api.lux.network/v1/chain/c" }
- { name: SPA_RPC_HOST_HANZO, value: "https://api.hanzo.network/v1/chain/hanzo" }
- { name: SPA_RPC_HOST_PARS, value: "https://api.pars.network/v1/chain/pars" }
- { name: SPA_API_HOST, value: "https://api.zoo.exchange" }
- { name: SPA_GATEWAY_HOST, value: "https://api.zoo.exchange" }
# Zoo's identity ORIGIN is zoolabs.id — it serves the login SPA at / and
# the IAM API at /v1/iam, /oauth, /.well-known, and its discovery doc
# says issuer=https://zoolabs.id. Fetching discovery from the same origin
# as the issuer is what a strict OIDC client requires.
#
# Was https://iam.zoo.network: a host that never existed. No ingress, no
# router, no issuer-map entry — DNS pointed it at the lux-k8s LB, which
# answered with the Traefik default cert, so every IAM call from this SPA
# died at the TLS handshake. There is no iam.<brand> host in the design;
# iam.hanzo.ai is the one legacy IAM host (the shared Google/GitHub OAuth
# callback is registered against it) and every brand reaches IAM through
# its own id origin.
- { name: SPA_IAM_HOST, value: "https://zoolabs.id" }
- { name: SPA_ID_HOST, value: "https://zoolabs.id" }
- { name: SPA_KMS_HOST, value: "https://kms.zoo.network" }
- { name: SPA_INSIGHTS_HOST, value: "https://insights.hanzo.ai" }
# SPA_INSIGHTS_API_KEY arrives via envFrom (zoo-exchange-secrets).
readinessProbe:
httpGet: { path: /health, port: http }
initialDelaySeconds: 2
periodSeconds: 5
livenessProbe:
httpGet: { path: /health, port: http }
initialDelaySeconds: 10
periodSeconds: 10
resources:
requests: { cpu: "50m", memory: "64Mi" }
limits: { cpu: "500m", memory: "256Mi" }
volumeMounts:
- { name: brand, mountPath: /public/brand.json, subPath: brand.json }
- { name: brand, mountPath: /public/logo.svg, subPath: logo.svg }
volumes:
- name: brand
configMap: { name: zoo-exchange-brand }
---
apiVersion: v1
kind: Service
metadata:
name: zoo-exchange
labels: { app: zoo-exchange, brand: zoo }
spec:
type: ClusterIP
selector: { app: zoo-exchange }
ports:
- { name: http, port: 3000, targetPort: http, protocol: TCP }