The catalog gains id-dev.hanzo.ai: org hanzo on network devnet, client hanzo-console, its issuer and /v1 its own origin (the dev cloud's IAM), its app https://app-dev.hanzo.ai, its pay surface https://app-dev.hanzo.ai/pay and its telemetry api-dev.hanzo.ai. Three things in the portal sent an org-hanzo host to mainnet, and each now reads the host's own entry: - the alias redirect read one front door per org, so id-dev was sent to hanzo.id with its OAuth request; front doors are now per org per network, and a network with none stays put; - a signed-in visit went to the brand's first app (hanzo.ai), the launcher, billing and the organization page linked mainnet; a host whose entry names `appUrl` links only its network's app and payUrl; - telemetry went to api.hanzo.ai; an entry's `apiUrl` names its gateway. Every mainnet entry names none of the new fields, so every mainnet host resolves, redirects and links exactly as before. Co-authored-by: Hanzo Dev <[email protected]>
Retired — this is a stale copy of
hanzoai/id.Its 4 unique commits are carried into
hanzoai/idfirst — merged onto the default branch or pushed there ascarry/*refs — so nothing here is lost. This copy has no push mirror, so those commits had reached nothing.It also declared
ghcr.io/hanzoai/id, the taghanzoai/idowns, so a push here could have published over it. That declaration is removed.
@hanzo/id
White-label login + identity verification portal. One Vite SPA, four hosts
(hanzo.id, lux.id, zoo.id, pars.id), per-tenant brand resolved from
the request hostname at runtime.
Layout
apps/
web/ Vite + React 19 + @hanzo/gui — the actual SPA
k8s/ Deployment + Service + Ingress (4 hosts, 4 TLS secrets)
pkgs/
shared/ @hanzo/id-shared — TenantConfig + brand resolver
auth/ @hanzo/id-auth — composable login/signup/OTP flows
on top of @hanzo/iam SDK
idv/ @hanzo/id-idv — pluggable identity verification
(Persona, Onfido, Veriff, stub)
legacy-nextjs/ Frozen — predecessor Next.js implementation. Kept
for reference until v0.1.0 ships to production.
Local dev
pnpm install
pnpm dev # http://localhost:5173 (defaults to hanzo brand)
To preview a different brand locally, edit /etc/hosts:
127.0.0.1 lux.id zoo.id pars.id
then visit http://lux.id:5173.
Build
pnpm build # builds apps/web -> dist/
docker build -t ghcr.io/hanzoai/id:0.1.0 .
Adding a brand
- Publish or workspace-link the new per-org brand pkg (must ship
brand.jsonat the package root and match theBrandContractshape inpkgs/shared/src/types.ts). - Add a
DEFAULT_TENANTSentry inpkgs/shared/src/tenant.tsOR put the override in the runtime catalog (IAM_TENANT_CONFIG_JSONenv) so no rebuild is needed. - Add the hostname to
apps/web/vite.config.ts::BRAND_PACKAGES(lets dev + build serve/brand/<pkg>/brand.json). - Add the hostname + TLS secret to
apps/web/k8s/ingress.yaml. - DNS: CNAME or A record → cluster ingress IP.
That's it — no per-brand Worker, no per-brand image, no per-brand deployment. One binary, four brands.
Plugging an IDV provider
import { registerProvider, createPersonaProvider } from '@hanzo/id-idv'
registerProvider(createPersonaProvider({
templateId: import.meta.env.VITE_PERSONA_TEMPLATE_ID,
apiKey: import.meta.env.VITE_PERSONA_API_KEY,
environment: 'production',
}))
The portal stays unchanged — switching providers is a single registration call at boot.