docs: design for the documentation site

This commit is contained in:
2026-07-28 15:20:07 +01:00
parent 6070972f9a
commit 9db16522e3
@@ -0,0 +1,150 @@
# Documentation site — design
**Date:** 2026-07-28
**Status:** approved
## Problem
Vantage has no user-facing documentation. Everything an operator needs — how to
install self-hosted, how to enrol an agent, what a workflow step is, how a
licence gets issued — lives either in `CLAUDE.md` (written for contributors, not
users) or in the code. The marketing site sells the product and the control
plane runs it; neither explains it.
## Solution
A fourth Next-adjacent frontend, `docsite/`, built with Docusaurus v3 and shipped
alongside the marketing site.
### Placement and deployment
- Lives at repo root as `docsite/`.
- Served at **`docs.hostxtra.co.uk`**, deliberately outside
`*.vantage.hostxtra.co.uk` — that namespace is per-tenant instance subdomains
and `APP_ROOT_LABEL` resolves an org from the label before `vantage`. Same
reasoning that puts `adminsite/` on `vantage-hq.hostxtra.co.uk`.
- Added to **`deploy/docker-compose.site.yml` only**, published as `3005`.
`deploy/docker-compose.yml` (the self-hosted install) must never mention it,
exactly as it never mentions `site`, `sitesvc`, `admin` or `adminsite`.
- Added to `.gitea/workflows/server-deploy.yml` as a seventh image, rebuilding
on `^docsite/` only, build context `docsite/`.
### Runtime
Docusaurus emits a fully static site, so unlike `site/`, `web/` and
`adminsite/` there is no Node server at runtime. Two stages:
1. `node:26-alpine` builder — `npm ci && npm run build``/app/build`.
2. `nginx:alpine-slim` runner — copies `build/` to `/usr/share/nginx/html`,
plus a small `nginx.conf` giving `try_files` a 404 fallback to Docusaurus's
`404.html` and long cache headers on `/assets/`.
`nginx:alpine-slim` is roughly 12MB against `caddy:alpine`'s ~50MB, and nothing
here needs Caddy's automatic TLS — the host proxy already terminates it.
Build args, baked at build time the same way `site/`'s are:
| Arg | Purpose |
| --- | --- |
| `DOCS_URL` | site `url`; defaults to `https://docs.hostxtra.co.uk` |
| `APP_URL` | navbar link to the control plane |
| `HQ_URL` | navbar link to the HQ portal |
### Theme
`docsite/src/css/custom.css` carries `site/app/globals.css`'s token blocks
**copied verbatim** — same names, same values — and maps Docusaurus's `--ifm-*`
variables onto them. Docusaurus stamps `data-theme="light|dark"` on `<html>`,
which is the same selector `site/`'s dark block already keys on, so the built-in
toggle works with no extra wiring. Light is the default, matching `site/` and
`adminsite/`.
This makes a **fifth** copy of the token block (`site/`, `adminsite/`, `web/`
dark-only, `shared/mail/templates/layout.html.tmpl` as literal hex, and now
`docsite/`). Nothing enforces the match; `CLAUDE.md`'s Frontend section is
updated to say so. No component in `docsite/` may carry a hex value.
Search is `@easyops-cn/docusaurus-search-local` — index built at compile time,
served from the same origin. No Algolia account, no external host, nothing to
key or rotate.
Docs-only mode: `routeBasePath: "/"`, blog disabled, no tutorial scaffolding.
## Content
Sidebar is authored explicitly in `sidebars.ts` rather than autogenerated, so
ordering is a decision rather than a filename accident.
### Getting Started
| Page | Covers |
| --- | --- |
| `what-is-vantage` | The control plane, the agent, what problem each solves |
| `cloud-vs-self-hosted` | The two deployments, what differs (licensing, HQ-managed users, reaping) |
| `self-hosted-install` | Prereqs (Docker, external MongoDB, DNS, TLS), `docker-compose.yml`, required env, `docker compose up -d` |
| `first-login` | `/setup` bootstrap, first org and owner |
| `first-server` | `POST /servers/new`, the install one-liner, Linux and Windows, watching it flip to `active` |
| `claim-free-licence` | Linking the install to an HQ account, `claim-free` |
`self-hosted-install` is the page the section exists for; it names every
required environment variable with its consequence-of-omission, in particular
`GRPC_HOST` (boot fails, no default is safe) and `KEY_ENCRYPTION_KEY`.
### Vantage (the application)
`servers` (agent install Linux/Windows, inventory, OS updates, agent
self-update) · `ssh-keys` (upload, generate-on-server, assign, revoke, what the
agent writes and when) · `workflows` (step library, default steps and why they
are read-only, the designer, running, live logs, `on_failure`, `output_env`,
workspaces, log retention) · `monitors` (the four check types, server vs agent
runner, retries, incidents, uptime rollups) · `notification-channels` (five
types, testing) · `secrets` (vault, `secret_refs` in steps, the ESO read path
and its bearer token) · `browser-console` (SSH/RDP/VNC, one-time tokens) ·
`audit-log` · `settings` (members and roles, OIDC per org, retention, ESO token,
licence).
### Vantage HQ (the portal)
`accounts-and-signup` (account-first signup, email verification, an account is
a team) · `people-and-roles` (owner/admin/member, invitations, accepting) ·
`cloud-instances` (create, overview, granting members and what a grant actually
is) · `self-hosted-instances` (purchase creates a placeholder, claim-link binds
the real UUID, relink) · `licensing-and-entitlements` (tiers, metered server
count, feature toggles, desired vs granted) · `billing` (Paddle as merchant of
record, checkout, the customer portal, changing configuration) · `free-tier`
(limits, the renewal window, reaping on cloud).
### Reference
`environment-variables` (server, sitesvc, admin, agent) · `rest-api` (the route
tables, grouped as in `CLAUDE.md`) · `grpc-api` (the eight RPCs, the command
stream) · `agent-config` (config.yaml, paths, permissions) ·
`ports-and-networking` (which ports, which direction, what needs to be
reachable) · `troubleshooting`.
### Operations
`upgrading` (pull and recreate) · `backups` (MongoDB is the durable state; Redis
is sessions only) · `agent-updates` · `ci-cd` (which image rebuilds when, and
the repo-variable gap that pushes no commit).
## Writing rules
- Every guide is task-shaped: numbered steps, real paths and commands taken from
the repository, never invented UI.
- Behaviour that is a hard refusal gets an admonition, not a paragraph: default
steps are read-only (409 `ErrDefaultStep`), `POST /license` answers 409
`cloud_managed` on cloud, HQ-sourced users cannot have their role changed
locally (409 `ErrHQManaged`).
- Where the UI enforces something, say that the API is the boundary and the UI
is the courtesy — the same phrasing the codebase uses.
- No screenshots in this pass. They rot faster than prose and there is no
capture pipeline.
## Out of scope
- Versioned documentation. One version, tracking `main`. Docusaurus versioning
can be switched on later without restructuring.
- Internationalisation.
- Screenshots and diagrams beyond what Mermaid renders inline.
- A docs search backed by an external service.