7 Commits
Author SHA1 Message Date
zeekay 0f104401e9 @hanzo/brand 1.5.0: title(id), what a host's wordmark reads
title() answers a brand's title or a Hanzo product's own name: PRODUCTS
holds the one product that has one, team → "Hanzo Team" (hanzo.team),
so @hanzo/app's app({ brand }) reads a name from here instead of keeping
its own brand() and wordmark(). An id that names neither throws.

The package publishes from CI now. Its gate could not pass on a fresh
checkout — pnpm gen read @hanzo/tokens from a sibling ../ui checkout, and
the declaration build died on TypeScript 6's baseUrl deprecation — so it
went out from a laptop. @hanzo/tokens is a dev dependency, tsconfig states
its types and the deprecation, and publish.yml reads the npm token from
KMS. styles/variables.css is regenerated from @hanzo/tokens 1.1.1, which
is what every build already wrote.
2026-10-22 20:04:53 -07:00
zooqueenandHanzo Dev 9b817c6652 asset URLs name their version, and Lux points at the package that has it
Every logoUrl and faviconUrl floated on `@latest`, which resolves when the
browser asks: the bytes behind a login page could change with no release here,
and nothing recorded which version a visitor got. jsDelivr also keeps serving a
path from a version that no longer ships it — @luxfi/brand@latest answers
assets/logo/logo.svg with a 120-byte triangle that no explicit version of that
package contains, because every version from 1.0.7 dropped assets/ entirely.

Lux publishes its mark in @luxfi/logo, which its own brand.json already names,
so that is where this points now: svg/lux-icon-white.svg and svg/lux-favicon.svg
at 1.0.8, both verified to serve. Hanzo pins 1.4.7 and Zoo 1.4.1, likewise
verified. @parsdao/brand ships no assets/ at any version, so its URL is a 404
either way; pinning it names the gap rather than leaving an alias to hide it.

A test walks the registry and refuses a URL that omits its version.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 22:11:51 -07:00
zeekay f2bee83747 a brand is called Brand
`BrandContract` said the same word twice: every type is a contract, so the suffix
carried no information a reader did not already have from the name and the
`interface` keyword. The pair it belongs to says what each half is on its own --
`BrandIdentity` is the authored registry entry, `Brand` is what a consumer holds --
and `toBrand` names the projection between them.

Nothing outside this package and @hanzo/id referred to either name, so this is a
rename and not a migration.
2026-08-24 11:00:47 -07:00
zeekayandHanzo Dev 47abb8b832 Write the null-byte spoofing hosts as escapes
The host-impersonation cases held literal NUL bytes, which made the test
file binary. grep reports no matches in a binary file without saying it
skipped it, so the suite proving we reject null-byte host spoofing was
itself invisible to any search of this repo. Same characters, same
assertions.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-23 19:48:38 -07:00
zeekayandHanzo Dev d3181e7aad fix: one x.com handle for Hanzo, pinned in all three sources
Hanzo's identity is written in three places and all three disagreed:
brand.json said x.com/hanaboroshi, the registry said x.com/hanzoai, and
brandConfig still pointed at the dead twitter.com domain. It is hanzoai, which
is also what every sibling does — luxdefi, zoo_labs, parsnetwork.

The consistency test pinned six fields and none of the socials, so this drifted
in silence. It now pins twitter and github across brand.json, the registry AND
brandConfig, and asserts the host is x.com. Verified it catches the old value:
restoring hanaboroshi fails two tests, exit 1.

Folded into the unpublished 1.4.5 rather than bumping again.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 22:43:38 -07:00
zeekay 387a35e36d brand: harden the white-label host resolver (FQDN root dot + fuzz suite)
normalizeHost strips trailing FQDN root dot(s) after the port strip, so a real brand FQDN (lux.network.) resolves to ITS brand instead of the default — without ever widening a match, since stripping root dots cannot change a host's registrable domain. Expand the resolver test across the attack surface: homoglyph/IDN/punycode, suffix-confusion, control-char/null/injection, IP/localhost, trailing-dot FQDN, multi-suffix, and a broad independent-model sweep. Every non-exact host falls to the default; no sibling-brand leak. 19/19 green.
2026-07-21 10:57:32 -07:00
zeekay 8689537969 feat(brand): white-label registry + host resolver (hanzo/lux/zoo/pars)
Add the multi-brand layer the fleet already expected (id-shared's
TenantConfig.brandPackage names @hanzo/brand; console/brands.ts modeled it):

- brand-id.ts: BrandId + HOST_BRANDS suffix table + brandFromHost(host).
  Exact suffix / dot-boundary match — lookalike hosts (lux.network.evil.com)
  cannot impersonate a brand. Pure module, no React/data deps.
- marks.ts: per-brand mark geometry. The Hanzo mark is consumed from
  @hanzo/logo (MARK_PATHS/MARK_VIEWBOX) — never re-typed. Lux/Zoo/Pars are
  the console v1 currentColor marks. renderMarkSVG() string helper.
- registry.ts: BrandIdentity + BRANDS registry + getBrand(host) +
  toBrandContract() (projects to @hanzo/id's BrandContract).
- logos.tsx: stop re-typing the block-H — consume @hanzo/logo; add the
  white-label <BrandLogo> component.

Fix: normalizeHost trims before stripping the port (padded host:port kept
its port under the old order — latent in the console source this consolidates).

13 node:test cases against the built dist: white-label invariant (distinct
marks, no Hanzo H in lux/zoo/pars), suffix-confusion attackers, port/case
insensitivity, brand.json<->registry consistency. type-check + build green.

Version 1.3.4 -> 1.4.0 (additive).
2026-07-21 09:27:10 -07:00