k8s(api): white-label api gateway pattern + api.zoo.exchange ingress
Lux-ecosystem KrakenD gateway (lux-mainnet/api-gateway-gateway) is now
documented as a shared multi-tenant backend. Any white-label app —
Zoo, Hanzo, Pars, <tenant>, third-party — points api.<their-domain>
at the same service via a 5-line Ingress + DNS A record. CORS is
wide-open (allow_origins=*) so browser SPAs from any origin work.
This commit:
- k8s/lux-mainnet/api-zoo-exchange-ingress.yaml — first WL ingress
(api.zoo.exchange → api-gateway-gateway:80, TLS via cert-manager)
- k8s/lux-mainnet/WHITE-LABEL-API.md — onboarding recipe + endpoint
list + backend topology diagram
zoo.exchange's SPA env (SPA_RPC_HOST, SPA_API_HOST, SPA_GATEWAY_HOST)
all now point at api.zoo.exchange. JS bundle was rewritten by the init
container to swap api.lux.network/* and gateway.{lux,uniswap}.org
references for api.zoo.exchange — zero third-party domains visible
in browser network tab.
Out-of-band: removed api.zoo.exchange from the conflicting
lux-ns/exchange-api Traefik IngressRoute so the standard Ingress
takes the route.
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
# White-Label API Gateway (`api-gateway-gateway`)
|
||||
|
||||
The Lux ecosystem's KrakenD gateway at `lux-mainnet/api-gateway-gateway:80`
|
||||
is a **shared multi-tenant backend**. Any white-label app — Zoo, Hanzo,
|
||||
Pars, Liquidity, or third-party — points its `api.<their-domain>` at the
|
||||
gateway and gets:
|
||||
|
||||
- All Lux-ecosystem chain RPCs (`/ext/bc/{C,X,P,zoo,hanzo,spc,pars,Q,Z,T,A,D}/rpc`)
|
||||
- Indexer endpoints (`/api/indexer/indexer-{c,zoo,hanzo,spc,pars}`)
|
||||
- Health, info, and admin endpoints (`/ext/health`, `/ext/info`)
|
||||
- DAG chain stats (`/api/v2/dag/{q,z,t,a}chain/{health,stats}`)
|
||||
- DEX precompile / gateway routes
|
||||
- TLS via cert-manager letsencrypt
|
||||
- CORS already wide-open (`Access-Control-Allow-Origin: *`)
|
||||
|
||||
Zero per-tenant config needed in the gateway itself.
|
||||
|
||||
## Onboarding a new white-label app
|
||||
|
||||
1. **Pick a hostname**: `api.<your-app>.<tld>` (e.g. `api.zoo.exchange`,
|
||||
`api.hanzo.network`, `api.pars.market`).
|
||||
|
||||
2. **Create an Ingress** in `lux-mainnet` ns:
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: api-<your-app>
|
||||
namespace: lux-mainnet
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
spec:
|
||||
ingressClassName: ingress
|
||||
tls:
|
||||
- hosts: [api.<your-app>.<tld>]
|
||||
secretName: api-<your-app>-tls
|
||||
rules:
|
||||
- host: api.<your-app>.<tld>
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api-gateway-gateway
|
||||
port: { number: 80 }
|
||||
```
|
||||
|
||||
3. **Add DNS**: `A` record for `api.<your-app>.<tld>` → `134.199.138.27`
|
||||
(`hanzo-ingress` LB on `do-sfo3-lux-k8s`). Cloudflare-proxied is fine.
|
||||
|
||||
4. **Verify**:
|
||||
|
||||
```bash
|
||||
curl -X POST -H 'Content-Type: application/json' \
|
||||
--data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
|
||||
https://api.<your-app>.<tld>/ext/bc/zoo/rpc
|
||||
# → {"jsonrpc":"2.0","id":1,"result":"0x30e08"}
|
||||
```
|
||||
|
||||
5. **Point your SPA** at the new hostname. With `hanzoai/spa` runtime,
|
||||
set the env vars:
|
||||
|
||||
```yaml
|
||||
- { name: SPA_RPC_HOST, value: https://api.<your-app>.<tld>/ext/bc/<chain>/rpc }
|
||||
- { name: SPA_API_HOST, value: https://api.<your-app>.<tld> }
|
||||
- { name: SPA_GATEWAY_HOST, value: https://api.<your-app>.<tld> }
|
||||
```
|
||||
|
||||
These get templated into `/config.json` at pod start; the SPA reads them
|
||||
on boot and uses them for every backend call.
|
||||
|
||||
## Live deployments
|
||||
|
||||
| host | namespace | ingress |
|
||||
|---|---|---|
|
||||
| `api.lux.network` | `lux-mainnet` | (legacy direct DNS) |
|
||||
| `api.zoo.exchange` | `lux-mainnet` | `api-zoo-exchange` |
|
||||
| `api.hanzo.network` | _(reserve)_ | _(create when needed)_ |
|
||||
| `api.pars.market` | _(reserve)_ | _(create when needed)_ |
|
||||
| `api.liquid.exchange` | _(reserve)_ | _(create when needed)_ |
|
||||
|
||||
All point to the same `api-gateway-gateway` service; each just gets its
|
||||
own TLS cert and DNS record.
|
||||
|
||||
## Endpoints exposed
|
||||
|
||||
```
|
||||
# Chain RPCs (40+ endpoints across 12 chains × 3 envs each)
|
||||
POST /ext/bc/C/rpc # Lux C-Chain (96369)
|
||||
POST /ext/bc/zoo/rpc # Zoo (200200)
|
||||
POST /ext/bc/hanzo/rpc # Hanzo (36963)
|
||||
POST /ext/bc/pars/rpc # Pars (494949)
|
||||
POST /ext/bc/spc/rpc # SPC (36911)
|
||||
POST /ext/bc/{Q,Z,T,A,D}/rpc # primary network chains
|
||||
POST /ext/bc/{X,P} # X-Chain, P-Chain
|
||||
GET /ext/info
|
||||
GET /ext/health{,/liveness}
|
||||
|
||||
# Indexer (block/tx data per chain)
|
||||
GET /api/indexer/indexer-{c,zoo,hanzo,spc,pars}/...
|
||||
|
||||
# DAG / consensus stats
|
||||
GET /api/v2/dag/{q,z,t,a}chain/{health,stats}
|
||||
GET /api/chains
|
||||
```
|
||||
|
||||
Add a new endpoint by editing `lux-mainnet/api-gateway-gateway-config`
|
||||
ConfigMap (krakend.json `endpoints[]`) and rolling the deployment.
|
||||
|
||||
## CORS
|
||||
|
||||
Wide-open by default — any browser Origin is allowed:
|
||||
|
||||
```json
|
||||
"security/cors": {
|
||||
"allow_origins": ["*"],
|
||||
"allow_methods": ["GET","POST","OPTIONS"],
|
||||
"allow_headers": ["Content-Type","Authorization"],
|
||||
"expose_headers": ["Content-Length"],
|
||||
"max_age": "12h"
|
||||
}
|
||||
```
|
||||
|
||||
To tighten to a specific allow-list, replace `["*"]` with the explicit
|
||||
origin list and roll `api-gateway-gateway` deployment in `lux-mainnet`.
|
||||
|
||||
## Backend topology
|
||||
|
||||
```
|
||||
Browser (zoo.exchange / hanzo.exchange / pars.market / ...)
|
||||
│
|
||||
│ https://api.<app>.<tld>/ext/bc/zoo/rpc
|
||||
▼
|
||||
Cloudflare (TLS termination + caching)
|
||||
│
|
||||
▼
|
||||
hanzo-ingress LB (lux-system, 134.199.138.27)
|
||||
│ routes by Host header → matching K8s Ingress
|
||||
▼
|
||||
api-gateway-gateway (lux-mainnet, KrakenD :80)
|
||||
│ matches /ext/bc/<alias>/rpc → looks up backend host
|
||||
▼
|
||||
luxd-headless.lux-mainnet.svc.cluster.local:9630
|
||||
│ StatefulSet: 5 luxd validators on Lux Mainnet
|
||||
▼
|
||||
Subnet-EVM plugin (per chain) → on-disk badgerdb state
|
||||
```
|
||||
|
||||
The SPA never needs to know any of this — it just calls
|
||||
`https://api.<their-domain>/ext/bc/<chain>/rpc` and KrakenD does the rest.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# api.zoo.exchange — white-label of api.lux.network. Same Lux KrakenD
|
||||
# gateway (lux-mainnet/api-gateway-gateway:8080) backs both. SPA hits
|
||||
# api.zoo.exchange for /ext/bc/zoo/rpc, /api/indexer/indexer-zoo, etc.
|
||||
# Zero third-party traffic visible to user.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: api-zoo-exchange
|
||||
namespace: lux-mainnet
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
# Allow all origins for browser SDK calls from zoo.exchange
|
||||
hanzo.ai/ingress.cors-allow-origin: "https://zoo.exchange,https://www.zoo.exchange"
|
||||
spec:
|
||||
ingressClassName: ingress
|
||||
tls:
|
||||
- hosts: [api.zoo.exchange]
|
||||
secretName: api-zoo-exchange-tls
|
||||
rules:
|
||||
- host: api.zoo.exchange
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api-gateway-gateway # KrakenD already wired with all chain RPCs + indexers
|
||||
port: { number: 8080 }
|
||||
Reference in New Issue
Block a user