Skip to content
sign in

==> read the decisions

ADR-0022: The documentation host is a Starlight surface of the web project, built from what already exists

Status: Accepted (2026-10-15). Extends ADR-0012 (Astro renders the surfaces). Amended 2026-10-15 (operator): the docs host is an OIDC client of its own (ADR-0024) for the reader's own projects; try-it is dropped for now. Implemented 2026-10-15 except the reader's own projects. Amended 2026-10-16 by ADR-0026: both API references are the platform's own renderer inside Starlight (starlight-openapi dropped); a project's reference is one page per operation; the reader's projects are rendered.

The platform needs a documentation host, docs.<domain>: the platform's API, the shared library, how to write a project, and -- for a signed-in reader -- their own projects' APIs. It also needs an llms.txt, so a language model can read the same documentation. Documentation written separately from the code drifts from it.

  • Starlight, the Astro documentation framework, as a third surface of the existing web project: the web server tells docs. apart by host, as it does the homepage and the app. Same theme tokens, same bar (through Starlight's header override), same deployment. A short spike checks that Starlight's styles stay off the app's pages; if they do not, docs becomes a separate Astro project reusing the theme and the bar.
  • Every page comes from something that already exists:
    • the API reference, from the OpenAPI documents (ADR-0021), through starlight-openapi;
    • the shared library's reference, from its doc comments, through TypeDoc;
    • the decisions, from these ADRs;
    • only the guides (getting started, writing a project, the repo layout) are written for the site.
  • llms.txt and llms-full.txt are generated from the same sources (starlight-llms-txt for the prose, the Markdown rendering of each OpenAPI document for the APIs).
  • Public docs are always public: the guides, the platform's API reference, the decisions, search and llms.txt stay prerendered and are served as files, with no sign-in.
  • A reader's own projects are documented at the docs host too (/projects/...), when signed in. The docs host is an OIDC client of the identity provider of its own (ADR-0024), like the app: the platform's API is its backend through a door of its own (docs.<domain>/api/*), holds its client secret, and keeps its tokens in host-only httpOnly cookies (Lax, with a Strict CSRF cookie). A /projects page is rendered per request by the web server, in Starlight's page layout, from the project's OpenAPI document, which the API reads from the reader's live release. Without a docs session, opening it starts the sign-in -- silent when the reader is signed in at the identity host. The edge serves the static site and sends /projects/* to the web server and /api/* to the API.
  • No try-it for now. Each endpoint shows a copyable request to api.<domain> with Authorization: Bearer <your personal access token>. The docs never call the API from the browser, so the API allows no cross-origin requests from them.
  • A signed-in reader sees the bar's link to the app (the terminal button), from the signed-in hint cookie, which a static page reads in its theme script.
  • User-written text is sanitised. Route descriptions are the users' Markdown; rendered into the /projects pages they are sanitised, and every page sends a strict Content Security Policy.
  • The docs' pages are prerendered -- Pagefind indexes the built pages, so search needs them static -- and the edge serves them as files from the web build's docs output, never through the web server. The app's own / is pages/app.astro (the middleware's rewrite), so / is the docs'.
  • The theme is the shared one; the docs read the cs-prefs cookie in a small inline script before the first paint -- the one surface that cannot resolve it on the server.
  • Each page's policy is a <meta>: Astro's hashes, plus the hash of every inline script on the page (Starlight's own included) added after the build; 'wasm-unsafe-eval' for the search, on the docs only.
  • The decisions are converted from these AsciiDoc files at build time; the API reference is the API build's own openapi.json, and its Markdown is served as /reference/api.md.
  • A route's documentation is its definition; changing the code changes the docs.
  • The docs host holds no session or token endpoint of its own; it is one more client.
  • Starlight's look is overridden to the platform's; its search (Pagefind), navigation and en/ru support come with it.
  • Scalar (or another hosted or embedded API reference). A second look to theme, and only for references; Starlight covers the prose too.
  • Our own documentation framework. Search, navigation, schema rendering and i18n to build and keep.
  • Pasted personal access tokens in "try it". A long-lived, full-reach credential in page memory, when a short-lived one is available.
  • A proxy on the docs host. Calling api. directly needs no forwarding code to keep narrow, and the request a reader sees is the one a script would make.