==> 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.
Context
Section titled “Context”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.
Decision
Section titled “Decision”- One Astro project,
web/, serves both hosts, as one unit (cs-web) on the edge. Middleware picks the surface byHost. 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.jsand 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.assetsPrefixis the CDN, the client build lands in the derivation'sstaticoutput, served immutable for a year, exactly as/_next/staticis 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 nonode_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.
Consequences
Section titled “Consequences”- 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.jsandsite/index.htmlare deleted. - shadcn components stay React (
.tsx), now shared with projects (ADR-0017). - Astro's dev server replaces
tools/devserver.pyfor the homepage.
Alternatives rejected
Section titled “Alternatives rejected”- 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.