Files
zeekayandHanzo Dev c893cbb54d Serve the marks from the Sites plane, and delete the app in front of them
The cdn is a directory of files. public/ is the whole of it, and the Next app
around it produced that directory copied verbatim plus one page reading
"LUX cdn - coming soon" -- output: 'export' made next build an expensive cp -r,
and @hanzo/ui, Tailwind, mobx, splinetool and firebaseui were carried to render
three lines of text. src/, next.config.js, tsconfig.json, package.json and a
446 KB lockfile go with it.

Two other serving paths go too. deploy.yml published the same bytes to GitHub
Pages, and docker.yml built ghcr.io/luxfi/cdn onto hanzoai/static so a pod could
hold a file. hanzoai/ingress reads s3://hanzo-sites/lux/lux-cdn directly, which
is how every other static host in the estate already reaches the edge.

site.yml is the lane: checkout, then hanzoai/ci's site action. Nothing is
re-checked in it -- bin/site refuses an empty directory, measures the completion
manifest against the edge limit before sending a byte, and downloads the root
back after the flip, and a copy of those checks here is a copy that drifts.

The slug is lux-cdn. `cdn` is on cloud's reserved-label list beside api, admin
and gateway, so that no site can be published at cdn.<apex> and shadow real
infrastructure; asking for it answers 400.

public/index.html is the one page written by hand, and it is not decoration:
bin/site downloads the site root after the flip and fails the publish if it does
not answer, so the root has to be a file. Its own mark is served by the host it
describes.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 10:34:30 -07:00

4.2 KiB
Raw Permalink Blame History

cdn

public/ is the site. There is no build, no image, and no server here.

What it holds

path what
exchange/icon-svg, exchange/icon-png 100 token marks, 513×513, keyed by symbol. What the old lux.exchange fetched.
exchange/tokens-lux/tokens.json 57 entries — chainId, address, logoURI — the address→mark table. Every cdn.lux.network URI in it resolves inside this tree.
bridge/currencies/{lux,zoo} 203 marks, a strict superset of exchange/icon-svg and byte-identical on every overlap.
bridge/networks 113 chain marks, <chain>_<network>.png.
img, commerce, video, pdf, icon brand artwork and product media.
dwallet, xwallet assets shipped clients fetch by hardcoded URL.

exchange/icon-svg/lux.svg and icon-png/lux.png are the brand mark — a white downward triangle on black. Not the purple two-triangle placeholder; that one is not in this repo.

How the hosts serve it

hanzoai/ingress reads s3://hanzo-sites/lux/lux-cdn directly through a staticFiles middleware, declared in luxfi/universe at deploy/lux-cdn/lux-cdn.yaml. One rule matches both cdn.lux.cloud and cdn.lux.network. There is no Deployment, because a static host is not a pod. Cloudflare fronts both names; both A records already point at the tunnel.

spaMode is off, and that is the load-bearing choice. On, every missing path would answer 200 with index.html — an HTML document where a PNG was asked for, cached by everything downstream and noticed by nobody. Off, a missing mark is a 404, which is the only thing that lets a caller report it missing instead of drawing it.

Cache-Control is the ingress default: max-age=86400 for files, no-cache for HTML, with an ETag from the object store. Nothing here sets it — the serving path composes it, and a second copy would drift.

Publishing

.github/workflows/site.yml on push to main: checkout, then hanzoai/ci/.github/actions/site@v1 with slug: lux-cdn. By hand, from the operator session:

HANZO_DEPLOY_TOKEN=$(hanzo auth token) CURL_HOME=<dir with .curlrc: header = "X-Org-Id: lux"> \
  /Users/z/work/hanzo/ci/bin/site lux-cdn public

The slug is the address. The prefix is derived server-side as <org>/<slug>, so a hanzo-org token publishes to hanzo/lux-cdn, which nothing serves.

The slug is lux-cdn and not cdn because cdn is on cloud's reserved-label list (apps/sites/reserved.go) with api, admin, gateway and the brand terms — nobody may publish a site at cdn.<apex> and shadow real infrastructure. Asking for it answers 400 slug is a reserved subdomain.

A publish is a rollout. The plane writes into the prefix the edge is already serving, so every object is live the moment S3 accepts it, and the completion's keys manifest is what cloud reconciles the prefix against — a file the checkout stopped carrying is deleted from the host.

What was removed, and why

A Next 14 app (src/, next.config.js, package.json, a 446 KB lockfile, Dockerfile) whose entire output was public/ copied verbatim plus one document reading "LUX cdn — coming soon". output: 'export' made next build an expensive cp -r; @hanzo/ui, Tailwind, mobx, splinetool and firebaseui were carried to render three lines of text. Two serving paths went with it: .github/workflows/deploy.yml published the same bytes to GitHub Pages, and docker.yml built ghcr.io/luxfi/cdn onto hanzoai/static. The Sites plane is the one way; the others were a second and a third.

public/index.html is the one page that stayed, hand-written. It is not decoration: bin/site downloads the site root after the flip and fails the publish if it does not answer, so the root must be a file.

The old cluster declaration is gone too — k8s/lux-k8s/cdn/cdn.yaml in luxfi/universe served these hosts from an in-cluster S3 bucket on DOKS, and that account is suspended.

Conventions

Names come from first principles. No compound words where one word says it.

/v1/ only, on api.*. Never an /api/ path segment, never a v2.

Secrets live in Hanzo KMS. Identity is Hanzo IAM.

Patch versions move forward one at a time. A published version is never overwritten and never floats.