Files
id/legacy-nextjs/lib/iam.ts
zeekayandGitHub 43090cdebf rewrite: Vite + @hanzo/gui monorepo (drops CF Worker + Next.js) (#2)
Replaces the Cloudflare Worker (hanzo.id-worker) and the Next.js portal
with a pnpm monorepo following the ~/work/liquidity/id pattern.

Layout:
  apps/web/        Vite + React 19 SPA, embeds @hanzo/gui shell
    k8s/           Deployment(2) + Service + Ingress (4 hosts, 4 TLS)
  pkgs/shared/     @hanzo/id-shared — TenantConfig, resolveTenant,
                                       loadBrand (browser + node)
  pkgs/auth/       @hanzo/id-auth   — AuthClient (wraps @hanzo/iam REST)
                                       + LoginForm/SignupForm/ForgotForm/OTPForm
  pkgs/idv/        @hanzo/id-idv    — pluggable IDV (stub, persona,
                                       onfido, veriff) behind one
                                       IDVProvider interface
  legacy-nextjs/   Frozen — Next.js predecessor. Delete after v0.1.0 ships.
  Dockerfile       Two-stage: pnpm build → hanzoai/static:0.4.1 serves /spa
  README.md
  LLM.md           Architecture, dev, deploy, cutover plan

Tenant resolution: hostname → TenantConfig (orgId, iamUrl, clientId,
appName, publicOrigin, brandPackage). Built-in defaults for
hanzo.id/lux.id/zoo.id/pars.id; runtime override via
IAM_TENANT_CONFIG_JSON env (served as /config.json at pod startup).

Brand resolution: each per-org brand pkg (@hanzo/brand, @luxfi/brand,
@zooai/brand, @parsdao/brand) ships brand.json. The Vite plugin
brandJsonPlugin emits /brand/<pkg>/brand.json verbatim; the browser
fetches the right one based on the resolved tenant. No bundle bloat.

IDV: stub (default for dev), persona, onfido, veriff. Each adapter
implements `IDVProvider` from pkgs/idv/src/provider.ts. Swap providers
with one registration call at boot — portal code unchanged.

Cutover (separate ops PR — not in this commit):
  1. Tag + push image ghcr.io/hanzoai/id:0.1.0
  2. Apply k8s manifests, cert-manager issues TLS
  3. Remove CF Worker routes for hanzo.id/lux.id/zoo.id/pars.id
  4. CF A records → 129.212.164.5 (hanzo ingress LB)
  5. Archive hanzo.id-worker repo
2026-05-24 06:00:20 -07:00

78 lines
2.2 KiB
TypeScript

/**
* IAM backend URL resolution.
*
* Maps the login portal domain to the correct IAM backend.
* Configurable via env vars for self-hosted deployments.
*/
// Default domain → IAM URL mapping
const IAM_URLS: Record<string, string> = {
'hanzo.id': 'https://iam.hanzo.ai',
'id.hanzo.ai': 'https://iam.hanzo.ai',
'lux.id': 'https://iam.lux.network',
'id.lux.network': 'https://iam.lux.network',
'zoo.id': 'https://iam.zoo.network',
'id.zoo.network': 'https://iam.zoo.network',
'pars.id': 'https://iam.pars.network',
'id.pars.network': 'https://iam.pars.network',
'zen.id': 'https://iam.hanzo.ai',
'id.ad.nexus': 'https://iam.hanzo.ai',
}
// Default domain → org mapping
const ORG_MAP: Record<string, string> = {
'hanzo.id': 'hanzo',
'id.hanzo.ai': 'hanzo',
'lux.id': 'lux',
'id.lux.network': 'lux',
'zoo.id': 'zoo',
'id.zoo.network': 'zoo',
'pars.id': 'pars',
'id.pars.network': 'pars',
'zen.id': 'zen',
'id.ad.nexus': 'adnexus',
}
// Default domain → default app clientId
const APP_MAP: Record<string, string> = {
'hanzo.id': 'hanzo-id',
'id.hanzo.ai': 'hanzo-id',
'lux.id': 'app-lux',
'id.lux.network': 'app-lux',
'zoo.id': 'app-zoo',
'id.zoo.network': 'app-zoo',
'pars.id': 'app-pars',
'id.pars.network': 'app-pars',
'zen.id': 'app-zen',
'id.ad.nexus': 'app-adnexus',
}
export function getIamUrl(host: string): string {
const domain = host.split(':')[0]
// 1. Check env override (for self-hosted / K8s)
if (typeof process !== 'undefined') {
const envUrl = process.env.NEXT_PUBLIC_IAM_URL || process.env.HANZO_IAM_URL
if (envUrl) return envUrl
}
// 2. Static map
return IAM_URLS[domain] ?? 'https://iam.hanzo.ai'
}
export function getOrg(host: string): string {
const domain = host.split(':')[0]
if (typeof process !== 'undefined' && process.env.NEXT_PUBLIC_ORG) {
return process.env.NEXT_PUBLIC_ORG
}
return ORG_MAP[domain] ?? 'hanzo'
}
export function getDefaultClientId(host: string): string {
const domain = host.split(':')[0]
if (typeof process !== 'undefined' && process.env.NEXT_PUBLIC_CLIENT_ID) {
return process.env.NEXT_PUBLIC_CLIENT_ID
}
return APP_MAP[domain] ?? 'hanzo-id'
}