==> 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.
Context
Section titled “Context”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.
Decision
Section titled “Decision”- 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.
- the API reference, from the OpenAPI documents (ADR-0021), through
llms.txtandllms-full.txtare generated from the same sources (starlight-llms-txtfor 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.txtstay 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/projectspage 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>withAuthorization: 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
/projectspages they are sanitised, and every page sends a strict Content Security Policy.
As built
Section titled “As built”- 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
docsoutput, never through the web server. The app's own/ispages/app.astro(the middleware's rewrite), so/is the docs'. - The theme is the shared one; the docs read the
cs-prefscookie 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.
Consequences
Section titled “Consequences”- 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.
Alternatives rejected
Section titled “Alternatives rejected”- 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.