feat: documentation site

Docusaurus 3 docs-only site at docsite/, served statically by nginx under
/docs on the marketing host. Covers getting started (self-hosted install
through first server and first licence), the control plane, Vantage HQ,
a reference section and operations.

Wired into docker-compose.site.yml as docsite (3005:80) and into the
image build workflow, rebuilding on its own directory only. Never added
to the self-hosted compose file.
This commit is contained in:
2026-07-28 15:46:33 +01:00
parent d9945882e5
commit f46fb7fc0e
46 changed files with 23697 additions and 2 deletions
+78
View File
@@ -0,0 +1,78 @@
---
id: accounts-and-signup
title: Accounts and signup
sidebar_label: Accounts and signup
---
Vantage HQ, at `vantage-hq.hostxtra.co.uk`, is where you manage the **account**
behind your instances: your team, your instances, their licences and billing.
## An account is a team, not a person
One account holds many people and many instances. Everyone in it has an account
role:
| Role | Can |
| --- | --- |
| `owner` | Everything, including billing |
| `admin` | Invite people, create instances, grant instance access |
| `member` | Read what the account holds |
Reading is open to any signed-in member. Every mutation except changing your own
password requires `owner` or `admin`. Billing is owner-only.
The three words are the same as the control plane's roles, on purpose — but they
are separate things. Your account role governs the portal; your role *inside* an
instance governs that instance.
## Signing up
Signup is **account-first**. Creating an account creates the account and you;
it does not create a Vantage instance. Nothing exists in any control plane until
you later create or link one.
1. Go to the signup form.
2. Enter your name, email and a password.
3. Check your email and click the verification link.
:::info Verify before you can sign in
An unverified account gets a distinct "check your email" message rather than a
generic authentication failure — the address is already known to be yours, so
there is nothing to protect by being vague.
:::
Verification links are valid for **24 hours**. The token is 32 random bytes and
only its SHA-256 hash is stored, so a leaked database yields no working links.
If the verification email cannot be sent, the signup is rolled back rather than
left stranded — retry rather than assuming a half-created account is in the way.
## Signing in
Email and password. The session is a cookie, separate from the control plane's:
signing in to HQ does not sign you in to an instance, and vice versa.
## What comes next
| You want | Go to |
| --- | --- |
| A Vantage instance we run | [Cloud instances](./cloud-instances.md) |
| To license an install you run | [Self-hosted instances](./self-hosted-instances.md) |
| To add colleagues | [People and roles](./people-and-roles.md) |
| To understand tiers and limits | [Licensing and entitlements](./licensing-and-entitlements.md) |
## The portal layout
Three destinations: **Overview**, **People**, **Billing**.
Settings lives in the account menu rather than the nav, because it is your
password rather than a place. The appearance toggle is there too.
Overview lists your instances. Each is one record, closed to a row and open to
its licence contents, members and actions. It opens by default when it is your
only instance or when it needs attention, and your manual choice is remembered.
There is deliberately no "your plan" card in the sidebar: tier, limits and
expiry belong to a **licence**, and a licence belongs to one instance. An
account with a Free cloud instance and a Professional self-hosted one has no
single plan to show.
+83
View File
@@ -0,0 +1,83 @@
---
id: billing
title: Billing
sidebar_label: Billing
---
Paid plans are billed through **Paddle**, which is the merchant of record. Your
invoice, your card details and your tax handling are all Paddle's; HQ holds a
customer reference and nothing sensitive.
Billing is **owner-only**.
## Buying
### Cloud
Open the instance, change its configuration to what you want, and check out.
Checkout runs in the browser.
### Self-hosted
**Buy self-hosted**, then bind the purchase to your install's UUID. See
[Self-hosted instances](./self-hosted-instances.md).
## What you are buying
A subscription's line items are the configuration: the plan base, the metered
server count above the base, and any per-instance features. Changing the
configuration changes the line items.
## Changing configuration
**Instance → Configuration**, adjust servers or features, and save.
- **Increases** take effect when the payment confirms.
- **Reductions** are scheduled for the end of the term. The portal shows the
date and the new value.
## The customer portal
**Billing → Manage** mints a Paddle customer-portal session where you can
update your payment method, see invoices and cancel.
## How a licence follows a payment
```mermaid
flowchart LR
C["Checkout / change"] --> P["Paddle"]
P -->|signed webhook| H["HQ"]
H --> G["Entitlement: desired → granted"]
G --> L["Licence signed from granted"]
```
The webhook is the **only** issuing path for paid plans. It is signature
verified, processed exactly once, and resolved from the subscription's *current*
line items — so a webhook that arrives out of order still produces the right
answer rather than replaying a stale state.
A licence is signed from **granted** only. A checkout you abandon changes
nothing.
## Cancelling and failed payments
Cancelling, or a payment going past due, takes **no immediate licence action**.
Your licence runs to its grace-padded expiry and then lapses normally. There is
no mid-term cut-off.
For a cloud Free instance, lapsing eventually leads to deletion — see
[Free tier](./free-tier.md). Paid instances are not reaped.
## Renewals
At renewal the subscription bills again and the licence is reissued for the new
term. It is also the only moment a scheduled **reduction** takes effect.
Self-hosted customers: download and paste the reissued licence. Cloud customers:
nothing to do.
## Free is not in Paddle at all
Free has no subscription, no £0 line item and no Paddle record. It has its own
renewal, in the portal. An account only acquires a Paddle customer reference
with its first paid purchase.
+78
View File
@@ -0,0 +1,78 @@
---
id: cloud-instances
title: Cloud instances
sidebar_label: Cloud instances
---
A cloud instance is a Vantage control plane we run for you, reachable at
`<your-slug>.vantage.hostxtra.co.uk`.
## Creating one
1. **Overview → New instance**.
2. Choose a name and a slug.
3. Create.
The instance is provisioned with you as its owner, and a Free licence is issued
immediately. The owner user inside it gets your HQ password hash **copied**, not
shared — see [People and roles](./people-and-roles.md).
### Slugs
The slug becomes your hostname label, so it is lowercase, and some names are
reserved. Pick something you can say on a phone call.
:::warning One Free instance per account, per deployment
Creating a second Free cloud instance is refused. If you want another, it needs
a paid plan — or free up the first.
:::
## Using it
Sign in at your instance's hostname with the email and password you use for HQ.
It is a normal Vantage install from that point: see
[Getting started](../getting-started/first-login.md) and the
[Vantage](../vantage/servers.md) section.
The instance does not depend on HQ at runtime. HQ being unreachable does not
affect anyone signing in or any agent syncing.
## The instance record
Each instance on Overview is one record. Closed, it is a row. Open, it shows:
- **Licence contents** — tier, server allowance, features, expiry.
- **Members** — who has access and with what instance role.
- **Actions** — grant access, change configuration, renew.
## Members
Granting access writes a real user into the instance. Covered fully in
[People and roles](./people-and-roles.md).
## Changing what it can do
Server allowance and per-instance features (browser console, single sign-on) are
part of the instance's **entitlement**. Changing it goes through billing — see
[Licensing and entitlements](./licensing-and-entitlements.md) and
[Billing](./billing.md).
## Renaming
The display name is free to change. The slug is the hostname and is not
casually changed — ask support if you need it.
## What happens if the licence lapses
A cloud instance whose Free licence expires enters degraded mode, then, after a
grace period, the instance and all its data are deleted. Warning emails go out
first. See [Free tier](./free-tier.md).
Paid instances do not get reaped by that mechanism. A cancelled subscription
runs to its grace-padded expiry and then lapses.
## Deleting
Ask support. Deletion is performed by the control plane, not by HQ — the control
plane is the only service that knows which collections carry the instance ID,
and duplicating that list into HQ would be a list that drifts.
+79
View File
@@ -0,0 +1,79 @@
---
id: free-tier
title: Free tier
sidebar_label: Free tier
---
Free is a real tier in both deployments — not a trial that turns into nothing.
## What you get
| | Free |
| --- | --- |
| Servers | 3 |
| Monitors | 3 |
| Secret groups | 1 |
| Notification channels | 1 |
| Audit retention | 30 days |
| Support | Community |
Browser console and single sign-on are not included; they are per-instance
features on a paid plan.
## One per account, per deployment
The limit is enforced per account **and** deployment. A Free cloud instance does
not prevent a Free self-hosted one — they are separate slots.
## Free is outside Paddle
There is no subscription, no £0 line item and no invoice. Your account acquires
a Paddle customer reference only with its first paid purchase.
## Renewing
Free licences have a term and must be renewed from the portal.
- The renew button appears **7 days before expiry**.
- It stays available **after** expiry, right up until the instance is reaped —
so the same button rescues a lapsed instance rather than needing a second
mechanism.
- Renewing outside that window is refused, and the message names the date it
opens.
:::tip Put the expiry in a calendar
Warning emails go to the account address. If nobody watches that inbox, a Free
cloud instance can lapse and eventually be deleted without anyone noticing.
:::
## What happens when it lapses
**Self-hosted:** the instance goes into degraded mode after the grace period and
stays that way. Nothing is deleted, ever — the reaper is disabled by default on
a self-hosted install and must stay that way.
**Cloud:** the instance goes into degraded mode, and after a further period the
instance **and all its data are deleted**. Warning emails are sent first, naming
the date.
:::danger Deletion is permanent
There is no restore. If a cloud Free instance is approaching that date and you
want to keep it, renew it, or move it to a paid plan.
:::
Deletion is carried out by the control plane rather than by HQ. HQ sends the
warnings because it knows the billing address; the control plane performs the
delete because it is the only service that knows which collections carry the
instance ID.
## Moving off Free
Change the instance's configuration to a paid tier and check out. Your data
stays where it is — a tier change reissues a licence, it does not rebuild
anything.
## Relinks
Free instances get the same relink allowance as paid ones: three per term. That
cap exists to put a human in front of a fourth attempt, not to obstruct a
genuine rebuild.
@@ -0,0 +1,95 @@
---
id: licensing-and-entitlements
title: Licensing and entitlements
sidebar_label: Licensing and entitlements
---
A **licence** is a signed statement of what one instance may do. An
**entitlement** is the configuration a licence is cut from.
## Tiers
Three tiers, in both deployments. The allowances are identical across cloud and
self-hosted — what differs is the term on offer, not what you get.
| | Free | Professional | Enterprise |
| --- | --- | --- | --- |
| Servers (base) | 3 | 3 | 10 |
| Monitors | 3 | unlimited | unlimited |
| Secret groups | 1 | unlimited | unlimited |
| Notification channels | 1 | unlimited | unlimited |
| Audit retention | 30 days | 365 days | unlimited |
| Support | Community | Email, 24×5 | Email and phone, 24×7 |
The server count is **metered**: the base allowance comes with the tier, and you
buy additional servers on top. That is why Professional shows a real number
rather than "unlimited" — the number you actually have is the one in your
entitlement.
## Features
Two are per-instance toggles rather than tier bundles:
| Feature | What it enables |
| --- | --- |
| `console` | The [browser console](../vantage/browser-console.md) |
| `oidc` | Per-instance [single sign-on](../vantage/settings.md#single-sign-on-oidc) |
No tier includes them by default; you enable them on the instances that need
them.
## Entitlements: desired and granted
Each instance has one entitlement row holding two configurations:
| | Meaning |
| --- | --- |
| **Desired** | What you last asked for |
| **Granted** | What a payment has confirmed |
Checkout is built from **desired**. A licence is only ever signed from
**granted**. An abandoned checkout therefore leaves a desired that reached
nothing and changed nothing.
### Increases and reductions
An increase takes effect when payment confirms, and the entitlement is promoted
desired → granted.
A **reduction** is scheduled rather than immediate: you keep what you paid for
until the end of the term, and the portal shows the date it drops. The collapse
happens at renewal.
## What a licence carries
Instance UUID, deployment, tier, resolved limits, features, term and expiry —
all signed.
Two properties follow from that:
- **A licence is bound to one instance UUID.** Moving it takes a
[relink](./self-hosted-instances.md#relinking).
- **A licence is a snapshot.** Editing a plan later never rewrites an issued
licence, the same way editing a workflow step never rewrites a past run.
Verification is local. Your instance does not call HQ to check a licence, and
signing happens only in HQ.
## Expiry and grace
Expiry is padded with a grace period. Past that, the instance goes into degraded
mode: it keeps running and keeps your data, but stops letting you do everything.
The way out is a current licence — renew or purchase, then paste it (self-hosted)
or let it be written for you (cloud).
## Server limits in practice
When you exceed your server allowance, enrolling another one is refused. The
existing fleet is unaffected. Raise the allowance in the portal, or remove a
server you are not using.
## Legacy tiers
An older `self_hosted` tier is mapped forward to self-hosted Professional
wherever it appears. Nothing needs doing about it.
+98
View File
@@ -0,0 +1,98 @@
---
id: people-and-roles
title: People and roles
sidebar_label: People and roles
---
Two separate things live here: who is in your **account**, and who has access to
each **instance**.
## Account members
**People** lists everyone in the account.
| Role | Can |
| --- | --- |
| `owner` | Everything, including billing |
| `admin` | Invite, create instances, grant instance access |
| `member` | Read |
Owners and admins invite; billing is owner-only.
### Inviting someone
1. **People → Invite**.
2. Enter their email and pick a role.
3. They receive a link and set their own password at `/accept-invite`.
:::info Why you cannot set their password
An invitation creates a person with an **empty password hash**, which cannot
authenticate at all until they set one. If the inviter chose it, that password
would be a shared credential to every instance the person is later granted
access to.
The verification endpoint knows the difference: a token belonging to a
passwordless person reports that a password is needed and is left unspent, so
the link still works when they get to it.
:::
### Removing someone
Removing them from the account removes their portal access. See below for what
happens to their instance access.
## Instance access
Granting access to a **cloud** instance creates a real user inside that
instance's control plane, with `auth_source: "hq"`.
```mermaid
flowchart LR
P["HQ account member"] -->|grant| U["Control-plane user<br/>auth_source: hq"]
U --> I["The instance authenticates<br/>this user like any other"]
```
The instance authenticates that user exactly as it authenticates anyone else,
with **no runtime dependency on HQ**. Revoking deletes the row — the control
plane has no disabled state, and a row that exists is a row that can sign in.
### Granting
On an instance record, **Members → Add**, choose an account member and an
instance role (`owner`, `admin`, `member`).
One person holds at most one user per instance, so granting twice is refused
rather than quietly creating a second user.
### Roles inside an instance
Independent of the account role. Someone can be an account `member` and an
instance `owner`, or the reverse.
### Revoking
Removes the user from the instance immediately. Any live session ends with the
session, since the user row backing it is gone.
:::warning Self-hosted instances cannot be granted from HQ
All three member endpoints refuse when the instance is self-hosted. Manage those
users in the instance itself, at **Settings → Access**.
:::
## Passwords
Your HQ password is the single source of truth for every user projected from it.
Changing it in the portal rehashes it and copies the hash to every instance you
have been granted.
Propagation is best-effort and immediate; a background pass compares and repairs
every 15 minutes, so a temporarily unreachable instance catches up on its own.
There is no local password-change endpoint for those users in the control plane,
so there is never a second writer for the hash.
:::warning HQ-managed users are read-only in the instance
Changing the role of, or deleting, an `hq`-sourced user inside the control plane
is refused with `409`. Do it from the portal. The UI shows those rows read-only
with a link back here, but the API is the boundary; the UI is the courtesy.
:::
+74
View File
@@ -0,0 +1,74 @@
---
id: self-hosted-instances
title: Self-hosted instances
sidebar_label: Self-hosted instances
---
A self-hosted instance is your install, licensed through HQ. HQ never touches
it: it issues a signed file that your install verifies locally.
## Free
Install first, then link and claim. Step by step in
[Claim a Free licence](../getting-started/claim-free-licence.md).
## Paid
Buying happens **before** the install has to exist, because you may well be
buying in order to build it.
```mermaid
flowchart LR
A["Buy in HQ"] --> B["Placeholder instance<br/>awaiting_link, no licence"]
B --> C["Install Vantage<br/>get its instance UUID"]
C --> D["Paste the UUID<br/>claim-link"]
D --> E["Licence issued<br/>bound to that UUID"]
```
1. **Overview → Buy self-hosted**, choose tier, term and configuration.
2. Complete checkout. HQ creates a **placeholder** instance in state
`awaiting_link` with no licence attached.
3. [Install Vantage](../getting-started/self-hosted-install.md) if you have not
already, and find its instance UUID at **Settings → Licence**.
4. Back in HQ, open the placeholder and paste the UUID.
5. The licence is issued, bound to that UUID. Download it and paste it into your
install.
:::info Why there is a placeholder at all
A licence binds to an instance UUID, and at the moment of payment that UUID may
not exist yet. Issuing early would mean issuing to nothing; refusing to sell
until you had installed would be the wrong order. The placeholder holds the
purchase until there is something to bind to.
:::
## Linking an existing install
If the install already exists, **Link an instance** takes the UUID directly. A
UUID already claimed by another account is refused with a conflict.
## Relinking
Rebuilding the host produces a new instance UUID, and the old licence no longer
matches. **Relink** moves the licence to the new UUID and reissues.
The number of relinks per term is capped, and the portal shows how many you have
left. This is not meant to obstruct disaster recovery — if you have exhausted
them for a real reason, ask support.
## Installing the licence
Paste it at **Settings → Licence** in your install. The instance verifies the
signature and checks that the UUID matches its own.
Pasting works even while the current licence is expired — that endpoint is
exempt from the licence check, because it is the route out of degraded mode.
## Keeping it current
Your install does not fetch licences. When a licence is reissued — renewal,
configuration change, relink — download the new one from HQ and paste it in.
:::warning Nothing reminds your install
The control plane knows only what its licence says. Expiry emails come from HQ,
to the account's address. Make sure someone reads them.
:::