Skip to content
sign in

==> read the decisions

ADR-0003: The CDN is a build product, not a site

Status: Accepted

Fleet static sites deploy through the inventory: sites.<domain> becomes a bare repo on the hub whose hook rsyncs to a docroot. The CDN could have used that path.

  • The CDN is a Nix derivation owned by the platform, served from the store.
  • It carries shared assets (themes, icons, i18n) and immutable per-user release artifacts under t/<user>/<release>/.
  • Hostname, vhost and certificate are FLEET facts. The bytes are the PLATFORM's.
  • Atomic switch and rollback through generations, and the content lands in the existing binary cache instead of being re-marshalled.
  • Generated CSS stops needing to be committed output.
  • The inventory stays what it is - fleet facts. Users, projects and live releases are platform state, in the control database.
  • The CDN is public by nature. No secret may ever be shipped in a frontend bundle.
  • Release segments should be content-addressed so the roster is not enumerable by URL.
  • An inv.sites entry: it works, but it publishes a mutable docroot with no rollback for content the platform owns.

Revision (2026-10-01): users' trees are served from their release profiles

Section titled “Revision (2026-10-01): users' trees are served from their release profiles”

The users' artifacts are no longer IN the CDN derivation. Since user repos left the fleet's configuration (nixos2 ADR-0013), a user's release -- including their UI at t/<user>/<hash>/<slug>.js -- is installed as a generation of the Nix profile /nix/var/nix/profiles/cs-users/<user>, and the CDN vhost serves /t/<user>/* straight out of that profile. The derivation holds only the platform's own assets (themes, icons, dictionaries, favicon); mkCdn no longer takes extraTrees: nothing user-built ships inside the CDN derivation.

What stands: the bytes are still store paths, switched and rolled back as generations -- the profile's rather than the system's -- and the segment is still a content hash, so the roster is not enumerable by URL. What is new:

  • A user's release moves the CDN without a fleet deploy, and a route exists for every roster user before their first release; until then it 404s.
  • The CDN serves only the profile's current generation, so an old hash 404s after the switch. The old generations are still on disk for rollback, but nothing routes to them. The hash is immutable while it is served, not forever: a page still holding the previous release's ui_path asks for a file the new generation does not have. That is why the registry is reconciled from the LIVE generation's manifest, the same one the CDN serves.