ADR 0034. Documentation site: Docusaurus, OpenAPI, Scalar; internal first
Status
Accepted.
Context
Docs were plain Markdown in docs/, spec/ and per-service READMEs, with no
site, link checking or API specification. Routes are hard-coded v1/...
prefixes validated by hand-written TypeScript; there was no OpenAPI. Partners
need integration guides and an API reference; founders need architecture,
decisions and operations in one place, with honest implementation status.
Decision
- Docusaurus (
docs-site/, ownpackage.jsonand lockfile) rendersdocs/in place. No files move.markdown.format: 'detect'keeps.mdas CommonMark;.mdxopts into MDX. Broken links and anchors fail the build. Links to files outside the site are rewritten to GitHub (docs-site/plugins/remark-repo-links.mjs). - OpenAPI is the source of the API reference, generated code-first with
@nestjs/swaggerdecorators (services/api/src/openapi/). Two documents:internal.json(every route; indocs-site/static-internal/, never copied into the public build) andpartner.json(operations markedx-audience: partner). Both are committed underdocs-site/static/openapi/;npm run openapi:checkfails when they differ from the code. Unbuilt endpoints are not added to OpenAPI. - Scalar renders both documents, self-hosted: the renderer is vendored
from a pinned
@scalar/api-referenceat build time, not loaded from a CDN. Request testing and the client button are off and no proxy is configured, until a sandbox exists. - Internal first, fail closed. Every page is internal unless its front
matter says
audience: public. The public build (DOCS_AUDIENCE=public) contains only those pages and the partner reference, and fails if a public page links to an internal page or into the repository. The internal site is hosted on Cloudflare Pages behind Cloudflare Access, which is already used for other hosts. CI deploys it on push tomain; setup is in Contributing to the docs. The public build is not deployed yet. - Status labels. Page front matter
status: implemented | in-progress | proposed, optionaldemo: trueandverified: <commit>; section-level> **Status: …** — evidence: …lines on mixed pages. A status must cite code or tests; a mockup alone never makes something implemented. - Spec and glossary (
spec/PLAYERPAL_AUTH_IDENTITY_SPEC.md,CONTEXT.md) are rendered on the internal site only, from copies made at build time. They stay where they are and the spec still wins on conflict.
Defaults chosen for open questions
These were taken as defaults when the site was built and can be revisited:
| Question | Default |
|---|---|
| Public site now or later | Later. No page is marked public. |
| Code-first or spec-first OpenAPI | Code-first |
| Sandbox for request testing | None yet; reference is read-only |
| PR previews | None; local preview plus CI build |
| App location | docs-site/ with its own lockfile |
| Publish the spec | No; internal build only |
| Partner doc scope | Includes routes behind the shared machine key (activity batches and uploads, link intents and results, aliases) because integrations call them today. Revisit before any public release: per-partner credentials do not exist for these routes yet. |
Still undecided and not answered by this ADR: retention schedule and purpose limits, reward-partner scope, the canonical redemption state model, packaging and pricing. Pages describing them say so.
Amendment: shareable public site
The site now serves partners, prospects and investors as well as the team.
Public pages are .mdx under product/, developers/, analytics/,
rewards/ and trust/, written to contributing/style.md:
reader-facing status labels (Available / In development / Planned), evidence
kept in MDX comments, no repository links or internals. The PlayerPal brand
theme, a landing page, offline search and a social preview image are part of
the build. npm run check:public scans the public build output for hostnames,
infrastructure vendors, configuration keys, non-placeholder IDs, repository
links and the internal OpenAPI document, and runs in CI.
Consequences
- A behaviour change updates its page and, for endpoints, the regenerated OpenAPI files in the same PR; CI checks the OpenAPI part and the KPI IDs.
- The API gains decorators and hand-written JSON schemas that must track the hand-written validators until validation moves to a schema library.
- Internal hosting needs a Cloudflare Access application before the docs image
is added to
build-push.ymlandk8s/apps/.