// The white-label contract, as executable assertions. // // Run: `pnpm test` (node:test + native TypeScript stripping — no test // framework, no new dependency). // // Every expectation here was verified against the live IAM before it was // written down; see the probe table at the top of lib/brand.ts. This file is // what stops it drifting: change a client id or a host and the build's test // step says so, instead of a customer discovering it at the login screen. import { strict as assert } from "node:assert" import { describe, it } from "node:test" import { getBrand, getBrandKey } from "./brand.ts" // host → [brand key, IAM issuer, OIDC client id]. The five hosts this one // image must serve, plus the aliases that ride along with them. const CONTRACT: [string, string, string, string][] = [ ["bootno.de", "bootnode", "https://id.bootno.de", "bootnode-platform"], ["web3.hanzo.ai", "hanzo", "https://hanzo.id", "hanzo-web3"], ["lux.cloud", "lux", "https://lux.id", "lux-cloud"], ["zoo.cloud", "zoo", "https://zoolabs.id", "zoo-cloud"], ["cloud.zoo.network", "zoo", "https://zoolabs.id", "zoo-cloud"], // Aliases that must inherit their parent's brand. ["www.bootno.de", "bootnode", "https://id.bootno.de", "bootnode-platform"], ["nodes.lux.cloud", "lux", "https://lux.id", "lux-cloud"], ["console.zoo.cloud", "zoo", "https://zoolabs.id", "zoo-cloud"], ["cloud.pars.network", "pars", "https://pars.id", "pars-app"], ] describe("white-label by hostname", () => { for (const [host, key, issuer, clientId] of CONTRACT) { it(`${host} → ${key} / ${clientId}`, () => { const brand = getBrand(host) assert.equal(getBrandKey(host), key) assert.equal(brand.key, key) assert.equal(brand.iam.url, issuer) assert.equal(brand.iam.clientId, clientId) }) } it("serves the Hanzo API from the console's own origin", () => { // web3.hanzo.ai routes /v1 to the bootnode service, so a Hanzo page never // calls a bootno.de address. const hanzo = getBrand("web3.hanzo.ai") assert.equal(hanzo.apiUrl, "https://web3.hanzo.ai") assert.equal(hanzo.wsUrl, "wss://web3.hanzo.ai") assert.equal(getBrand("bootno.de").apiUrl, "https://api.bootno.de") }) it("shows a node fleet only on the brand that operates one", () => { assert.equal(getBrand("lux.cloud").fleet, "lux") for (const host of ["bootno.de", "web3.hanzo.ai", "zoo.cloud", "cloud.pars.network"]) { assert.equal(getBrand(host).fleet, null, host) } }) it("does not regress the one login that works today", () => { // lux.cloud + lux-cloud + https://lux.cloud/auth/callback is the triple // that answers 302 on lux.id and lands a real user on /dashboard. If this // assertion ever fails, the merge broke a working production login. const lux = getBrand("lux.cloud") assert.equal(lux.iam.url, "https://lux.id") assert.equal(lux.iam.clientId, "lux-cloud") }) it("resolves unknown hosts and localhost to the neutral default", () => { for (const host of ["localhost", "127.0.0.1", "example.com", ""]) { assert.equal(getBrandKey(host), "bootnode") } }) it("matches on exact host or dot-suffix, never on substring", () => { // The old resolver used `hostname.includes(match)`, which would hand a // real brand's OIDC client id — and therefore its login — to any host an // attacker can name. These must all fall through to the default. for (const host of [ "lux.cloud.attacker.example", "zoo.cloudy.example", "notbootno.de.example", "web3.hanzo.ai.evil.test", "xlux.cloud", ]) { assert.equal(getBrandKey(host), "bootnode", host) } }) it("gives every brand its own mark, wordmark and favicon", () => { // One image, five identities: a shared /logo.svg for all of them would // mean bootno.de's mark showing up on zoo.cloud. const seen = new Set() for (const host of ["bootno.de", "web3.hanzo.ai", "lux.cloud", "zoo.cloud", "cloud.pars.network"]) { const brand = getBrand(host) assert.match(brand.logoIcon, new RegExp(`/logo/${brand.key}-icon\\.svg$`)) assert.match(brand.logoWhite, new RegExp(`/logo/${brand.key}-logo-white\\.svg$`)) assert.equal(brand.favicon, brand.logoIcon) assert.ok(!seen.has(brand.name), `duplicate brand name ${brand.name}`) seen.add(brand.name) } assert.equal(seen.size, 5) }) it("flags exactly the marks that cannot paint themselves", () => { // Measured from public/logo/*-icon.svg: bootnode is fill="currentColor" // and lux is fill="white" — both need the current-text-colour mask, or one // is invisible on black and the other on white. The rest ship a palette. assert.equal(getBrand("bootno.de").monoMark, true) assert.equal(getBrand("lux.cloud").monoMark, true) assert.equal(getBrand("zoo.cloud").monoMark, false) assert.equal(getBrand("web3.hanzo.ai").monoMark, false) assert.equal(getBrand("cloud.pars.network").monoMark, false) }) it("carries a complete, non-empty config for every brand", () => { for (const host of ["bootno.de", "web3.hanzo.ai", "lux.cloud", "zoo.cloud", "cloud.pars.network"]) { const b = getBrand(host) for (const field of ["name", "shortName", "tagline", "domain", "apiUrl", "statusUrl", "networkName"] as const) { assert.ok(b[field], `${host}: ${field} is empty`) } // Partial catalog entries used to produce `undefined` colours and crash // rendering, which is why no catalog was ever shipped. assert.ok(b.colors.accent && b.colors.accentEnd) assert.ok(b.iam.idLabel) assert.match(b.apiUrl, /^https:\/\//) } }) })