Skip to content
sign in

==> read the decisions

ADR-0012: Astro renders both surfaces, on the server

Status: Accepted (2026-10-14). Replaces the Next.js app and the hand-written homepage (site/index.html). Implemented 2026-10-15.

There are two surfaces: the homepage at the apex, a hand-written static file with its own copy of the preference ladder and a pre-paint script, and the app at app., a Next.js server whose pre-paint boot.js exists because a static file cannot read a cookie before it paints (ADR-0007). The app has five pages (home, login, profile, a project's page, not found); most of its weight is a framework it uses little of.

  • One Astro project, web/, serves both hosts, as one unit (cs-web) on the edge. Middleware picks the surface by Host. Layouts, the navbar and the preference controls exist once.
  • Everything is rendered on the server, per request. The middleware reads the preference cookie (ADR-0013) and renders <html lang data-theme class> on the first byte, so neither surface needs a pre-paint script: boot.js and the homepage's inline ladder are deleted.
  • No JavaScript unless a component asks for it. Astro sends none by default. Interactive parts are React islands (client:load): the login form, the preference controls, the profile's token and session managers, the project host. The homepage ships no React; its controls are a small script of the platform's own.
  • Assets are a build product on the CDN (ADR-0003, unchanged in spirit): build.assetsPrefix is the CDN, the client build lands in the derivation's static output, served immutable for a year, exactly as /_next/static is today. Island and stylesheet URLs carry the prefix (verified).
  • The server is one Bun bundle (ADR-0011): the node adapter's standalone output, bundled again into server/entry.js, run by Bun with no node_modules.
  • Project UIs are unchanged: still custom elements in a shadow root, loaded from the CDN (ADR-0004); the project host island mounts them.
  • /api/* is still the API on the hub, proxied by Caddy; Astro renders, it does not serve data.
  • The homepage is no longer static. A request now reaches a server on the edge instead of a file, which is what lets it render the visitor's preferences without a script. Its cold load (about 450 ms today, document plus themes, CSS and icons) is measured again after the switch.
  • Next.js, its eslint plugin, next.config.ts, src/app/, api.server.ts, public/boot.js and site/index.html are deleted.
  • shadcn components stay React (.tsx), now shared with projects (ADR-0017).
  • Astro's dev server replaces tools/devserver.py for the homepage.
  • Two Astro projects, one per surface: the navbar, the controls and the layout would be shared through a library and deployed twice, for isolation nothing needs.
  • Keeping the homepage static. It would keep the pre-paint script, the one thing this change is for.