Files
zeekay e8e4ea81e4 fix(type): the console carries its own faces, because the CDN never had them
The shipped default fetched Zen from cdn.hanzo.ai. Nothing publishes it there:
every /fonts/zen/* path answers 404, while /fonts/geist/1.7.2/ still answers 200
because those bytes were put there by hand. So every first paint of this console
asked for two files that are not there and fell to the system stack — in a
compliance product whose own tests assert it is set in Zen.

The self-hosting path already existed as FONT_ORIGIN=, and it is now the
default: vite emits the two faces out of @hanzo/font at the same paths, served
same-origin. The CDN stays reachable through FONT_ORIGIN for the day the fleet
publishes there, and the URL contract is still pinned so a rename cannot pass
unnoticed.

Three things follow, and each is a tightening rather than a trade:

font-src drops its host. A cross-origin face has to be named in the policy; a
face we carry does not, so the cheapest possible widening of a CSP is now no
widening. csp.txt and the chart's copy move together, as they must.

The cross-origin assertion gets stronger. It used to allow exactly one origin,
ours; it now expects the set to be EMPTY — no subresource loaded from anywhere
but here. An origin nothing fetches from cannot serve the wrong bytes, go down,
or be asked for a beacon.

And the pin moves to @hanzo/font 1.9.2. 1.9.0 and 1.9.1 shipped a zen.css whose
:root block was missing, so the two custom properties a non-Next consumer reads
were undefined — which computes to an invalid value and falls to the OS stack,
silently.
2026-08-28 11:14:32 -07:00

2 lines
308 B
Plaintext

default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; object-src 'none'; script-src 'self'; style-src 'self' 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU='; img-src 'self' data:; font-src 'self'; connect-src 'self' https://api.hanzo.ai https://hanzo.id; manifest-src 'self'