Merge pull request #6 from luxfi/ci/arc-hanzo-consolidation

feat: multi-brand build matrix + white-label support
This commit is contained in:
2026-05-05 20:51:37 -07:00
committed by GitHub
10 changed files with 446 additions and 68 deletions
+49
View File
@@ -0,0 +1,49 @@
# External white-label deployment template.
#
# This file documents the env vars an external operator must set when running
# this image with their own brand. The source code is brand-neutral — all
# strings below are operator-supplied at deploy time.
#
# Copy and customize for your deployment:
# cp .env.example.external .env.local
#
# Replace every <PLACEHOLDER> with your own values. NEVER commit a populated
# version of this file with third-party trademarks back to this repo.
NEXT_PUBLIC_BRAND=other
# Network
NEXT_PUBLIC_NETWORK_NAME=<Your Network Name>
NEXT_PUBLIC_NETWORK_SHORT_NAME=<SYMBOL>
NEXT_PUBLIC_NETWORK_ID=<chain-id-uint64>
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=<Token Name>
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=<TOKEN>
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_RPC_URL=https://<your-rpc-host>/rpc
# API
NEXT_PUBLIC_API_HOST=<your-api-host>
NEXT_PUBLIC_API_PROTOCOL=https
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=wss
# App
NEXT_PUBLIC_APP_HOST=<your-explorer-host>
NEXT_PUBLIC_APP_PROTOCOL=https
NEXT_PUBLIC_IS_TESTNET=false
# Branding (operator-supplied)
NEXT_PUBLIC_NETWORK_ORG_NAME=<Your Org>
NEXT_PUBLIC_NETWORK_WEBSITE_URL=https://<your-domain>
NEXT_PUBLIC_NETWORK_DESCRIPTION=<one-line description>
NEXT_PUBLIC_NETWORK_GITHUB_URL=
NEXT_PUBLIC_NETWORK_TWITTER_URL=
NEXT_PUBLIC_NETWORK_DISCORD_URL=
# Auth (IAM) — operator-supplied OIDC provider
NEXT_PUBLIC_ACCOUNT_AUTH_PROVIDER=oidc
NEXT_PUBLIC_OIDC_SERVER_URL=<https://your-iam-host>
NEXT_PUBLIC_OIDC_CLIENT_ID=<your-client-id>
# Theme
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dark
+39
View File
@@ -0,0 +1,39 @@
# Hanzo Explorer — Hanzo AI brand.
# Use: cp .env.example.hanzo .env.local && pnpm build
NEXT_PUBLIC_BRAND=hanzo
# Network (Hanzo subnet on Lux mainnet)
NEXT_PUBLIC_NETWORK_NAME=Hanzo AI
NEXT_PUBLIC_NETWORK_SHORT_NAME=HANZO
NEXT_PUBLIC_NETWORK_ID=36963
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=HANZO
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=HANZO
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
# API
NEXT_PUBLIC_API_HOST=api-explore-hanzo.lux.network
NEXT_PUBLIC_API_PROTOCOL=https
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=wss
# App
NEXT_PUBLIC_APP_HOST=explore.hanzo.ai
NEXT_PUBLIC_APP_PROTOCOL=https
NEXT_PUBLIC_IS_TESTNET=false
# Branding
NEXT_PUBLIC_NETWORK_ORG_NAME=Hanzo Industries Inc.
NEXT_PUBLIC_NETWORK_WEBSITE_URL=https://hanzo.ai
NEXT_PUBLIC_NETWORK_DESCRIPTION=AI blockchain — decentralized compute and inference.
NEXT_PUBLIC_NETWORK_GITHUB_URL=https://github.com/hanzoai
NEXT_PUBLIC_NETWORK_TWITTER_URL=https://x.com/hanaboratory
NEXT_PUBLIC_NETWORK_DISCORD_URL=https://discord.gg/hanzoai
# Auth (IAM)
NEXT_PUBLIC_ACCOUNT_AUTH_PROVIDER=oidc
NEXT_PUBLIC_OIDC_SERVER_URL=https://hanzo.id
NEXT_PUBLIC_OIDC_CLIENT_ID=hanzo-explore-client-id
# Theme
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dark
+40
View File
@@ -0,0 +1,40 @@
# Lux Explorer — default brand.
# `pnpm build` with no NEXT_PUBLIC_BRAND override produces a Lux-branded site.
NEXT_PUBLIC_BRAND=lux
# Network
NEXT_PUBLIC_NETWORK_NAME=Lux Network
NEXT_PUBLIC_NETWORK_SHORT_NAME=LUX
NEXT_PUBLIC_NETWORK_ID=96369
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=LUX
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=LUX
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_RPC_URL=https://api.lux.network/ext/bc/C/rpc
# API
NEXT_PUBLIC_API_HOST=api-explore.lux.network
NEXT_PUBLIC_API_PROTOCOL=https
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=wss
# App
NEXT_PUBLIC_APP_HOST=explore.lux.network
NEXT_PUBLIC_APP_PROTOCOL=https
NEXT_PUBLIC_IS_TESTNET=false
# Branding
NEXT_PUBLIC_NETWORK_ORG_NAME=Lux Industries Inc.
NEXT_PUBLIC_NETWORK_WEBSITE_URL=https://lux.network
NEXT_PUBLIC_NETWORK_DESCRIPTION=High-performance blockchain for decentralized applications.
NEXT_PUBLIC_NETWORK_GITHUB_URL=https://github.com/luxfi
NEXT_PUBLIC_NETWORK_TWITTER_URL=https://x.com/luxdefi
NEXT_PUBLIC_NETWORK_DISCORD_URL=https://discord.gg/luxnetwork
# Auth (IAM)
NEXT_PUBLIC_ACCOUNT_AUTH_PROVIDER=oidc
NEXT_PUBLIC_OIDC_SERVER_URL=https://lux.id
NEXT_PUBLIC_OIDC_CLIENT_ID=lux-explore-client-id
# Theme
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dark
+39
View File
@@ -0,0 +1,39 @@
# Pars Explorer — Parsis Foundation brand.
# Use: cp .env.example.pars .env.local && pnpm build
NEXT_PUBLIC_BRAND=pars
# Network (Pars subnet on Lux mainnet)
NEXT_PUBLIC_NETWORK_NAME=Pars Network
NEXT_PUBLIC_NETWORK_SHORT_NAME=PARS
NEXT_PUBLIC_NETWORK_ID=494949
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=PARS
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=PARS
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
# API
NEXT_PUBLIC_API_HOST=api-explore-pars.lux.network
NEXT_PUBLIC_API_PROTOCOL=https
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=wss
# App
NEXT_PUBLIC_APP_HOST=explore.pars.network
NEXT_PUBLIC_APP_PROTOCOL=https
NEXT_PUBLIC_IS_TESTNET=false
# Branding
NEXT_PUBLIC_NETWORK_ORG_NAME=Parsis Foundation
NEXT_PUBLIC_NETWORK_WEBSITE_URL=https://pars.network
NEXT_PUBLIC_NETWORK_DESCRIPTION=Pars blockchain — financial infrastructure for the Persian-speaking world.
NEXT_PUBLIC_NETWORK_GITHUB_URL=https://github.com/luxfi
NEXT_PUBLIC_NETWORK_TWITTER_URL=https://x.com/parsnetwork
NEXT_PUBLIC_NETWORK_DISCORD_URL=https://discord.gg/luxnetwork
# Auth (IAM)
NEXT_PUBLIC_ACCOUNT_AUTH_PROVIDER=oidc
NEXT_PUBLIC_OIDC_SERVER_URL=https://pars.id
NEXT_PUBLIC_OIDC_CLIENT_ID=pars-explore-client-id
# Theme
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dark
+39
View File
@@ -0,0 +1,39 @@
# Zoo Explorer — Zoo Labs Foundation brand.
# Use: cp .env.example.zoo .env.local && pnpm build
NEXT_PUBLIC_BRAND=zoo
# Network (Zoo subnet on Lux mainnet)
NEXT_PUBLIC_NETWORK_NAME=Zoo Chain
NEXT_PUBLIC_NETWORK_SHORT_NAME=ZOO
NEXT_PUBLIC_NETWORK_ID=200200
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=ZOO
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ZOO
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
# API
NEXT_PUBLIC_API_HOST=api-explore-zoo.lux.network
NEXT_PUBLIC_API_PROTOCOL=https
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=wss
# App
NEXT_PUBLIC_APP_HOST=explore.zoo.ngo
NEXT_PUBLIC_APP_PROTOCOL=https
NEXT_PUBLIC_IS_TESTNET=false
# Branding
NEXT_PUBLIC_NETWORK_ORG_NAME=Zoo Labs Foundation
NEXT_PUBLIC_NETWORK_WEBSITE_URL=https://zoo.ngo
NEXT_PUBLIC_NETWORK_DESCRIPTION=Open AI research network — decentralized AI and science.
NEXT_PUBLIC_NETWORK_GITHUB_URL=https://github.com/zooai
NEXT_PUBLIC_NETWORK_TWITTER_URL=https://x.com/zoolabs
NEXT_PUBLIC_NETWORK_DISCORD_URL=https://discord.gg/zoolabs
# Auth (IAM)
NEXT_PUBLIC_ACCOUNT_AUTH_PROVIDER=oidc
NEXT_PUBLIC_OIDC_SERVER_URL=https://zoo.id
NEXT_PUBLIC_OIDC_CLIENT_ID=zoo-explore-client-id
# Theme
NEXT_PUBLIC_COLOR_THEME_DEFAULT=dark
+70 -10
View File
@@ -21,14 +21,28 @@ concurrency:
env:
REGISTRY: ghcr.io
IMAGE_NAME: luxfi/explore
jobs:
build:
runs-on: hanzo-build-linux-amd64
name: Build ${{ matrix.brand }} (${{ matrix.arch }})
runs-on: hanzo-build-linux-${{ matrix.arch }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
brand: [ lux, hanzo, zoo, pars ]
arch: [ amd64, arm64 ]
include:
- brand: lux
image: luxfi/explore
- brand: hanzo
image: luxfi/explore-hanzo
- brand: zoo
image: luxfi/explore-zoo
- brand: pars
image: luxfi/explore-pars
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -47,32 +61,78 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
images: ${{ env.REGISTRY }}/${{ matrix.image }}
flavor: |
latest=false
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=semver,pattern={{version}}
type=raw,value=latest-${{ matrix.arch }},enable={{is_default_branch}}
type=ref,event=branch,suffix=-${{ matrix.arch }}
type=semver,pattern={{version}},suffix=-${{ matrix.arch }}
- name: Build and push image
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
platforms: linux/${{ matrix.arch }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=gha,scope=${{ matrix.brand }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.brand }}-${{ matrix.arch }}
provenance: false
push: ${{ github.event_name != 'pull_request' }}
build-args: |
GIT_COMMIT_SHA=${{ github.sha }}
GIT_TAG=${{ github.ref_type == 'tag' && github.ref_name || '' }}
NEXT_PUBLIC_BRAND=${{ matrix.brand }}
manifest:
name: Multi-arch manifest (${{ matrix.brand }})
needs: build
if: github.event_name != 'pull_request'
runs-on: hanzo-build-linux-amd64
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- brand: lux
image: luxfi/explore
- brand: hanzo
image: luxfi/explore-hanzo
- brand: zoo
image: luxfi/explore-zoo
- brand: pars
image: luxfi/explore-pars
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest (latest)
if: github.ref == 'refs/heads/main'
run: |
docker buildx imagetools create \
-t ${{ env.REGISTRY }}/${{ matrix.image }}:latest \
${{ env.REGISTRY }}/${{ matrix.image }}:latest-amd64 \
${{ env.REGISTRY }}/${{ matrix.image }}:latest-arm64
- name: Create and push manifest (tag)
if: github.ref_type == 'tag'
run: |
TAG="${GITHUB_REF_NAME#v}"
docker buildx imagetools create \
-t ${{ env.REGISTRY }}/${{ matrix.image }}:${TAG} \
${{ env.REGISTRY }}/${{ matrix.image }}:${TAG}-amd64 \
${{ env.REGISTRY }}/${{ matrix.image }}:${TAG}-arm64
deploy:
needs: build
needs: manifest
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
runs-on: hanzo-deploy-linux-amd64
permissions:
+3
View File
@@ -56,6 +56,9 @@ ARG GIT_TAG
ENV NEXT_PUBLIC_GIT_TAG=$GIT_TAG
ARG NEXT_OPEN_TELEMETRY_ENABLED
ENV NEXT_OPEN_TELEMETRY_ENABLED=$NEXT_OPEN_TELEMETRY_ENABLED
# White-label brand selector (lux | hanzo | zoo | pars | other). Defaults to lux.
ARG NEXT_PUBLIC_BRAND=lux
ENV NEXT_PUBLIC_BRAND=$NEXT_PUBLIC_BRAND
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_OPTIONS="--max-old-space-size=8192"
+99 -57
View File
@@ -39,6 +39,9 @@ export interface ChainEntry {
readonly label: string;
readonly vm: string;
readonly network: 'mainnet' | 'testnet' | 'devnet' | 'localnet';
/** EVM chain ID (uint64). Canonical values tracked in MEMORY.md. */
readonly chainId: number;
readonly hostnames: ReadonlyArray<string>;
readonly explorerUrl: string;
readonly apiUrl: string;
@@ -214,28 +217,13 @@ const PARS_BRANDING: ChainBranding = {
'</g>',
};
/** Liquidity — wordmark "L" from ~/work/liquidity/brand/ */
const LIQUIDITY_BRANDING: ChainBranding = {
brandName: 'Liquidity Network',
orgName: 'Liquidity Inc.',
websiteUrl: 'https://',
description: 'Digital securities exchange — ATS, broker-dealer, transfer agent.',
githubUrl: 'https://github.com/',
twitterUrl: 'https://x.com/',
discordUrl: 'https://discord.gg/',
logoViewBox: '0 0 100 100',
logoContent: '<path d="M30 20 L30 80 L70 80 L70 65 L45 65 L45 20 Z" fill="currentColor"/>',
faviconContent:
'<rect width="512" height="512" rx="64" fill="#000"/>' +
'<path d="M155 100 L155 410 L360 410 L360 340 L230 340 L230 100 Z" fill="#fff"/>',
};
export const CHAINS: ReadonlyArray<ChainEntry> = [
{
name: 'C-Chain',
label: 'Contract Chain',
vm: 'EVM',
network: 'mainnet',
chainId: 96369,
// explore.lux.build is a legacy alias — the runtime ingress 301-redirects
// it to explore.lux.network, so we only register the canonical host here.
hostnames: [ 'explore.lux.network', 'localhost', '127.0.0.1', '0.0.0.0' ],
@@ -248,6 +236,7 @@ export const CHAINS: ReadonlyArray<ChainEntry> = [
label: 'Zoo Chain',
vm: 'L2',
network: 'mainnet',
chainId: 200200,
hostnames: [ 'explore-zoo.lux.network', 'explore.zoo.network', 'explore.zoo.ngo' ],
explorerUrl: 'https://explore-zoo.lux.network',
apiUrl: 'https://api-explore-zoo.lux.network',
@@ -258,6 +247,7 @@ export const CHAINS: ReadonlyArray<ChainEntry> = [
label: 'Hanzo AI',
vm: 'L2',
network: 'mainnet',
chainId: 36963,
hostnames: [ 'explore-hanzo.lux.network', 'explore.hanzo.network', 'explore.hanzo.ai' ],
explorerUrl: 'https://explore-hanzo.lux.network',
apiUrl: 'https://api-explore-hanzo.lux.network',
@@ -268,6 +258,7 @@ export const CHAINS: ReadonlyArray<ChainEntry> = [
label: 'SPC Chain',
vm: 'L2',
network: 'mainnet',
chainId: 36911,
hostnames: [ 'explore-spc.lux.network' ],
explorerUrl: 'https://explore-spc.lux.network',
apiUrl: 'https://api-explore-spc.lux.network',
@@ -278,41 +269,67 @@ export const CHAINS: ReadonlyArray<ChainEntry> = [
label: 'Pars Network',
vm: 'L2',
network: 'mainnet',
chainId: 494949,
hostnames: [ 'explore-pars.lux.network', 'explore.pars.network' ],
explorerUrl: 'https://explore-pars.lux.network',
apiUrl: 'https://api-explore-pars.lux.network',
branding: PARS_BRANDING,
},
{
name: 'Liquidity',
label: 'Liquidity Network',
vm: '',
network: 'mainnet',
hostnames: [ 'explore.', 'explore.' ],
explorerUrl: 'https://explore.',
apiUrl: 'https://api-explore.',
branding: LIQUIDITY_BRANDING,
},
// Testnet chains
{
name: 'C-Chain',
label: 'Contract Chain',
vm: 'EVM',
network: 'testnet',
chainId: 96368,
hostnames: [ 'explore-test.lux.network', 'explore.lux-test.network' ],
explorerUrl: 'https://explore-test.lux.network',
apiUrl: 'https://api-explore-test.lux.network',
branding: LUX_BRANDING,
},
{
name: 'Liquidity',
label: 'Liquidity Network',
vm: '',
name: 'Zoo',
label: 'Zoo Chain (Testnet)',
vm: 'L2',
network: 'testnet',
hostnames: [ 'explore.test.' ],
explorerUrl: 'https://explore.test.',
apiUrl: 'https://api-explore.test.',
branding: LIQUIDITY_BRANDING,
chainId: 200201,
hostnames: [ 'explore-zoo-test.lux.network' ],
explorerUrl: 'https://explore-zoo-test.lux.network',
apiUrl: 'https://api-explore-zoo-test.lux.network',
branding: ZOO_BRANDING,
},
{
name: 'Hanzo',
label: 'Hanzo AI (Testnet)',
vm: 'L2',
network: 'testnet',
chainId: 36964,
hostnames: [ 'explore-hanzo-test.lux.network' ],
explorerUrl: 'https://explore-hanzo-test.lux.network',
apiUrl: 'https://api-explore-hanzo-test.lux.network',
branding: HANZO_BRANDING,
},
{
name: 'SPC',
label: 'SPC Chain (Testnet)',
vm: 'L2',
network: 'testnet',
chainId: 36910,
hostnames: [ 'explore-spc-test.lux.network' ],
explorerUrl: 'https://explore-spc-test.lux.network',
apiUrl: 'https://api-explore-spc-test.lux.network',
branding: SPC_BRANDING,
},
{
name: 'Pars',
label: 'Pars Network (Testnet)',
vm: 'L2',
network: 'testnet',
chainId: 7071,
hostnames: [ 'explore-pars-test.lux.network' ],
explorerUrl: 'https://explore-pars-test.lux.network',
apiUrl: 'https://api-explore-pars-test.lux.network',
branding: PARS_BRANDING,
},
// Devnet chains
{
@@ -320,45 +337,71 @@ export const CHAINS: ReadonlyArray<ChainEntry> = [
label: 'Contract Chain',
vm: 'EVM',
network: 'devnet',
chainId: 96370,
hostnames: [ 'explore-dev.lux.network', 'explore.lux-dev.network' ],
explorerUrl: 'https://explore-dev.lux.network',
apiUrl: 'https://api-explore-dev.lux.network',
branding: LUX_BRANDING,
},
{
name: 'Liquidity',
label: 'Liquidity Network',
vm: '',
name: 'Zoo',
label: 'Zoo Chain (Devnet)',
vm: 'L2',
network: 'devnet',
hostnames: [ 'explore.dev.' ],
explorerUrl: 'https://explore.dev.',
apiUrl: 'https://api-explore.dev.',
branding: LIQUIDITY_BRANDING,
chainId: 200202,
hostnames: [ 'explore-zoo-dev.lux.network' ],
explorerUrl: 'https://explore-zoo-dev.lux.network',
apiUrl: 'https://api-explore-zoo-dev.lux.network',
branding: ZOO_BRANDING,
},
{
name: 'Hanzo',
label: 'Hanzo AI (Devnet)',
vm: 'L2',
network: 'devnet',
chainId: 36964,
hostnames: [ 'explore-hanzo-dev.lux.network' ],
explorerUrl: 'https://explore-hanzo-dev.lux.network',
apiUrl: 'https://api-explore-hanzo-dev.lux.network',
branding: HANZO_BRANDING,
},
{
name: 'SPC',
label: 'SPC Chain (Devnet)',
vm: 'L2',
network: 'devnet',
chainId: 36912,
hostnames: [ 'explore-spc-dev.lux.network' ],
explorerUrl: 'https://explore-spc-dev.lux.network',
apiUrl: 'https://api-explore-spc-dev.lux.network',
branding: SPC_BRANDING,
},
{
name: 'Pars',
label: 'Pars Network (Devnet)',
vm: 'L2',
network: 'devnet',
chainId: 494951,
hostnames: [ 'explore-pars-dev.lux.network' ],
explorerUrl: 'https://explore-pars-dev.lux.network',
apiUrl: 'https://api-explore-pars-dev.lux.network',
branding: PARS_BRANDING,
},
// Localnet — chainId 1337 (Lux) / 8675312 (Liquidity). Listing localhost /
// 127.0.0.1 / 0.0.0.0 here keeps isWhiteLabelMode() false in local dev so
// the chain + network selectors remain visible without env gymnastics.
// Localnet — chainId 1337. Listing localhost / 127.0.0.1 / 0.0.0.0 here
// keeps isWhiteLabelMode() false in local dev so the chain + network
// selectors remain visible without env gymnastics.
{
name: 'C-Chain',
label: 'Contract Chain',
vm: 'EVM',
network: 'localnet',
chainId: 1337,
hostnames: [ 'explore.localnet', 'explore-local.lux.network' ],
explorerUrl: 'http://localhost:3000',
apiUrl: 'http://localhost:4000',
branding: LUX_BRANDING,
},
{
name: 'Liquidity',
label: 'Liquidity Network',
vm: '',
network: 'localnet',
hostnames: [ 'explore.local.', 'explore.local.' ],
explorerUrl: 'http://explore.local.',
apiUrl: 'http://api-explore.local.',
branding: LIQUIDITY_BRANDING,
},
];
export const NETWORKS: ReadonlyArray<NetworkEntry> = [
@@ -424,6 +467,7 @@ function buildWhiteLabelChain(hostname: string): ChainEntry {
label: branding.brandName,
vm: 'EVM',
network: (getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true' ? 'testnet' : 'mainnet') as 'mainnet' | 'testnet' | 'devnet' | 'localnet',
chainId: Number(getEnvValue('NEXT_PUBLIC_NETWORK_ID')) || 0,
hostnames: [ hostname ],
explorerUrl: `${ protocol }://${ appHost }`,
apiUrl: apiHost ? `${ apiProtocol }://${ apiHost }` : '',
@@ -443,13 +487,11 @@ function getHostname(): string {
// health checks, etc.).
try {
// Lazy require so browser bundles don't pick up the ALS module.
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
const { getRequestHost } = require('lib/requestHost') as { getRequestHost: () => string };
const reqHost = getRequestHost();
if (reqHost) return reqHost;
} catch {
// Falls through to env fallback.
}
} catch { /* Falls through to env fallback. */ }
return getEnvValue('NEXT_PUBLIC_APP_HOST') || 'explore.lux.network';
}
+67
View File
@@ -0,0 +1,67 @@
// White-label brand detection.
//
// Source code in this repo is brand-neutral. Brand selection happens at
// deploy time via `NEXT_PUBLIC_BRAND` env var or by hostname inspection.
// All brand-specific assets (logo, title, favicon, color theme, IAM URL)
// load from `NEXT_PUBLIC_*` env vars — no hardcoded brand strings here.
//
// Supported brands:
// - lux (default)
// - hanzo
// - zoo
// - pars
// - other (any external white-label deploy: branding from env vars)
//
// External deployments select their brand by setting NEXT_PUBLIC_BRAND in
// the runtime environment. They do NOT add their brand here — keeping this
// repo free of any third-party trademarks.
import { getEnvValue } from 'configs/app/utils';
export type WhiteLabelBrand = 'lux' | 'hanzo' | 'zoo' | 'pars' | 'other';
const BRAND_HOSTNAME_SUFFIXES: ReadonlyArray<{ readonly suffix: string; readonly brand: WhiteLabelBrand }> = [
{ suffix: '.lux.network', brand: 'lux' },
{ suffix: '.lux.build', brand: 'lux' },
{ suffix: '.hanzo.ai', brand: 'hanzo' },
{ suffix: '.hanzo.network', brand: 'hanzo' },
{ suffix: '.zoo.ngo', brand: 'zoo' },
{ suffix: '.zoo.network', brand: 'zoo' },
{ suffix: '.pars.network', brand: 'pars' },
];
export function getWhiteLabelBrand(hostname?: string): WhiteLabelBrand {
// 1. Explicit env var wins (CI/CD, k8s ConfigMap)
const envBrand = getEnvValue('NEXT_PUBLIC_BRAND');
if (envBrand) {
const normalized = envBrand.toLowerCase();
if (
normalized === 'lux' ||
normalized === 'hanzo' ||
normalized === 'zoo' ||
normalized === 'pars' ||
normalized === 'other'
) {
return normalized;
}
// Any unknown brand string → 'other' (env-driven white-label)
return 'other';
}
// 2. Hostname-based detection
const host = (hostname || (typeof window !== 'undefined' ? window.location.hostname : '')).toLowerCase();
if (host) {
for (const entry of BRAND_HOSTNAME_SUFFIXES) {
if (host === entry.suffix.slice(1) || host.endsWith(entry.suffix)) {
return entry.brand;
}
}
}
// 3. Default
return 'lux';
}
export function isWhiteLabelExternal(brand: WhiteLabelBrand): boolean {
return brand === 'other';
}
+1 -1
View File
@@ -103,7 +103,7 @@ const TopBar = () => {
'text-[10px] tracking-[0.03em] whitespace-nowrap text-[var(--color-text-secondary)] transition-all duration-300',
isHome ? 'max-h-[14px] opacity-100 mt-[2px]' : 'max-h-0 opacity-0 mt-0',
) }>
Explorer
Lux Explorer
</span>
</div>
</a>