chore: replace em dashes with hyphens, add no-em-dash rule to CLAUDE.md
Deploy / deploy (push) Successful in 2m47s
Deploy / deploy (push) Successful in 2m47s
This commit is contained in:
@@ -5,7 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
# DOCS_URL, DOCS_BASE_URL and APP_URL are repo variables, and editing one
|
||||
# pushes no commit — so nothing would rebuild on its own. This is the
|
||||
# pushes no commit - so nothing would rebuild on its own. This is the
|
||||
# escape hatch for that, and DOCS_BASE_URL in particular is baked into
|
||||
# every asset path in the build.
|
||||
workflow_dispatch:
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
docker login ${{ vars.DOCKER_HOST }} \
|
||||
-u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||
|
||||
# One image, one directory — the whole repository is the docs site,
|
||||
# One image, one directory - the whole repository is the docs site,
|
||||
# so there is nothing to detect a change in. It rebuilds on every
|
||||
# push, which is what a docs site wants anyway.
|
||||
#
|
||||
|
||||
@@ -6,18 +6,18 @@ tracking `main`, search indexed at build time by
|
||||
`@easyops-cn/docusaurus-search-local` so nothing external is keyed or called.
|
||||
|
||||
Extracted from the `vantage` monorepo with its history, where it was
|
||||
`docsite/`. The whole repository is the site — there is no subdirectory.
|
||||
`docsite/`. The whole repository is the site - there is no subdirectory.
|
||||
|
||||
It documents the **product**, not the codebase: each repository's own CLAUDE.md
|
||||
remains the contributor's map, and the two are allowed to differ in altitude but
|
||||
not in fact. Five sections — Getting started, Vantage, Vantage HQ, Reference,
|
||||
Operations — with `sidebars.ts` authored by hand so ordering is a decision
|
||||
not in fact. Five sections - Getting started, Vantage, Vantage HQ, Reference,
|
||||
Operations - with `sidebars.ts` authored by hand so ordering is a decision
|
||||
rather than a filename accident.
|
||||
|
||||
## A path, not a subdomain
|
||||
|
||||
`*.vantage.hostxtra.co.uk` is the per-tenant instance namespace, and the control
|
||||
plane's `APP_ROOT_LABEL` resolves an instance from the label before `vantage` —
|
||||
plane's `APP_ROOT_LABEL` resolves an instance from the label before `vantage` -
|
||||
so a `docs.` label would be read as a tenant slug. Hence `/docs` on the
|
||||
marketing host instead.
|
||||
|
||||
@@ -25,7 +25,7 @@ marketing host instead.
|
||||
asset 404s:**
|
||||
|
||||
1. `DOCS_BASE_URL` (default `/docs/` in the Dockerfile)
|
||||
2. the proxy location that routes here — which must also sort **above** the
|
||||
2. the proxy location that routes here - which must also sort **above** the
|
||||
catch-all forwarding to `site:3003`, or Next answers the 404 first
|
||||
3. the directory the runtime stage serves from,
|
||||
`/usr/share/nginx/html/docs`
|
||||
@@ -39,7 +39,7 @@ Unlike the three Next apps this builds to static files, so its runtime stage is
|
||||
## Design tokens
|
||||
|
||||
`src/css/custom.css` holds `vantage-site`'s `web/app/globals.css` token blocks
|
||||
**copied verbatim** — same names, same values. This is the one place the tokens
|
||||
**copied verbatim** - same names, same values. This is the one place the tokens
|
||||
are not consumed through Tailwind: everything below the token block maps
|
||||
Docusaurus's `--ifm-*` variables onto them. Docusaurus already stamps
|
||||
`data-theme` on `<html>`, which is the selector the dark block keys on, so the
|
||||
@@ -47,7 +47,7 @@ built-in toggle needed no wiring.
|
||||
|
||||
The rule holds all the same: **no rule in that file outside the token blocks may
|
||||
carry a hex.** The one concession is `static/img/favicon.svg`, which must, for
|
||||
the same reason the email layout must — a browser tab cannot read a token.
|
||||
the same reason the email layout must - a browser tab cannot read a token.
|
||||
|
||||
**Nothing enforces the match across the four front ends, and they are now in
|
||||
four repositories.** A token change here follows one in `vantage-site`; there is
|
||||
@@ -71,13 +71,13 @@ Not part of a self-hosted install.
|
||||
## CI/CD
|
||||
|
||||
`.gitea/workflows/deploy.yml`, on every push to `main` plus `workflow_dispatch`.
|
||||
One image, `vantage/vantage-docs:latest`, from the repository root — the whole
|
||||
One image, `vantage/vantage-docs:latest`, from the repository root - the whole
|
||||
repository is the site, so there is nothing to detect a change in and it
|
||||
rebuilds on every push.
|
||||
|
||||
**No build args are passed, deliberately.** `DOCS_URL`, `DOCS_BASE_URL`,
|
||||
`APP_URL` and `HQ_URL` all have correct defaults in the Dockerfile, and passing
|
||||
an unset repo variable would blank one rather than leave it alone — which for
|
||||
an unset repo variable would blank one rather than leave it alone - which for
|
||||
`DOCS_BASE_URL` means every asset path in the build. Change them in the
|
||||
Dockerfile. The monorepo's workflow passed none either; this preserves that.
|
||||
|
||||
@@ -88,3 +88,7 @@ Dockerfile. The monorepo's workflow passed none either; this preserves that.
|
||||
| `DOCKER_HOST` | Variable | registry host used for image tags |
|
||||
|
||||
Despite the name the workflow **does not deploy**; it only builds and pushes.
|
||||
|
||||
## Writing style
|
||||
|
||||
Never use em dashes (the long dash character) anywhere: code, comments, UI copy, docs, commit messages. Use a plain hyphen ` - `, a comma, a colon, or split the sentence instead.
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ RUN npm run build
|
||||
#
|
||||
# Docusaurus emits a fully static site, so unlike web/ and site/
|
||||
# there is no Node server at runtime. alpine-slim is roughly a quarter the size
|
||||
# of caddy:alpine, and nothing here needs automatic TLS — the host proxy
|
||||
# of caddy:alpine, and nothing here needs automatic TLS - the host proxy
|
||||
# terminates it.
|
||||
FROM nginx:alpine-slim AS runner
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# The user documentation, served at vantage.hostxtra.co.uk/docs.
|
||||
#
|
||||
# A fragment, composed on top of the vantage repository's own file — see the
|
||||
# A fragment, composed on top of the vantage repository's own file - see the
|
||||
# vantage-site fragment for the full command.
|
||||
#
|
||||
# A path on the marketing host, not a subdomain: *.vantage.hostxtra.co.uk is
|
||||
@@ -8,8 +8,8 @@
|
||||
# would read a `docs.` label as a tenant slug.
|
||||
#
|
||||
# The proxy location that routes here must sort ABOVE the catch-all forwarding
|
||||
# to site:3003, or Next answers the 404. It forwards the FULL path — it does
|
||||
# not strip /docs — so the proxy location, DOCS_BASE_URL and the directory the
|
||||
# to site:3003, or Next answers the 404. It forwards the FULL path - it does
|
||||
# not strip /docs - so the proxy location, DOCS_BASE_URL and the directory the
|
||||
# image serves from (/usr/share/nginx/html/docs) must all agree. When they do
|
||||
# not, the HTML loads and every asset 404s.
|
||||
#
|
||||
|
||||
@@ -58,7 +58,7 @@ as a Windows service and starts it.
|
||||
Windows agents register, heartbeat, report inventory, run workflow steps,
|
||||
serve the browser console, check and apply OS updates, and report workloads
|
||||
(services and containers). Managing `authorized_keys` is a Linux-only
|
||||
feature, and so is package inventory and CVE scanning — the vulnerability
|
||||
feature, and so is package inventory and CVE scanning - the vulnerability
|
||||
feeds this project uses carry no Windows data.
|
||||
:::
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ The `nginx` service routes **one hostname to two backends**:
|
||||
|
||||
Both rules are required. The web app forwards nothing to the API, so a proxy
|
||||
that sends the whole hostname to `web:3000` serves the interface and answers
|
||||
`404` to every request it makes — starting with the login form. If you replace
|
||||
`404` to every request it makes - starting with the login form. If you replace
|
||||
the bundled nginx with your own proxy, it must route the same way.
|
||||
|
||||
This is the shipped `nginx/vantage.conf`:
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ The instance is created and licensed as soon as payment confirms.
|
||||
|
||||
### Self-hosted
|
||||
|
||||
Install your control plane first — a licence binds to its instance ID.
|
||||
Install your control plane first - a licence binds to its instance ID.
|
||||
|
||||
1. Set **Deployment** to **Self-hosted**.
|
||||
2. Choose a **Plan** and configure its features and server allowance.
|
||||
|
||||
@@ -15,7 +15,7 @@ Install first, then link and claim. Step by step in
|
||||
## Paid
|
||||
|
||||
**Install first.** A licence is issued to one instance, so your control plane
|
||||
has to exist and report an instance ID before you can buy for it — the same
|
||||
has to exist and report an instance ID before you can buy for it - the same
|
||||
precondition Free has.
|
||||
|
||||
```mermaid
|
||||
@@ -36,7 +36,7 @@ flowchart LR
|
||||
|
||||
## Upgrading an instance you already have
|
||||
|
||||
Paste the same instance ID you already hold — an install on Free moves to the
|
||||
Paste the same instance ID you already hold - an install on Free moves to the
|
||||
paid plan in place, keeping its ID and its history. An ID belonging to another
|
||||
account is refused.
|
||||
|
||||
|
||||
@@ -7,16 +7,16 @@ sidebar_label: Backup and restore
|
||||
`vantagectl` is a separate command-line tool that backs up and restores the
|
||||
MongoDB database behind a Vantage control plane. It talks to MongoDB directly,
|
||||
never to the Vantage API, so it works against a control plane that is down,
|
||||
half-migrated, or gone — exactly the situation a backup tool has to survive.
|
||||
half-migrated, or gone - exactly the situation a backup tool has to survive.
|
||||
|
||||
For the store-level overview — what holds what, and why the database alone is
|
||||
not a backup — see [Backups](./backups.md). This page covers the tool.
|
||||
For the store-level overview - what holds what, and why the database alone is
|
||||
not a backup - see [Backups](./backups.md). This page covers the tool.
|
||||
|
||||
:::danger The key comes first
|
||||
Vantage encrypts SSH private keys, key passphrases, vault secrets, SSO client
|
||||
secrets and console credentials with `KEY_ENCRYPTION_KEY`. **It is not in your
|
||||
backup, and it is not recoverable.** A database restored without it is
|
||||
permanently unreadable — not degraded, not partially readable, unreadable.
|
||||
permanently unreadable - not degraded, not partially readable, unreadable.
|
||||
|
||||
Store it wherever you store the credentials you could not rebuild: a password
|
||||
manager, a secrets vault outside this control plane, a piece of paper in a
|
||||
@@ -26,7 +26,7 @@ safe. Anywhere but next to the archive.
|
||||
## What a backup holds
|
||||
|
||||
Every collection in the database, the index definitions each one needs to be
|
||||
useful again, and a SHA-256 **fingerprint** of `KEY_ENCRYPTION_KEY` — never the
|
||||
useful again, and a SHA-256 **fingerprint** of `KEY_ENCRYPTION_KEY` - never the
|
||||
key itself. The fingerprint is what lets a later `restore` or `verify` tell you
|
||||
that the key you are holding is the wrong one, before it writes a database
|
||||
nobody can read.
|
||||
@@ -37,8 +37,8 @@ nobody can read.
|
||||
true whenever Redis itself restarts.
|
||||
- **The vulnerability database.** It is re-pulled automatically on next boot.
|
||||
- **Agent state on managed servers.** Nothing needs re-enrolling: agents
|
||||
reconnect on their own, because `servers.agent_token_hash` — the thing an
|
||||
agent authenticates with — is itself in the backup.
|
||||
reconnect on their own, because `servers.agent_token_hash` - the thing an
|
||||
agent authenticates with - is itself in the backup.
|
||||
|
||||
:::note Pin the version
|
||||
The image is published on each `vantagectl/v*` release and tagged with that
|
||||
@@ -80,7 +80,7 @@ backup:
|
||||
```
|
||||
|
||||
`backup.enabled` defaults to `false`, and the chart refuses to render if it is
|
||||
turned on without both `backup.image` and `backup.pvcName` — a backup needs a
|
||||
turned on without both `backup.image` and `backup.pvcName` - a backup needs a
|
||||
known image and somewhere durable to land, and guessing at either is worse than
|
||||
refusing to start. `backup.exclude` names collections to leave out (recorded in
|
||||
the archive's manifest, so an archive never claims to be complete when it is
|
||||
@@ -88,13 +88,13 @@ not), and `backup.successfulJobsHistoryLimit` / `backup.failedJobsHistoryLimit`
|
||||
/ `backup.resources` behave exactly as they do on any other `CronJob`.
|
||||
|
||||
`backup` refuses to run without `KEY_ENCRYPTION_KEY` set in the environment,
|
||||
unless you pass `--allow-no-key` — for a deployment that genuinely stores no
|
||||
unless you pass `--allow-no-key` - for a deployment that genuinely stores no
|
||||
encrypted data. Everywhere else, treat the refusal as the tool doing its job.
|
||||
|
||||
## Where to put the archive
|
||||
|
||||
`--out -` streams the tarball to stdout instead of writing a file, and every
|
||||
line of progress output goes to stderr — so piping the archive into something
|
||||
line of progress output goes to stderr - so piping the archive into something
|
||||
else is always safe, nothing progress-related lands in the stream.
|
||||
|
||||
Into `restic`:
|
||||
@@ -109,7 +109,7 @@ Into S3:
|
||||
vantagectl backup --out - | aws s3 cp - s3://my-backups/vantage-$(date +%F).tar.gz
|
||||
```
|
||||
|
||||
An archive is as sensitive as a raw database dump — it carries every SSH key
|
||||
An archive is as sensitive as a raw database dump - it carries every SSH key
|
||||
assignment, every secret group, every session-adjacent setting, in a form the
|
||||
right `KEY_ENCRYPTION_KEY` can decrypt. Whatever you pipe it into should
|
||||
encrypt it at rest; `vantagectl` itself does not.
|
||||
@@ -123,23 +123,23 @@ vantagectl verify /backups/vantage-backup-vantage-20260907T020000Z.tar.gz \
|
||||
|
||||
Each line of output answers a different question:
|
||||
|
||||
- **`Archive`** — every member's checksum still matches; the tarball has not
|
||||
- **`Archive`** - every member's checksum still matches; the tarball has not
|
||||
been truncated or corrupted.
|
||||
- **`Archive key`** / **`Your key`** — the fingerprint stored in the archive
|
||||
- **`Archive key`** / **`Your key`** - the fingerprint stored in the archive
|
||||
next to the fingerprint of the `KEY_ENCRYPTION_KEY` in your environment.
|
||||
- **`Key match`** — whether those two fingerprints agree.
|
||||
- **`Live probe`** — given `--mongo-uri`, `verify` goes one step further and
|
||||
- **`Key match`** - whether those two fingerprints agree.
|
||||
- **`Live probe`** - given `--mongo-uri`, `verify` goes one step further and
|
||||
decrypts a real ciphertext value from that database with the key you hold.
|
||||
A fingerprint match proves two archives agree about a key; only the probe
|
||||
proves the key in your hand actually reads the data.
|
||||
|
||||
`verify` exits non-zero the moment anything above is wrong, which is what makes
|
||||
it worth putting on a schedule — a backup job that "succeeded" last night is
|
||||
it worth putting on a schedule - a backup job that "succeeded" last night is
|
||||
not the same claim as a backup that will actually restore.
|
||||
|
||||
## Looking inside an archive
|
||||
|
||||
`inspect` prints an archive's manifest and touches no database at all — no
|
||||
`inspect` prints an archive's manifest and touches no database at all - no
|
||||
`--mongo-uri`, no key. It is what to run against an archive of unknown origin,
|
||||
before deciding whether it is the one you want:
|
||||
|
||||
@@ -173,8 +173,8 @@ collection named in the archive before loading it. `--force` always needs a
|
||||
second assurance, in one of two forms:
|
||||
|
||||
- `--confirm-db NAME`, naming the target exactly. A mismatch is refused. This
|
||||
works everywhere — on a terminal and in a Kubernetes Job, a CI step or a cron
|
||||
entry alike — and is the form to script.
|
||||
works everywhere - on a terminal and in a Kubernetes Job, a CI step or a cron
|
||||
entry alike - and is the form to script.
|
||||
- Nothing, on a terminal: `--force` alone prompts you to type the target
|
||||
database's name back, a deliberate pause before something destructive.
|
||||
|
||||
@@ -190,7 +190,7 @@ log lines are still there, joined to freshly restored runs. Dropping them
|
||||
instead would delete data you never asked to delete.
|
||||
|
||||
`restore` also refuses when the archive's key fingerprint does not match the
|
||||
`KEY_ENCRYPTION_KEY` in your environment — see "When the key is wrong" below.
|
||||
`KEY_ENCRYPTION_KEY` in your environment - see "When the key is wrong" below.
|
||||
|
||||
## The restore drill
|
||||
|
||||
@@ -210,7 +210,7 @@ monthly:
|
||||
```
|
||||
3. Drop the scratch database. It served its purpose.
|
||||
|
||||
The failure this catches is not "the archive is corrupt" — `verify` alone
|
||||
The failure this catches is not "the archive is corrupt" - `verify` alone
|
||||
catches that. It is "the archive is fine but nobody can actually stand a
|
||||
control plane back up from it," which only a real restore proves.
|
||||
|
||||
@@ -221,13 +221,13 @@ If `restore` finds the archive's key fingerprint does not match the
|
||||
`--ignore-key-mismatch` proceeds anyway, but says plainly which collections
|
||||
will come back with ciphertext nobody can read:
|
||||
|
||||
- `keys` — SSH private keys and passphrases
|
||||
- `secrets` — the vault
|
||||
- `auth_providers` — OIDC/SSO client secrets
|
||||
- `console_sessions` — RDP/VNC credentials
|
||||
- `keys` - SSH private keys and passphrases
|
||||
- `secrets` - the vault
|
||||
- `auth_providers` - OIDC/SSO client secrets
|
||||
- `console_sessions` - RDP/VNC credentials
|
||||
|
||||
There is no way to recover that ciphertext afterwards. If you have reached
|
||||
this point, the right key was lost along with the chance to read those rows —
|
||||
this point, the right key was lost along with the chance to read those rows -
|
||||
the fix is to re-enter each of them by hand (re-upload SSH keys, re-save vault
|
||||
secrets, reconfigure SSO), not to keep searching for a way to decrypt what is
|
||||
already in the database.
|
||||
|
||||
@@ -27,7 +27,7 @@ Store the key somewhere other than the server it protects.
|
||||
:::info Use `vantagectl`
|
||||
[**Backup and restore**](./backup-and-restore.md) is the supported way to take
|
||||
and restore a backup. It writes an archive that carries a fingerprint of
|
||||
`KEY_ENCRYPTION_KEY` — never the key — so a restore taken with the wrong key
|
||||
`KEY_ENCRYPTION_KEY` - never the key - so a restore taken with the wrong key
|
||||
**refuses** rather than silently producing a database whose secrets nobody can
|
||||
read. It also checksums every archive member before writing anything, and
|
||||
refuses to restore into a database that already holds data. A plain
|
||||
@@ -52,7 +52,7 @@ docker compose exec -T mongo mongodump --archive --gzip --db vantage \
|
||||
:::warning
|
||||
This archive records nothing about which `KEY_ENCRYPTION_KEY` it was taken
|
||||
under. Restoring it with the wrong key produces a database that looks intact
|
||||
and is not — every secret in it is silently unreadable until something tries
|
||||
and is not - every secret in it is silently unreadable until something tries
|
||||
to decrypt one.
|
||||
:::
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ sidebar_label: API tokens
|
||||
---
|
||||
|
||||
A session cookie is fine for a browser. A script, a CI job or a cron task
|
||||
needs something it can hold onto instead — an API token.
|
||||
needs something it can hold onto instead - an API token.
|
||||
|
||||
## Creating one
|
||||
|
||||
@@ -15,7 +15,7 @@ admin additionally see every key in the instance.
|
||||
|
||||
**Create key** opens a dialog with four decisions, in the order they matter:
|
||||
|
||||
1. **Name and role.** The name says what will use the key — the CI pipeline,
|
||||
1. **Name and role.** The name says what will use the key - the CI pipeline,
|
||||
the script, the cluster. The role list offers your own role and everything
|
||||
below it, never above.
|
||||
2. **Scopes.** A grid of resources against read and write. Ticking write also
|
||||
@@ -26,7 +26,7 @@ admin additionally see every key in the instance.
|
||||
4. **Expiry.** Each option names the date it resolves to, so "90 days" and
|
||||
"7 December 2026" are the same choice read two ways.
|
||||
|
||||
Beneath them sits a preview line that reads the key back as a sentence —
|
||||
Beneath them sits a preview line that reads the key back as a sentence -
|
||||
*"gitea-ci-deploy acts as admin, may read and write servers and workflows,
|
||||
read secrets, and stops working on 7 December 2026."* Read it before you
|
||||
submit; an over-granted key is far easier to spot in a sentence than in a grid
|
||||
@@ -40,7 +40,7 @@ vt_8f2c1a9e4b6d0735a1c8e29f4b0d6e17...
|
||||
|
||||
That is the only time you will see it. Vantage stores a hash of the token,
|
||||
never the value itself, so if you lose it there is no support ticket that gets
|
||||
it back — create a new token and revoke the old one.
|
||||
it back - create a new token and revoke the old one.
|
||||
|
||||
The panel showing it also carries a ready-made `curl` line and a summary of
|
||||
what was granted, so the key can go straight into a secret store without a
|
||||
@@ -50,7 +50,7 @@ second trip to the list.
|
||||
|
||||
A token can reach only what its scopes name. Each resource has
|
||||
a `:read` and a `:write` scope, and holding `:write` on a resource also
|
||||
satisfies a `:read` requirement for it — you do not need to tick both.
|
||||
satisfies a `:read` requirement for it - you do not need to tick both.
|
||||
|
||||
| Resource | Covers |
|
||||
| ----------- | --------------------------------------------------- |
|
||||
@@ -64,7 +64,7 @@ satisfies a `:read` requirement for it — you do not need to tick both.
|
||||
| `settings` | Instance settings, members, single sign-on, licence, and token management itself |
|
||||
|
||||
A token created with only `servers:read` can list and inspect servers but
|
||||
cannot run a workflow against them, touch a key, or read a secret — each of
|
||||
cannot run a workflow against them, touch a key, or read a secret - each of
|
||||
those needs its own scope.
|
||||
|
||||
## Tag restrictions
|
||||
@@ -72,12 +72,12 @@ those needs its own scope.
|
||||
A key can be pinned to part of the fleet as well as part of the API. **Restrict
|
||||
to servers tagged** in the create dialog offers the tag keys and values already
|
||||
in use across your servers, and the key then reaches only servers carrying
|
||||
**every** pair listed — the restriction is an AND, not an OR. Leaving it empty
|
||||
**every** pair listed - the restriction is an AND, not an OR. Leaving it empty
|
||||
is the opposite: no restriction at all, the whole fleet.
|
||||
|
||||
The restriction is fixed at creation, like the role and the scopes. Changing
|
||||
what a credential already deployed in CI can reach, with no record of what it
|
||||
could reach before, is worse than requiring a rotation — so to widen or narrow
|
||||
could reach before, is worse than requiring a rotation - so to widen or narrow
|
||||
one, create a replacement and revoke the old key.
|
||||
|
||||
Restricted keys show their tags as chips beside their scopes in the list.
|
||||
@@ -86,7 +86,7 @@ Unrestricted keys show nothing there, which is the common case.
|
||||
The restriction applies to every API token, not only ones handed to an
|
||||
[MCP agent](../vantage/mcp.md). A CI token, a monitoring script's token and
|
||||
an agent's token are all held to the same tag scope check wherever the
|
||||
service layer resolves servers — the mechanism does not know or care what
|
||||
service layer resolves servers - the mechanism does not know or care what
|
||||
kind of caller is holding the token.
|
||||
|
||||
Nor can a token created under a restricted token reach further than its
|
||||
@@ -98,27 +98,27 @@ token that also sees production.
|
||||
|
||||
Each key is one record rather than a row of bare strings:
|
||||
|
||||
- **Key** — the name, the `vt_` prefix hint, the holder (when viewing all
|
||||
- **Key** - the name, the `vt_` prefix hint, the holder (when viewing all
|
||||
keys) and the role.
|
||||
- **Scopes** — one chip per resource, its access half tinted: `rw` in accent,
|
||||
- **Scopes** - one chip per resource, its access half tinted: `rw` in accent,
|
||||
`r` in grey. A key with nothing granted says so in words rather than showing
|
||||
a dash.
|
||||
- **Lifetime** — a bar showing how much of the key's issued life is left,
|
||||
- **Lifetime** - a bar showing how much of the key's issued life is left,
|
||||
with the date beside it. Four states, and the label always says which:
|
||||
|
||||
| Bar | Means |
|
||||
| --- | ----- |
|
||||
| Green | More than seven days left |
|
||||
| Amber | Seven days or fewer — rotate it |
|
||||
| Amber | Seven days or fewer - rotate it |
|
||||
| Red | Already expired; the key no longer authenticates |
|
||||
| Grey, full width | No expiry at all |
|
||||
|
||||
A key issued before the instance's maximum lifetime was tightened also
|
||||
carries *"outside the current policy — rotate when convenient"*. That is a
|
||||
carries *"outside the current policy - rotate when convenient"*. That is a
|
||||
prompt, not a failure: the cap is never applied retroactively and the key
|
||||
keeps working.
|
||||
|
||||
- **Last call** — when the key last authenticated, or *Never used*.
|
||||
- **Last call** - when the key last authenticated, or *Never used*.
|
||||
|
||||
Above the list, four counts summarise the same thing at fleet scale: keys
|
||||
listed, keys expiring within seven days, keys that never expire, and keys
|
||||
@@ -128,7 +128,7 @@ change with the **My keys** / **All keys** toggle.
|
||||
## A token never outranks its owner
|
||||
|
||||
A token's role can be at most the role of the person who created it, and its
|
||||
effective role is **recomputed on every request** as the lower of the two —
|
||||
effective role is **recomputed on every request** as the lower of the two -
|
||||
not fixed at creation. Demote the person from owner to member and every token
|
||||
they hold drops to member from that request onward. Remove the person and
|
||||
every token they hold stops working immediately: a token has no existence
|
||||
@@ -154,7 +154,7 @@ curl -H "Authorization: Bearer vt_…" https://acme.vantage.example.com/api/serv
|
||||
```
|
||||
|
||||
Everything else about the [REST API](./rest-api.md) applies the same way it
|
||||
does to a session — JSON errors, audit logging, licence gating on writes —
|
||||
does to a session - JSON errors, audit logging, licence gating on writes -
|
||||
except that authority comes from the token's role and scopes rather than a
|
||||
signed-in person's role.
|
||||
|
||||
@@ -178,12 +178,12 @@ already been deleted.
|
||||
|
||||
This page covers the token model. Every route, request and response shape is
|
||||
in the generated OpenAPI reference, served by **your own instance** at
|
||||
`/api/docs` — not this documentation site, since the routes and their shapes
|
||||
`/api/docs` - not this documentation site, since the routes and their shapes
|
||||
are specific to your install. The raw document is at `/api/openapi.json`.
|
||||
|
||||
:::danger Not the External Secrets token
|
||||
The bearer token read by `GET /api/secrets/:group/values` for the Kubernetes
|
||||
External Secrets Operator is a **separate credential** — a single instance-wide
|
||||
External Secrets Operator is a **separate credential** - a single instance-wide
|
||||
value, rotated from Settings, that reaches only that one endpoint. It is not an
|
||||
API token and an API token cannot be used in its place: the two are checked by
|
||||
different code, and neither substitutes for the other. See
|
||||
|
||||
@@ -25,7 +25,7 @@ it is absent.
|
||||
| `VANTAGE_LICENSE` | no | | A licence supplied at startup, so an automated install does not have to paste one in |
|
||||
| `VANTAGE_TRIVY_DB_REF` | no | `ghcr.io/aquasecurity/trivy-db:2` | Where the vulnerability database is pulled from. Point it at a mirror for an air-gapped install |
|
||||
| `VANTAGE_VULNDB_DISABLED` | no | | `true` switches [vulnerability scanning](../vantage/vulnerabilities.md) off entirely. Findings already stored are still served, and still shown as stale |
|
||||
| `TRUSTED_PROXIES` | no | `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` | Comma-separated CIDRs or addresses of proxies allowed to set `X-Forwarded-For`. The shipped Docker Compose and Helm chart default to the private RFC1918 ranges, which covers Nginx Proxy Manager on the Docker bridge network and Traefik on a Kubernetes pod CIDR. An operator whose proxy sits on a public address must set this themselves, or every visitor behind it shares one address for rate-limiting purposes. Unset entirely (outside those shipped defaults) trusts none, so the client address is the direct peer. **On a LAN-only install, narrow this to your proxy's address.** The RFC1918 default trusts every private range, so a client on 192.168.0.0/16 reaching the server directly is itself a "trusted proxy" and can put whatever it likes in `X-Forwarded-For` — and, on the public status route, in `X-Forwarded-Host`. Behind a proxy on a public address, or with no proxy at all, that is not reachable; on a flat LAN it is |
|
||||
| `TRUSTED_PROXIES` | no | `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` | Comma-separated CIDRs or addresses of proxies allowed to set `X-Forwarded-For`. The shipped Docker Compose and Helm chart default to the private RFC1918 ranges, which covers Nginx Proxy Manager on the Docker bridge network and Traefik on a Kubernetes pod CIDR. An operator whose proxy sits on a public address must set this themselves, or every visitor behind it shares one address for rate-limiting purposes. Unset entirely (outside those shipped defaults) trusts none, so the client address is the direct peer. **On a LAN-only install, narrow this to your proxy's address.** The RFC1918 default trusts every private range, so a client on 192.168.0.0/16 reaching the server directly is itself a "trusted proxy" and can put whatever it likes in `X-Forwarded-For` - and, on the public status route, in `X-Forwarded-Host`. Behind a proxy on a public address, or with no proxy at all, that is not reachable; on a flat LAN it is |
|
||||
|
||||
:::danger `KEY_ENCRYPTION_KEY` has no recovery path
|
||||
It encrypts SSH private keys, vault secrets, OIDC client secrets and console
|
||||
|
||||
@@ -9,7 +9,7 @@ sidebar_label: Ports and networking
|
||||
| Port | Service | Who connects | Expose publicly |
|
||||
| ------- | ----------- | -------------------------------- | --------------- |
|
||||
| `3000` | web | Browsers, via your reverse proxy | Yes, behind TLS |
|
||||
| `8080` | server API | Your reverse proxy | Not directly — proxied |
|
||||
| `8080` | server API | Your reverse proxy | Not directly - proxied |
|
||||
| `9090` | server gRPC | Agents | **Yes** |
|
||||
| `4822` | guacd | The server | No, firewall it |
|
||||
| `27017` | MongoDB | The server | No |
|
||||
@@ -85,7 +85,7 @@ On a private network you can skip TLS instead, by setting `tls: false` in each
|
||||
| everything else | `web:3000` |
|
||||
|
||||
The web app forwards nothing to the API. Sending the whole hostname to
|
||||
`web:3000` loads the interface and every request it makes answers `404` —
|
||||
`web:3000` loads the interface and every request it makes answers `404` -
|
||||
including the login form.
|
||||
|
||||
- Both backends must be the **same** hostname and certificate. The browser
|
||||
|
||||
@@ -22,7 +22,7 @@ or is not 64 hex characters.
|
||||
|
||||
**Every request 404s and the interface loads fine.** Your reverse proxy sends
|
||||
the whole hostname to `web:3000`. `/api`, `/auth`, `/public`, `/install*` and
|
||||
`/update*` belong to `server:8080` and the web app forwards nothing — see
|
||||
`/update*` belong to `server:8080` and the web app forwards nothing - see
|
||||
[Ports and networking](./ports-and-networking.md#reverse-proxy-notes). The
|
||||
tell is `curl -si https://<your-host>/auth/bootstrap-status` returning HTML
|
||||
with `x-powered-by: Next.js` instead of JSON.
|
||||
@@ -124,7 +124,7 @@ User-Agent: Vantage-Monitor/1.0 (+https://vantage.hostxtra.co.uk)
|
||||
|
||||
That string is the hook to allow the check through. In whichever product is
|
||||
doing the blocking, add a rule that skips bot protection, managed rules and rate
|
||||
limiting for requests carrying it — Cloudflare, AWS WAF, Azure Front Door,
|
||||
limiting for requests carrying it - Cloudflare, AWS WAF, Azure Front Door,
|
||||
Akamai, Fastly, Imperva, Sucuri, ModSecurity, nginx and HAProxy all match on a
|
||||
request header. The shape of the rule is the same everywhere:
|
||||
|
||||
@@ -137,7 +137,7 @@ Three details are worth getting right:
|
||||
exact match breaks silently on an upgrade, and the symptom is a monitor that
|
||||
goes down on deploy day.
|
||||
- **Keep the rule narrow.** Scope it to the specific host and path being
|
||||
monitored. A User-Agent is not a secret — anyone can send it — so a rule that
|
||||
monitored. A User-Agent is not a secret - anyone can send it - so a rule that
|
||||
skips protection site-wide on that string alone is a bypass you have
|
||||
published.
|
||||
- **Allow the source address too, where you can.** Combining the User-Agent with
|
||||
@@ -170,7 +170,7 @@ needs `host`, `port`, `from` and `to`, and Telegram needs both `token` and
|
||||
## A status page 404s or shows no data
|
||||
|
||||
**404, and it should be published.** Check the **Published** toggle on the
|
||||
page's editor — an unpublished page answers *not found* for everyone,
|
||||
page's editor - an unpublished page answers *not found* for everyone,
|
||||
including you, with no session exemption. Also check the host: the public URL
|
||||
is `<your-instance>.vantage.<yourdomain>/status/<page-id>`, the same
|
||||
per-instance subdomain everything else in Vantage uses. A wrong or missing
|
||||
@@ -178,11 +178,11 @@ subdomain resolves to no instance at all, which is also a 404.
|
||||
|
||||
Third possibility: `/public` is not routed to the server. Check with
|
||||
`curl -si https://<your-instance>.vantage.<yourdomain>/public/status/<page-id>`
|
||||
— JSON is correct, HTML carrying `x-powered-by: Next.js` means the proxy sent
|
||||
- JSON is correct, HTML carrying `x-powered-by: Next.js` means the proxy sent
|
||||
that prefix to the web app.
|
||||
|
||||
**Loads, but shows an explanation instead of components.** This is not a
|
||||
fault — it is the page working as designed. It means either the licence has
|
||||
fault - it is the page working as designed. It means either the licence has
|
||||
lapsed (a self-hosted instance past its grace period, or a cloud instance
|
||||
between billing events) or the current tier does not include the **Status
|
||||
pages** feature. Fix the licence or the plan and the same link starts serving
|
||||
|
||||
+22
-22
@@ -4,8 +4,8 @@ title: Agent access (MCP)
|
||||
sidebar_label: Agent access (MCP)
|
||||
---
|
||||
|
||||
The Model Context Protocol is how an LLM agent — Claude, or any other MCP
|
||||
client — talks to a tool server over a defined wire format instead of a
|
||||
The Model Context Protocol is how an LLM agent - Claude, or any other MCP
|
||||
client - talks to a tool server over a defined wire format instead of a
|
||||
one-off integration. Connecting Vantage to an agent this way lets it answer
|
||||
questions about your fleet and, if you choose to let it, act on it: run a
|
||||
workflow, apply updates, or draft a step for you to review.
|
||||
@@ -24,18 +24,18 @@ An agent authenticates the same way any script does: with an
|
||||
on the MCP endpoint specifically, on top of whatever resource scopes
|
||||
(`servers:read`, `workflows:write`, and so on) it also needs:
|
||||
|
||||
- **`mcp:read`** — permission to reach the MCP endpoint at all and use its
|
||||
- **`mcp:read`** - permission to reach the MCP endpoint at all and use its
|
||||
read-only tools.
|
||||
- **`mcp:write`** — permission to use its write tools: running a workflow,
|
||||
- **`mcp:write`** - permission to use its write tools: running a workflow,
|
||||
applying updates, assigning a key, or creating a step, workflow or monitor.
|
||||
|
||||
Without `mcp:write`, write tools are not merely refused when called — they
|
||||
Without `mcp:write`, write tools are not merely refused when called - they
|
||||
are left out of the tool listing entirely, so a read-only agent never learns
|
||||
they exist. A tool it cannot see is not one it can talk a user into asking
|
||||
for.
|
||||
|
||||
Each tool additionally needs the ordinary resource scope for what it touches
|
||||
— `run_workflow` needs `workflows:write`, `list_servers` needs
|
||||
- `run_workflow` needs `workflows:write`, `list_servers` needs
|
||||
`servers:read`, and so on, exactly as the [scopes table](../reference/api-tokens.md#scopes)
|
||||
describes for any other token. `mcp:read` or `mcp:write` decides whether the
|
||||
MCP surface is open to this token at all; the resource scope decides what it
|
||||
@@ -56,7 +56,7 @@ and cannot run a workflow that would touch one.
|
||||
It can list and inspect staging servers and run a workflow already
|
||||
configured to target them. Point it at a workflow whose saved targets
|
||||
include even one production host, though, and `run_workflow` refuses the
|
||||
call outright — see [what an agent cannot do](#what-an-agent-cannot-do)
|
||||
call outright - see [what an agent cannot do](#what-an-agent-cannot-do)
|
||||
below for why that refusal is all-or-nothing rather than "run on the
|
||||
servers it can see."
|
||||
|
||||
@@ -64,7 +64,7 @@ servers it can see."
|
||||
|
||||
Agent access lives at `/api/mcp` on your instance, over the streamable HTTP
|
||||
transport, and takes a bearer token the same way the REST API does. It has
|
||||
no separate authentication of its own — a browser tab with a signed-in
|
||||
no separate authentication of its own - a browser tab with a signed-in
|
||||
session does not count, only a real API token does.
|
||||
|
||||
Point an MCP client at it with a configuration block like this:
|
||||
@@ -83,15 +83,15 @@ Point an MCP client at it with a configuration block like this:
|
||||
|
||||
The endpoint is stateless and answers POST requests only: it holds no
|
||||
per-connection session, so each call is self-contained and there is nothing
|
||||
to resume. A GET request — a client probing for the endpoint, or a browser
|
||||
navigating to it directly — gets a `405`, by design, rather than a page. That
|
||||
to resume. A GET request - a client probing for the endpoint, or a browser
|
||||
navigating to it directly - gets a `405`, by design, rather than a page. That
|
||||
is the protocol-correct response from a server that does not offer the
|
||||
GET/SSE half of MCP, not a misconfiguration.
|
||||
|
||||
## The tool list
|
||||
|
||||
Every tool below calls the same service code the REST API and the Vantage
|
||||
UI call. Nothing in this list has its own path to the database — the MCP
|
||||
UI call. Nothing in this list has its own path to the database - the MCP
|
||||
surface is a presentation layer over existing authority, not a new one.
|
||||
|
||||
| Tool | What it does | Scope required |
|
||||
@@ -111,7 +111,7 @@ surface is a presentation layer over existing authority, not a new one.
|
||||
| `get_server_packages` | List installed packages on one server, optionally filtered by name. | `mcp:read`, `vulns:read` |
|
||||
| `search_fleet` | Search every server's installed packages by name across the fleet. | `mcp:read`, `vulns:read` |
|
||||
| `list_audit_events` | List recent audit log events. | `mcp:read`, `settings:read` |
|
||||
| `list_secret_names` | List secret group and key names — metadata only, never a value. | `mcp:read`, `secrets:read` |
|
||||
| `list_secret_names` | List secret group and key names - metadata only, never a value. | `mcp:read`, `secrets:read` |
|
||||
| `run_workflow` | Run a workflow against the servers it is already saved to target. | `mcp:write`, `workflows:write` |
|
||||
| `cancel_run` | Cancel an in-progress workflow run. | `mcp:write`, `workflows:write` |
|
||||
| `apply_updates` | Apply pending OS package updates on real servers. | `mcp:write`, `servers:write` |
|
||||
@@ -132,13 +132,13 @@ or the agent to filter.
|
||||
## Running a workflow: targets are fixed, not chosen
|
||||
|
||||
`run_workflow` does not take a target list. A workflow runs the servers and
|
||||
tags it was already saved with — an agent cannot redirect it at a different
|
||||
tags it was already saved with - an agent cannot redirect it at a different
|
||||
part of the fleet on the fly. Before starting the run, Vantage checks that
|
||||
those saved targets sit entirely inside the calling token's tag
|
||||
restriction. If even one of them falls outside it, the whole call is
|
||||
refused; there is no partial run against just the servers the token happens
|
||||
to be able to see. A token scoped to `env=staging` cannot use a
|
||||
production-and-staging workflow to touch only the staging half — it has to
|
||||
production-and-staging workflow to touch only the staging half - it has to
|
||||
be pointed at a workflow whose saved targets are staging-only from the
|
||||
start.
|
||||
|
||||
@@ -184,7 +184,7 @@ This is the section to read before turning the feature on.
|
||||
that runs a script, but it cannot open an interactive session on a
|
||||
machine.
|
||||
- **It cannot exceed its tag restriction.** Every tool that resolves
|
||||
servers — by ID, by tag, or through a saved workflow — resolves them
|
||||
servers - by ID, by tag, or through a saved workflow - resolves them
|
||||
through the token's own scope. A server outside that scope is invisible
|
||||
to `list_servers` and `get_server`, not-found to anything asking about it
|
||||
by ID, and excluded from what a write tool will act on.
|
||||
@@ -193,7 +193,7 @@ This is the section to read before turning the feature on.
|
||||
sees those tools in the listing.
|
||||
- **It cannot touch more than 25 servers in one call without saying so
|
||||
explicitly.** Any write tool that would affect more than 25 servers is
|
||||
refused with a message asking for `confirm:true` before it will proceed —
|
||||
refused with a message asking for `confirm:true` before it will proceed -
|
||||
cheap insurance against a mis-parsed tag selector reaching the whole
|
||||
fleet on one badly phrased instruction.
|
||||
- **It cannot edit or delete anything that already exists.** The creation
|
||||
@@ -201,10 +201,10 @@ This is the section to read before turning the feature on.
|
||||
workflow or monitor a person defined.
|
||||
|
||||
A monitor whose runner server sits outside a token's scope is not hidden
|
||||
from that token — it still appears in `list_monitors`. The REST API reaches
|
||||
from that token - it still appears in `list_monitors`. The REST API reaches
|
||||
that outcome by redacting: it replaces the runner with a
|
||||
`RunnerRestricted` placeholder when the runner falls outside the caller's
|
||||
visible servers. The MCP tools reach the same outcome a different way —
|
||||
visible servers. The MCP tools reach the same outcome a different way -
|
||||
`list_monitors` and `get_monitor_status` project a monitor onto a summary
|
||||
that never carries a runner field for any caller, restricted or not, so
|
||||
there is nothing there to redact in the first place. Two mechanisms, chosen
|
||||
@@ -219,16 +219,16 @@ many.
|
||||
Every tool call reaches the audit log, whether it read something or changed
|
||||
something:
|
||||
|
||||
- A successful call — read or write — is recorded as `mcp.tool_call`.
|
||||
- A call refused by policy — missing scope, an out-of-scope target, or a
|
||||
fan-out over 25 servers without confirmation — is recorded as
|
||||
- A successful call - read or write - is recorded as `mcp.tool_call`.
|
||||
- A call refused by policy - missing scope, an out-of-scope target, or a
|
||||
fan-out over 25 servers without confirmation - is recorded as
|
||||
`mcp.tool_denied`, naming the specific gate that refused it, so a human
|
||||
reading the log can tell "the agent was stopped" from "the agent tried
|
||||
and the underlying action failed."
|
||||
- A write tool that reached the underlying service and that service
|
||||
rejected is recorded as `mcp.tool_failed`.
|
||||
- Creating a step, workflow or monitor is recorded separately as
|
||||
`mcp.created`, naming what was made and its ID — a question like "what
|
||||
`mcp.created`, naming what was made and its ID - a question like "what
|
||||
has this agent added to my instance" should not require digging through
|
||||
hundreds of read rows to answer.
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Port `465` uses implicit TLS; anything else uses STARTTLS.
|
||||
### Credentials are never read back
|
||||
|
||||
The SMTP `password`, the Telegram `token` and the webhook, Slack and Discord
|
||||
`url`s come back from `GET /api/channels` as `••••••••` — a webhook URL is the
|
||||
`url`s come back from `GET /api/channels` as `••••••••` - a webhook URL is the
|
||||
authorisation to post to that channel, so it is treated as a credential like
|
||||
the rest. Writing that value back unchanged keeps the stored one, which is what
|
||||
lets you rename a channel without retyping its password. Anything else you send
|
||||
|
||||
@@ -89,7 +89,7 @@ metrics is normal rather than a fault.
|
||||
|
||||
### OS updates
|
||||
|
||||
Agents check for pending package updates hourly and report the count — the
|
||||
Agents check for pending package updates hourly and report the count - the
|
||||
machine's own package manager on Linux, the Windows Update COM API on Windows.
|
||||
From the server page you can:
|
||||
|
||||
@@ -113,7 +113,7 @@ Opens a browser SSH, RDP or VNC session. See [Browser console](./browser-console
|
||||
Windows agents register, heartbeat, run workflow steps, report inventory,
|
||||
check and apply OS updates, report workloads (services and containers), and
|
||||
serve the browser console. They do not manage `authorized_keys`, and they are
|
||||
not covered by package inventory or CVE scanning — the vulnerability feeds
|
||||
not covered by package inventory or CVE scanning - the vulnerability feeds
|
||||
this project uses carry no Windows data.
|
||||
|
||||
## Removing a server
|
||||
|
||||
@@ -6,12 +6,12 @@ sidebar_label: Status pages
|
||||
|
||||
A status page is a public page reporting a chosen set of monitors as up-front
|
||||
components, with a 90-day history and an uptime percentage per component. It
|
||||
needs no session and no token to read — anyone with the link can open it,
|
||||
needs no session and no token to read - anyone with the link can open it,
|
||||
which is the point: it is what you hand a customer instead of an incident
|
||||
email.
|
||||
|
||||
Requires the **Status pages** licence feature. If the licence lapses, or the
|
||||
tier does not include the feature, the page keeps serving — it renders an
|
||||
tier does not include the feature, the page keeps serving - it renders an
|
||||
explanation rather than data or a broken page, so a customer who follows an
|
||||
old link never sees an error.
|
||||
|
||||
@@ -28,7 +28,7 @@ https://<your-vantage-address>/status/<page-id>
|
||||
On **Vantage Cloud** that address is your instance's own subdomain, so the page
|
||||
is at `https://<your-instance>.vantage.hostxtra.co.uk/status/<page-id>`.
|
||||
|
||||
On a **self-hosted** install it is whatever address you reach Vantage on —
|
||||
On a **self-hosted** install it is whatever address you reach Vantage on -
|
||||
`https://vantage.acme.com/status/<page-id>`, or an IP and port on a LAN
|
||||
install. A self-hosted install serves exactly one Vantage instance, so no
|
||||
subdomain is needed to say which one you mean. The **Copy** control next to the
|
||||
@@ -37,26 +37,26 @@ the one to hand out.
|
||||
|
||||
**The page id cannot be changed after creation.** Once you have shared the
|
||||
link, changing the id would break it, so pick something you would still be
|
||||
happy with in a year — `platform`, `api`, a customer's own name for a
|
||||
happy with in a year - `platform`, `api`, a customer's own name for a
|
||||
dedicated page.
|
||||
|
||||
## Draft versus published
|
||||
|
||||
A new page starts unpublished. Unpublished pages answer *not found* to
|
||||
anyone who requests them, including you, from a browser without a session —
|
||||
anyone who requests them, including you, from a browser without a session -
|
||||
so you can build out the components and copy before announcing it. Toggle
|
||||
**Published** when it is ready. Un-publishing later takes it back to *not
|
||||
found* rather than deleting anything.
|
||||
|
||||
**Delete page**, in the editor header, is the only way to correct a page id you
|
||||
regret — the id is fixed once created. It takes the page, its sections and its
|
||||
regret - the id is fixed once created. It takes the page, its sections and its
|
||||
authored incidents with it; monitors and their history are untouched. If you
|
||||
only want the page off the internet, un-publish it instead.
|
||||
|
||||
## Sections and components
|
||||
|
||||
A page is organised into **sections** — arbitrary groupings such as "API" or
|
||||
"Region: EU" — each holding one or more **components**. A component is a
|
||||
A page is organised into **sections** - arbitrary groupings such as "API" or
|
||||
"Region: EU" - each holding one or more **components**. A component is a
|
||||
monitor plus a **display name** you choose for this page.
|
||||
|
||||
The display name is never the monitor's own name unless you type it in. An
|
||||
@@ -64,7 +64,7 @@ internal monitor name ("prod-db-primary-eu1") is rarely what you want a
|
||||
customer reading; give it whatever name makes sense to them, and change it
|
||||
for a different page without touching the monitor.
|
||||
|
||||
If a monitor listed on a page is later deleted, its component still appears —
|
||||
If a monitor listed on a page is later deleted, its component still appears -
|
||||
reading `Unknown` rather than up or down, because nothing is checking it any
|
||||
more and claiming otherwise would be a false claim of health.
|
||||
|
||||
@@ -77,7 +77,7 @@ more and claiming otherwise would be a false claim of health.
|
||||
- A 90-day history bar per component.
|
||||
- Any active incidents, upcoming maintenance, and a rolling history of both.
|
||||
- An optional banner across the top of the page, for anything you want said
|
||||
regardless of component state. It is one notice with one appearance — there
|
||||
regardless of component state. It is one notice with one appearance - there
|
||||
are no severity levels to choose between.
|
||||
|
||||
A visitor never sees a target URL, host or port, the check's expected status
|
||||
@@ -90,17 +90,17 @@ reachable is on this page.
|
||||
|
||||
Two kinds of entries appear on a page's timeline:
|
||||
|
||||
- **Automatic** — a monitor going down opens an incident on any page that
|
||||
- **Automatic** - a monitor going down opens an incident on any page that
|
||||
lists it, with no action from you. These appear the moment the monitor's
|
||||
state changes and close the moment it recovers.
|
||||
- **Authored** — an incident or maintenance window you create by hand, with
|
||||
- **Authored** - an incident or maintenance window you create by hand, with
|
||||
its own title, impact and a set of affected components you choose. You
|
||||
post updates to it (Investigating → Identified → Monitoring → Resolved) as
|
||||
the situation develops, and each update is timestamped and kept on the
|
||||
page's history.
|
||||
|
||||
An authored incident is attached to one or more pages explicitly when you
|
||||
create it — it does not follow a monitor onto every page that monitor happens
|
||||
create it - it does not follow a monitor onto every page that monitor happens
|
||||
to be listed on.
|
||||
|
||||
### Scheduling maintenance
|
||||
@@ -111,7 +111,7 @@ is in progress and its affected components are within the scheduled time,
|
||||
those components are drawn as "under maintenance" instead of up or down.
|
||||
|
||||
**Maintenance changes how a day is drawn, never the uptime number itself.**
|
||||
The 90-day percentage is computed from what actually happened — a component
|
||||
The 90-day percentage is computed from what actually happened - a component
|
||||
that stayed up throughout a maintenance window still shows as up in its
|
||||
history, it is only the live status pill that reads "under maintenance" for
|
||||
the duration.
|
||||
@@ -120,6 +120,6 @@ the duration.
|
||||
|
||||
A visitor's read of a page is cached for up to 30 seconds, so posting an
|
||||
update or flipping Published does not necessarily change what a visitor sees
|
||||
instantly — though most authoring actions invalidate that cache immediately,
|
||||
instantly - though most authoring actions invalidate that cache immediately,
|
||||
so in practice it usually shows within a second or two. If a change genuinely
|
||||
does not appear, reloading after 30 seconds always will.
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Workloads
|
||||
sidebar_label: Workloads
|
||||
---
|
||||
|
||||
A **workload** is one Docker container or one service — a systemd unit on
|
||||
A **workload** is one Docker container or one service - a systemd unit on
|
||||
Linux, a Windows service on Windows. Every server reports what it is running,
|
||||
and you can start, stop and restart those workloads, and read their recent
|
||||
logs, without opening a console.
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ server {
|
||||
return 302 /docs/;
|
||||
}
|
||||
|
||||
# Hashed assets are immutable — the filename changes when the content does.
|
||||
# Hashed assets are immutable - the filename changes when the content does.
|
||||
location /docs/assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/* ==========================================================================
|
||||
Vantage documentation design tokens
|
||||
|
||||
The token block below is COPIED VERBATIM from site/app/globals.css — same
|
||||
The token block below is COPIED VERBATIM from site/app/globals.css - same
|
||||
names, same values. adminsite/ holds a copy too, web/ holds the dark half,
|
||||
and shared/mail/templates/layout.html.tmpl holds it a fifth time as literal
|
||||
hex because email clients support neither var() nor prefers-color-scheme.
|
||||
@@ -13,7 +13,7 @@
|
||||
value outside the two token blocks.
|
||||
|
||||
Docusaurus always stamps data-theme on <html>, so unlike site/ there is no
|
||||
prefers-color-scheme branch to keep in step — the theme toggle is the only
|
||||
prefers-color-scheme branch to keep in step - the theme toggle is the only
|
||||
writer.
|
||||
========================================================================== */
|
||||
|
||||
@@ -200,7 +200,7 @@
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Machine output — install one-liners, key blobs, run logs — sits on a floor
|
||||
/* Machine output - install one-liners, key blobs, run logs - sits on a floor
|
||||
beneath the panel, the same distinction web/ draws with --well. */
|
||||
.theme-code-block {
|
||||
border: 1px solid var(--rule-soft);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
The Vantage mark, traced from site/components/Logo.tsx. A favicon is an asset
|
||||
rather than a component, and a browser tab has no access to the token block,
|
||||
so the logo navy is a literal here — the same concession the email layout
|
||||
so the logo navy is a literal here - the same concession the email layout
|
||||
makes. Keep it in step with --logo.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="246 207 533 610">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Reference in New Issue
Block a user