Zoo/Pars bridge UIs fetched networks from bridge-api.lux.network cross-origin — a cross-brand call that the backend rejects for the bridge.zoo.network origin (500), so chains never loaded. Route /api on each tenant host to the shared bridge-server (a longer Prefix beats "/"), and point BRIDGE_API_HOST at the host's own origin. Same-origin GET carries no Origin header, so networks load; zero cross-brand leak in the network tab. Mirrors bridge.lux.network. Co-authored-by: Hanzo Dev <[email protected]>
148 lines
11 KiB
YAML
148 lines
11 KiB
YAML
---
|
|
###############################################################################
|
|
# Tenant bridges — Zoo + Pars (lux-k8s / lux-bridge ns)
|
|
#
|
|
# ONE brand-neutral image (ghcr.io/luxfi/bridge, app/bridge Vite SPA on
|
|
# ghcr.io/hanzoai/spa) white-labeled PER HOST by a mounted /__ENV.js that the
|
|
# SPA's bridge.config.ts reads at boot (window.__ENV → brand name/logo/favicon/
|
|
# footer). Same pattern as bridge.lux.network; only the ConfigMap differs.
|
|
#
|
|
# Routing: k8s Ingress `bridge-ingress` sends bridge.zoo.network + bridge.zoo.ngo
|
|
# -> zoo-bridge:80 and bridge.pars.network -> pars-bridge:80, with /api -> the
|
|
# shared bridge-server:3000 on each host so the tenant UI calls its OWN origin
|
|
# (BRIDGE_API_HOST below) — same-origin, no cross-brand call, no CORS.
|
|
###############################################################################
|
|
apiVersion: v1
|
|
data:
|
|
__ENV.js: |
|
|
window.__ENV = {
|
|
BRIDGE_API_HOST: 'https://bridge.zoo.network',
|
|
BRIDGE_ENV: 'mainnet',
|
|
BRIDGE_IAM_ORG: 'zoo',
|
|
BRIDGE_BRAND_NAME: 'Zoo Bridge',
|
|
BRIDGE_LOGO_URL: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDwhLS0gWm9vIGJyYW5kIG1hcms6IHRocmVlLWNpcmNsZSBDTVlLIFZlbm4gZGlhZ3JhbSwgZnVsbCBjb2xvci4KICAgICAgIEZpbGxlZCB0byB0aGUgdmlld3BvcnQgc28gaXQgc2NhbGVzIGNsZWFubHkgaW50byAzMngzMiBuYXYgc2xvdHMKICAgICAgIGFuZCAyNTZ4MjU2IE9HIGNhcmRzIHdpdGhvdXQgdGlueSBkZWFkIG1hcmdpbnMuIC0tPgogIDxkZWZzPgogICAgPGNsaXBQYXRoIGlkPSJsb2dvQ2xpcCI+CiAgICAgIDxjaXJjbGUgY3g9IjUxMiIgY3k9IjUxMSIgcj0iNTAwIi8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoIGlkPSJsb2dvWWVsbG93Ij4KICAgICAgPGNpcmNsZSBjeD0iNTEyIiBjeT0iMjUwIiByPSI0MzAiLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8Y2xpcFBhdGggaWQ9ImxvZ29NYWdlbnRhIj4KICAgICAgPGNpcmNsZSBjeD0iMjQwIiBjeT0iNjcwIiByPSI0MzAiLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8Y2xpcFBhdGggaWQ9ImxvZ29DeWFuIj4KICAgICAgPGNpcmNsZSBjeD0iNzg0IiBjeT0iNjcwIiByPSI0MzAiLz4KICAgIDwvY2xpcFBhdGg+CiAgPC9kZWZzPgogIDxnIGNsaXAtcGF0aD0idXJsKCNsb2dvQ2xpcCkiPgogICAgPGNpcmNsZSBjeD0iNTEyIiBjeT0iMjUwIiByPSI0MzAiIGZpbGw9IiNGQ0YwMDYiLz4KICAgIDxjaXJjbGUgY3g9IjI0MCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjRUEwMThFIi8+CiAgICA8Y2lyY2xlIGN4PSI3ODQiIGN5PSI2NzAiIHI9IjQzMCIgZmlsbD0iIzAxQUNGMSIvPgogICAgPGcgY2xpcC1wYXRoPSJ1cmwoI2xvZ29ZZWxsb3cpIj4KICAgICAgPGNpcmNsZSBjeD0iMjQwIiBjeT0iNjcwIiByPSI0MzAiIGZpbGw9IiNFRDFDMjQiLz4KICAgIDwvZz4KICAgIDxnIGNsaXAtcGF0aD0idXJsKCNsb2dvWWVsbG93KSI+CiAgICAgIDxjaXJjbGUgY3g9Ijc4NCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjMDBBNjUyIi8+CiAgICA8L2c+CiAgICA8ZyBjbGlwLXBhdGg9InVybCgjbG9nb01hZ2VudGEpIj4KICAgICAgPGNpcmNsZSBjeD0iNzg0IiBjeT0iNjcwIiByPSI0MzAiIGZpbGw9IiMyRTMxOTIiLz4KICAgIDwvZz4KICAgIDxnIGNsaXAtcGF0aD0idXJsKCNsb2dvWWVsbG93KSI+CiAgICAgIDxnIGNsaXAtcGF0aD0idXJsKCNsb2dvTWFnZW50YSkiPgogICAgICAgIDxjaXJjbGUgY3g9Ijc4NCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjMDAwMDAwIi8+CiAgICAgIDwvZz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=',
|
|
BRIDGE_FAVICON_URL: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDwhLS0gWm9vIGJyYW5kIG1hcms6IHRocmVlLWNpcmNsZSBDTVlLIFZlbm4gZGlhZ3JhbSwgZnVsbCBjb2xvci4KICAgICAgIFNhbWUgbW90aWYgYXMgbG9nby5zdmcgYnV0IHNjYWxlZCB0byBmaWxsIHRoZSBmYXZpY29uIHZpZXdwb3J0IHNvCiAgICAgICB0aGUgY29sb3JzIHJlbWFpbiByZWFkYWJsZSBhdCAxNngxNiAvIDMyeDMyLiAtLT4KICA8ZGVmcz4KICAgIDxjbGlwUGF0aCBpZD0iZmF2Q2xpcCI+CiAgICAgIDxjaXJjbGUgY3g9IjUxMiIgY3k9IjUxMSIgcj0iNTAwIi8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoIGlkPSJmYXZZZWxsb3ciPgogICAgICA8Y2lyY2xlIGN4PSI1MTIiIGN5PSIyNTAiIHI9IjQzMCIvPgogICAgPC9jbGlwUGF0aD4KICAgIDxjbGlwUGF0aCBpZD0iZmF2TWFnZW50YSI+CiAgICAgIDxjaXJjbGUgY3g9IjI0MCIgY3k9IjY3MCIgcj0iNDMwIi8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoIGlkPSJmYXZDeWFuIj4KICAgICAgPGNpcmNsZSBjeD0iNzg0IiBjeT0iNjcwIiByPSI0MzAiLz4KICAgIDwvY2xpcFBhdGg+CiAgPC9kZWZzPgogIDxnIGNsaXAtcGF0aD0idXJsKCNmYXZDbGlwKSI+CiAgICA8Y2lyY2xlIGN4PSI1MTIiIGN5PSIyNTAiIHI9IjQzMCIgZmlsbD0iI0ZDRjAwNiIvPgogICAgPGNpcmNsZSBjeD0iMjQwIiBjeT0iNjcwIiByPSI0MzAiIGZpbGw9IiNFQTAxOEUiLz4KICAgIDxjaXJjbGUgY3g9Ijc4NCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjMDFBQ0YxIi8+CiAgICA8ZyBjbGlwLXBhdGg9InVybCgjZmF2WWVsbG93KSI+CiAgICAgIDxjaXJjbGUgY3g9IjI0MCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjRUQxQzI0Ii8+CiAgICA8L2c+CiAgICA8ZyBjbGlwLXBhdGg9InVybCgjZmF2WWVsbG93KSI+CiAgICAgIDxjaXJjbGUgY3g9Ijc4NCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjMDBBNjUyIi8+CiAgICA8L2c+CiAgICA8ZyBjbGlwLXBhdGg9InVybCgjZmF2TWFnZW50YSkiPgogICAgICA8Y2lyY2xlIGN4PSI3ODQiIGN5PSI2NzAiIHI9IjQzMCIgZmlsbD0iIzJFMzE5MiIvPgogICAgPC9nPgogICAgPGcgY2xpcC1wYXRoPSJ1cmwoI2ZhdlllbGxvdykiPgogICAgICA8ZyBjbGlwLXBhdGg9InVybCgjZmF2TWFnZW50YSkiPgogICAgICAgIDxjaXJjbGUgY3g9Ijc4NCIgY3k9IjY3MCIgcj0iNDMwIiBmaWxsPSIjMDAwMDAwIi8+CiAgICAgIDwvZz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=',
|
|
BRIDGE_PRIMARY_COLOR: '#00d97e',
|
|
BRIDGE_SUPPORT_EMAIL: '[email protected]',
|
|
BRIDGE_DOCS_URL: 'https://docs.zoo.network',
|
|
}
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: zoo-bridge-env
|
|
namespace: lux-bridge
|
|
---
|
|
apiVersion: v1
|
|
data:
|
|
__ENV.js: |
|
|
window.__ENV = {
|
|
BRIDGE_API_HOST: 'https://bridge.pars.network',
|
|
BRIDGE_ENV: 'mainnet',
|
|
BRIDGE_IAM_ORG: 'pars',
|
|
BRIDGE_BRAND_NAME: 'Pars Bridge',
|
|
BRIDGE_LOGO_URL: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgIDxkZWZzPgogICAgICAgICAgICA8Y2xpcFBhdGggaWQ9Im91dGVyQ2lyY2xlQ29sb3IiPgogICAgICAgICAgICAgICAgPGNpcmNsZSBjeD0iNTEyIiBjeT0iNTExIiByPSIyNzAiLz4KICAgICAgICAgICAgPC9jbGlwUGF0aD4KICAgICAgICAgICAgPGNsaXBQYXRoIGlkPSJncmVlbkNsaXAiPgogICAgICAgICAgICAgICAgPGNpcmNsZSBjeD0iNTEzIiBjeT0iMzY5IiByPSIyMzQiLz4KICAgICAgICAgICAgPC9jbGlwUGF0aD4KICAgICAgICAgICAgPGNsaXBQYXRoIGlkPSJyZWRDbGlwIj4KICAgICAgICAgICAgICAgIDxjaXJjbGUgY3g9IjM2NSIgY3k9IjU5NSIgcj0iMjM0Ii8+CiAgICAgICAgICAgIDwvY2xpcFBhdGg+CiAgICAgICAgICAgIDxjbGlwUGF0aCBpZD0iYmx1ZUNsaXAiPgogICAgICAgICAgICAgICAgPGNpcmNsZSBjeD0iNjQzIiBjeT0iNTk1IiByPSIyMzQiLz4KICAgICAgICAgICAgPC9jbGlwUGF0aD4KICAgICAgICA8L2RlZnM+CiAgICAgICAgPGcgY2xpcC1wYXRoPSJ1cmwoI291dGVyQ2lyY2xlQ29sb3IpIj4KICAgICAgICAgICAgPGNpcmNsZSBjeD0iNTEzIiBjeT0iMzY5IiByPSIyMzQiIGZpbGw9IiMwMEE2NTIiLz4KICAgICAgICAgICAgPGNpcmNsZSBjeD0iMzY1IiBjeT0iNTk1IiByPSIyMzQiIGZpbGw9IiNFRDFDMjQiLz4KICAgICAgICAgICAgPGNpcmNsZSBjeD0iNjQzIiBjeT0iNTk1IiByPSIyMzQiIGZpbGw9IiMyRTMxOTIiLz4KICAgICAgICAgICAgPGcgY2xpcC1wYXRoPSJ1cmwoI2dyZWVuQ2xpcCkiPgogICAgICAgICAgICAgICAgPGNpcmNsZSBjeD0iMzY1IiBjeT0iNTk1IiByPSIyMzQiIGZpbGw9IiNGQ0YwMDYiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8ZyBjbGlwLXBhdGg9InVybCgjZ3JlZW5DbGlwKSI+CiAgICAgICAgICAgICAgICA8Y2lyY2xlIGN4PSI2NDMiIGN5PSI1OTUiIHI9IjIzNCIgZmlsbD0iIzAxQUNGMSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxnIGNsaXAtcGF0aD0idXJsKCNyZWRDbGlwKSI+CiAgICAgICAgICAgICAgICA8Y2lyY2xlIGN4PSI2NDMiIGN5PSI1OTUiIHI9IjIzNCIgZmlsbD0iI0VBMDE4RSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxnIGNsaXAtcGF0aD0idXJsKCNncmVlbkNsaXApIj4KICAgICAgICAgICAgICAgIDxnIGNsaXAtcGF0aD0idXJsKCNyZWRDbGlwKSI+CiAgICAgICAgICAgICAgICAgICAgPGNpcmNsZSBjeD0iNjQzIiBjeT0iNTk1IiByPSIyMzQiIGZpbGw9IiNGRkZGRkYiLz4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvc3ZnPg==',
|
|
BRIDGE_FAVICON_URL: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPGRlZnM+CiAgICAgIDxjbGlwUGF0aCBpZD0ib3V0ZXJDaXJjbGVNb25vIj4KICAgICAgICA8Y2lyY2xlIGN4PSI1MDgiIGN5PSI1MTAiIHI9IjI4MyI+PC9jaXJjbGU+CiAgICAgIDwvY2xpcFBhdGg+CiAgICA8L2RlZnM+CiAgICA8ZyBjbGlwLXBhdGg9InVybCgjb3V0ZXJDaXJjbGVNb25vKSI+CiAgICAgIDxjaXJjbGUgY3g9IjUxMyIgY3k9IjM2OSIgcj0iMjM0IiBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjMzIj48L2NpcmNsZT4KICAgICAgPGNpcmNsZSBjeD0iMzY1IiBjeT0iNTk1IiByPSIyMzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS13aWR0aD0iMzMiPjwvY2lyY2xlPgogICAgICA8Y2lyY2xlIGN4PSI2NDMiIGN5PSI1OTUiIHI9IjIzNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLXdpZHRoPSIzMyI+PC9jaXJjbGU+CiAgICAgIDxjaXJjbGUgY3g9IjUwOCIgY3k9IjUxMCIgcj0iMjY1IiBmaWxsPSJub25lIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utd2lkdGg9IjM2Ij48L2NpcmNsZT4KICAgIDwvZz4KICA8L3N2Zz4=',
|
|
BRIDGE_PRIMARY_COLOR: '#C9A227',
|
|
BRIDGE_SUPPORT_EMAIL: '[email protected]',
|
|
BRIDGE_DOCS_URL: 'https://docs.pars.network',
|
|
}
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: pars-bridge-env
|
|
namespace: lux-bridge
|
|
---
|
|
# ── Zoo Bridge ──────────────────────────────────────────────────────────────
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: zoo-bridge
|
|
namespace: lux-bridge
|
|
labels: { app: zoo-bridge, tenant: zoo }
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels: { app: zoo-bridge }
|
|
template:
|
|
metadata:
|
|
labels: { app: zoo-bridge, tenant: zoo }
|
|
spec:
|
|
containers:
|
|
- name: bridge
|
|
image: ghcr.io/luxfi/bridge:main
|
|
imagePullPolicy: Always
|
|
ports: [{ containerPort: 3000, name: http }]
|
|
volumeMounts:
|
|
- { name: env-js, mountPath: /public/__ENV.js, subPath: __ENV.js, readOnly: true }
|
|
readinessProbe:
|
|
httpGet: { path: /health, port: http }
|
|
initialDelaySeconds: 3
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet: { path: /health, port: http }
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 30
|
|
resources:
|
|
requests: { cpu: "50m", memory: "64Mi" }
|
|
limits: { cpu: "300m", memory: "256Mi" }
|
|
volumes:
|
|
- name: env-js
|
|
configMap: { name: zoo-bridge-env }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: zoo-bridge
|
|
namespace: lux-bridge
|
|
labels: { app: zoo-bridge }
|
|
spec:
|
|
type: ClusterIP
|
|
selector: { app: zoo-bridge }
|
|
ports: [{ name: http, port: 80, targetPort: http }]
|
|
---
|
|
# ── Pars Bridge ─────────────────────────────────────────────────────────────
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: pars-bridge
|
|
namespace: lux-bridge
|
|
labels: { app: pars-bridge, tenant: pars }
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels: { app: pars-bridge }
|
|
template:
|
|
metadata:
|
|
labels: { app: pars-bridge, tenant: pars }
|
|
spec:
|
|
containers:
|
|
- name: bridge
|
|
image: ghcr.io/luxfi/bridge:main
|
|
imagePullPolicy: Always
|
|
ports: [{ containerPort: 3000, name: http }]
|
|
volumeMounts:
|
|
- { name: env-js, mountPath: /public/__ENV.js, subPath: __ENV.js, readOnly: true }
|
|
readinessProbe:
|
|
httpGet: { path: /health, port: http }
|
|
initialDelaySeconds: 3
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet: { path: /health, port: http }
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 30
|
|
resources:
|
|
requests: { cpu: "50m", memory: "64Mi" }
|
|
limits: { cpu: "300m", memory: "256Mi" }
|
|
volumes:
|
|
- name: env-js
|
|
configMap: { name: pars-bridge-env }
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: pars-bridge
|
|
namespace: lux-bridge
|
|
labels: { app: pars-bridge }
|
|
spec:
|
|
type: ClusterIP
|
|
selector: { app: pars-bridge }
|
|
ports: [{ name: http, port: 80, targetPort: http }]
|