docs: Self review of doc pages

This commit is contained in:
2026-08-10 16:32:23 +01:00
parent bd7b3256fc
commit 83c38db92e
34 changed files with 860 additions and 991 deletions
+40 -31
View File
@@ -4,64 +4,73 @@ title: Claim a Free licence
sidebar_label: Claim a Free licence
---
A self-hosted install runs unlicensed until you give it a licence. Free is a
real tier in both deployments, and you can claim one for your install from the
HQ portal.
A self-hosted install stays read-only until you give it a licence. You claim a
Free one from Vantage HQ, and it takes a couple of minutes.
:::warning An unlicensed install is read-only
You can sign in and look around, but adding servers, keys, workflows and
everything else is refused until a licence is installed. Do this before
[adding your first server](./first-server.md).
:::
## What a licence is
A signed file. It carries the instance ID it belongs to, the tier, the server
allowance, feature toggles and an expiry. The control plane verifies the
signature locally.
A signed file that names your instance, its tier, how many servers you may
manage, which features are enabled and when it expires. Your install checks the
signature itself, so it never has to reach Vantage HQ to work.
A running instance does not need HQ to be reachable.
:::info One Free per account, per deployment.
The limit is enforced per account **and** deployment, so a Free cloud instance does not stop you claiming Free on a self-hosted install.
:::info One Free licence per account, per deployment
A Free cloud instance does not use up your Free self-hosted one. They are
separate.
:::
## 1. Find your instance ID
In the control plane, go to **Settings → Licence**. The instance ID is shown there.
Open the **Licence** page from the sidebar of your install. The instance ID is
shown at the top, and it is the value Vantage HQ asks for.
## 2. Create a free license
## 2. Create the instance in Vantage HQ
1. Sign in at [Vantage HQ](https://vantage-hq.hostxtra.co.uk). If you have no
account, see [Accounts and signup](../hq/accounts-and-signup.md).
2. Click on the **Buy A Plan** button.
3. Click on **Self Hosted** then click on the **Free** plan, then finally Paste the instance ID and give it a name you will recognise.
account yet, see [Accounts and signup](../hq/accounts-and-signup.md).
2. On **Overview**, choose **License my own install**. Once you already have an
instance, the same page offers **Buy a plan** instead.
3. Choose **Self-hosted**, then the **Free** plan.
4. Paste your instance ID, give the instance a name you will recognise, and
click **Create licence**.
You will then see the new instance on the **Overview** page.
The new instance now appears on the **Overview** page.
## 3. Downloading the free license
## 3. Download the licence
With the instance created go to the **Overview** page and expand the new instance.
Click on the **View Instance Settings** button. You can then click on the **Download License** or the **Copy to clipboard** button.
Expand the new instance on **Overview** and click **View Instance Settings**.
From there, use **Download licence** or **Copy to clipboard**.
## 4. Install the licence
Download the licence from HQ and paste it in the control plane at
**Settings → Licence**.
Back in your install, open the **Licence** page, paste the licence and save.
Your instance confirms the licence was issued to it, then shows your tier,
server allowance and expiry date.
The instance validates the signature, checks the ID matches its own, and
starts reporting the tier, allowance and expiry.
:::info Cloud instances do **not** require installing the license as this is done automatically.
:::info Cloud instances need none of this
A cloud instance is licensed automatically when it is created. These steps are
for self-hosted installs only.
:::
## Renewing
Free licences are renewable from HQ within a renewal window near expiry;
outside that window you cannot renew early. See [Free tier](../hq/free-tier.md).
Free licences run for a year. The renew button appears in Vantage HQ seven days
before expiry and stays available after it, so a lapsed instance can still be
rescued. See [Free tier](../hq/free-tier.md).
## Moving the install to new hardware
Rebuilding produces a new instance ID, and a licence binds to a ID. Use
**Relink** in HQ to move the licence across. The number of relinks per term is
capped; the portal shows how many you have left.
A rebuilt install gets a new instance ID, and a licence only works for the ID it
was issued to. Use **Relink** in Vantage HQ to move the licence across. You get
three relinks per term, and the portal shows how many are left.
## Next
- [Add your first server](./first-server.md)
- [Licensing and entitlements](../hq/licensing-and-entitlements.md)
- [Buying a paid self-hosted licence](../hq/self-hosted-instances.md)
+35 -37
View File
@@ -4,64 +4,62 @@ title: Cloud or self-hosted
sidebar_label: Cloud or self-hosted
---
Vantage runs in two deployments. They are the same software; what differs is
who operates it and how licensing, users and data lifecycle work.
Vantage runs in two ways. It is the same software; what differs is who runs it,
and how licensing and user accounts work.
## At a glance
| | Cloud | Self-hosted |
| --------------------- | --------------------------------------------------- | ------------------------------ |
| Who runs it | We do | You do |
| Where you sign in | `<your-slug>.vantage.hostxtra.co.uk` | Your own hostname |
| Database and backups | Ours | Yours |
| Licence | Written for you when you buy or create the instance | Pasted in, or claimed from HQ |
| Team members | Granted from HQ; the instance holds a projection | Created in the instance itself |
| Free tier | Yes, one per account | Yes, one per account |
| Expired Free instance | Eventually deleted, after warning | Never deleted |
| | Cloud | Self-hosted |
| --------------------- | ------------------------------------------- | ------------------------------ |
| Who runs it | We do | You do |
| Where you sign in | `<your-slug>.vantage.hostxtra.co.uk` | Your own hostname |
| Database and backups | Ours | Yours |
| Licence | Installed for you | You paste it in |
| Team members | Granted from Vantage HQ | Created in the instance itself |
| Free tier | Yes, one per account | Yes, one per account |
| Expired Free instance | Eventually deleted, after warning emails | Never deleted |
## Cloud
You create an instance from the HQ portal and it exists a few seconds later,
already licensed. People you grant access to get a real user inside that
instance see [People and roles](../hq/people-and-roles.md) but HQ owns their
password, role and existence.
You create an instance from the Vantage HQ portal and it is ready seconds later,
already licensed. People you grant access to get a real account inside that
instance, but Vantage HQ owns their password and role. See
[People and roles](../hq/people-and-roles.md).
:::info The instance does not phone home
A grant writes a user row into the control plane once. After that the instance
authenticates that person entirely on its own. HQ being down does not stop
anyone signing in to a running instance.
:::
Your instance keeps working whether or not Vantage HQ is reachable. Signing in
and managing servers never depend on it.
Cloud instances on the Free tier are reaped after their licence expires, with
warning emails first. See [Free tier](../hq/free-tier.md).
Cloud instances on the Free tier are deleted some time after their licence
expires, with warning emails first. See [Free tier](../hq/free-tier.md).
## Self-hosted
You run the Docker Compose stack on your own infrastructure. Nothing about the
control plane requires an internet connection to HQ at runtime a licence is a
signed file, verified locally.
You run Vantage with Docker Compose on your own infrastructure. It needs no
connection to us at runtime, because a licence is a signed file your install
checks for itself.
Two ways to get one:
1. **Free** link the install to an HQ account and claim it
([Claim a Free licence](./claim-free-licence.md)).
2. **Paid** buy from HQ, which creates a placeholder, then paste the install's
real instance UUID to bind and issue
([Self-hosted instances](../hq/self-hosted-instances.md)).
1. **Free.** Link the install to a Vantage HQ account and claim it. See
[Claim a Free licence](./claim-free-licence.md).
2. **Paid.** Buy from Vantage HQ, then paste your install's instance ID to have
the licence issued. See
[Self-hosted instances](../hq/self-hosted-instances.md).
Self-hosted users are local (or OIDC). There is no projection from HQ, and the
three member endpoints in HQ refuse to touch a self-hosted instance at all.
People who sign in to a self-hosted install are created in the install itself,
either with a password or through single sign-on. Vantage HQ cannot add them for
you.
## Which should you pick
Pick cloud if you want the thing running now and do not want to own a MongoDB.
Pick cloud if you want it running today and would rather not run a database.
Pick self-hosted if your policy requires the control plane inside your own
network, or the servers you manage cannot reach the public internet.
network, or the servers you manage cannot reach the internet.
Moving between them is a migration, not a switch instances are bound to a
deployment at creation, and a licence binds to an instance UUID.
Moving between the two means migrating your data, and a new licence, since a
licence is tied to one instance.
## Next
- [Self-hosted install](./self-hosted-install.md)
- [Accounts and signup](../hq/accounts-and-signup.md) if you are going cloud
- [Accounts and signup](../hq/accounts-and-signup.md), if you are going cloud
+56 -41
View File
@@ -6,67 +6,82 @@ sidebar_label: First login
A fresh install has no users and no instance. The first visit creates both.
## 1. Bootstrap
## 1. Create the first account
Open the control plane in a browser. Because no user exists, you land on
`/setup`.
Open your Vantage address in a browser. Because no user exists yet, you land on
the setup page.
Fill in:
| Field | Notes |
| ------------- | ------------------------------------- |
| Instance name | Display name. Shown throughout the UI |
| Email | Becomes your sign-in identity |
| Password | Stored bcrypt-hashed |
| Field | Notes |
| ---------------- | --------------------------------------------------------- |
| Instance name | Also used to derive your instance's own subdomain |
| Owner email | Becomes your sign-in identity |
| Password | At least 8 characters |
| Confirm password | Must match |
Submitting creates the instance and its **owner** you.
**Setup Instance** creates the instance and makes you its **owner**.
:::warning Bootstrap works exactly once
The endpoint is open only while the database has no users. As soon as the first
one exists, There is no second chance to create the first owner, so record the
credentials before you continue.
:::warning Your instance gets its own address
If you installed on a name like `vantage.example.com`, an instance called Acme
signs in at `acme.vantage.example.com`, and each instance keeps its own sign-in.
Make sure DNS and your reverse proxy cover that subdomain, or use a wildcard.
:::
## 2. Copy the Instance ID
:::warning Setup runs exactly once
It is only available while the database has no users. Once yours exists, the
page closes for good, so record the email and password before you continue.
:::
Once you have finished setup you will see the successfully created page.
## 2. Copy the instance ID
This will show the Instance ID. You will need this ID when creating a license in the HQ.
The confirmation page, headed **Instance created**, shows your instance ID and
your sign-in address. You need that ID to claim a licence in Vantage HQ, and it
is the reference support works from. You can find it again later on the
**Licence** page in the sidebar.
## 3. Sign in
Click the continue to sign in button on the successful setup page.
You will be taken to `/login`. Sign in with the email and password you just set.
Click **Go to sign in**, then sign in with the email and password you just set.
## 4. Look around
You land on the servers dashboard, which is empty. The sidebar is the whole
product:
You land on the servers page, which is empty. The sidebar is the whole product:
| Section | What it does |
| --------- | ------------------------------------------ |
| Servers | The server enrol, inspect, console, update |
| Keys | SSH public keys and their assignments |
| Workflows | Compose and run scripted work |
| Steps | The reusable step library |
| Monitors | HTTP, TCP, ICMP and TLS checks |
| Secrets | The encrypted vault |
| Audit | Every mutating action |
| Settings | Members, SSO, alerts, retention, licence |
| Section | What it does |
| --------------- | ------------------------------------------------ |
| Servers | Your fleet: enrol, inspect, console, update |
| Monitors | HTTP, TCP, ping and certificate checks |
| Vulnerabilities | Known security issues in installed packages |
| Workloads | Containers and services running on your servers |
| SSH Keys | Public keys and which servers they are on |
| Secrets | The encrypted vault |
| Workflows | Compose and run scripted work |
| Steps | The reusable step library |
| Audit Log | A record of everything that changed |
| Licence | Your tier, allowance and expiry |
| Settings | People, sign-in, alerts and integrations |
## 5. Add the rest of your team
**Licence** and **Settings** are shown only to owners and admins.
Go to **Settings → Access**. Add members with a role:
## 5. Install your licence
| Role | Can |
| -------- | -------------------------------------------------- |
| `owner` | Everything, including billing-adjacent settings |
| `admin` | Everything except owner-only settings |
| `member` | Day-to-day work servers, keys, workflows, monitors |
Until a licence is installed, the instance is read-only: you can look, but you
cannot add servers or anything else. Continue with
[Claim a Free licence](./claim-free-licence.md).
Settings and organisation management require `owner` or `admin`.
## 6. Add the rest of your team
If you would rather not manage passwords, configure single sign-on instead: see [Settings](../vantage/settings.md#single-sign-on).
Go to **Settings → Access** and add people with a role:
You can add more than one identity provider; each gets its own button on the login page, and no buttons appear at all until at least one provider is configured.
| Role | Can |
| -------- | ---------------------------------------------------- |
| `owner` | Everything |
| `admin` | Everything except owner-only settings |
| `member` | Day-to-day work: servers, keys, workflows, monitors |
Changing settings, and adding or removing people, needs `owner` or `admin`.
If you would rather not manage passwords, you can use single sign-on instead,
which is available on paid plans. See
[Settings](../vantage/settings.md#single-sign-on).
+50 -45
View File
@@ -4,64 +4,67 @@ title: Add your first server
sidebar_label: Add your first server
---
Enrolling a server means running one command on it. The control plane issues a
short-lived token, the install script fetches the agent and writes a config, and
the machine registers itself.
Enrolling a server means running one command on it. Vantage issues a short-lived
token, the install script fetches the agent and writes a config file, and the
machine registers itself.
:::info You need a licence first
An unlicensed install is read-only, so **Add server** will be refused until a
licence is in place. If you have not done that yet, start with
[Claim a Free licence](./claim-free-licence.md).
:::
## 1. Create the enrolment
In the UI, go to **Servers → Add server** Then click the **Generate Install Command** button.
This generates a server ID and a pre-registration token
Go to **Servers → Add server**, then click **Generate install command**. Vantage
creates a server record and an enrolment token for it.
:::warning The token is single-use and lives one hour
It is the only credential in the flow, and it is spent the moment the agent registers.
:::warning The token is single-use and lasts one hour
It is the only credential in the flow, and it is spent the moment the agent
registers. If it expires, generate a new command rather than reusing the old one.
:::
## 2. Run the one-liner
### Linux
Run the generated install script as root.
Here is an example of the install script:
Run the generated command as root. It looks like this:
```bash
curl -fsSL "https://vantage.example.com/install?server_id=<id>&token=<token>" | bash
```
What the script does:
The script:
1. Detects architecture `x86_64` and `aarch64` only; anything else exits.
2. Downloads the binary and `checksums.txt`, and **verifies the SHA-256**, aborting on a mismatch.
3. Installs to `/usr/local/bin/vantage-agent`, mode `0755`.
4. Writes the config file at `/etc/vantage/config.yaml`
1. This contains the server ID, the pre-registration token and the gRPC host.
5. Writes the systemd service file `/etc/systemd/system/vantage-agent.service` and starts the agent.
1. Checks the architecture. Only `x86_64` and `aarch64` are supported.
2. Downloads the agent and verifies its SHA-256 checksum, stopping on a mismatch.
3. Installs the agent to `/usr/local/bin/vantage-agent`.
4. Writes `/etc/vantage/config.yaml` with the server ID, the enrolment token and
the address the agent connects to.
5. Installs and starts the `vantage-agent` systemd service.
### Windows
Run this from an elevated PowerShell prompt:
```powershell
irm "https://vantage.example.com/install.ps1?server_id=<id>&token=<token>" | iex
```
Run from an elevated PowerShell.
It writes the config to `%ProgramData%\vantage\config.yaml`, installs the agent
as a Windows service and starts it.
What the script does:
1. Creates the config at `%ProgramData%\vantage\config.yaml`.
1. This contains the server ID, the pre-registration token and the gRPC host.
2. Downloads the agent MSI from Gitea.
3. Installs the MSI and creates the Windows service.
4. Starts the agent.
:::info Windows agents do **not** manage `authorized_keys` as this is a Linux-only function.
:::info Windows servers do not get SSH key management
Windows agents register, report inventory and run workflow steps. Managing
`authorized_keys` is a Linux-only feature.
:::
## 3. Watch it come up
The server appears immediately as `pending`. Within one poll interval, 30 seconds it becomes `active`.
The server appears as `pending` straight away, and becomes `active` within about
30 seconds.
Check the systemd logs using the following commands:
On Linux you can watch the agent itself:
```bash
systemctl status vantage-agent
@@ -70,28 +73,30 @@ journalctl -u vantage-agent -f
## 4. Confirm it works
Open the server's detail page. Within a minute or two you should see:
Open the server's page. Within a minute or two you should see:
- Status `active`, with a recent last-seen timestamp.
- Inventory CPU, memory, swap, partitions, kernel. Metrics refresh every 30
seconds; the full static snapshot every 15 minutes.
- Pending OS updates, checked hourly.
- Status `active`, with a recent last-seen time.
- Inventory: CPU, memory, swap, partitions and kernel. Metrics refresh every 30
seconds, and the fuller snapshot every 15 minutes.
- Any pending OS updates, which the agent checks for hourly.
## If it does not appear
| Symptom | Cause |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| Script exits at "Unsupported architecture" | Not amd64 or arm64 |
| "Checksum mismatch!" | Interrupted download, or a proxy rewriting the body. Re-run |
| "Could not determine latest agent version" | The host cannot reach `gitea.hostxtra.co.uk`, or no `agent/v*` release exists |
| Service runs, server stays `pending` | The machine cannot reach `GRPC_HOST`. Test it from that machine |
| Registers once then goes `offline` | Reachable for `Register` but not for the poll usually a firewall that permits the initial connection but drops the long-lived one |
| Symptom | What to check |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| Script stops at "Unsupported architecture" | The machine is not 64-bit x86 or ARM |
| "Checksum mismatch!" | An interrupted download, or a proxy rewriting the response. Run it again |
| "Could not determine latest agent version" | The machine cannot reach the release host, or `GITEA_HOST` is not set on your control plane |
| Service runs, server stays `pending` | The machine cannot reach the agent port. Test it from that machine, not from the control plane |
| Registers, then goes `offline` | A firewall or proxy allows the first connection but drops the long-lived one |
| "Server limit reached" | Your licence allowance is full. Raise it in Vantage HQ, or remove a server you no longer manage |
A server is marked `offline` when its last-seen time passes the threshold; that
sweep runs every two minutes, so allow for it before concluding anything.
A server is marked `offline` once it has not been seen for a while, and that
check runs every couple of minutes, so give it a moment before concluding
anything.
## Next Steps
## Next steps
- [Assign an SSH key](../vantage/ssh-keys.md)
- [Run a workflow](../vantage/workflows.md)
- [Claim a Free licence](./claim-free-licence.md)
- [Watch something with a monitor](../vantage/monitors.md)
+65 -54
View File
@@ -4,52 +4,46 @@ title: Install Vantage (self-hosted)
sidebar_label: Self-hosted install
---
This installs the control plane on a host you own. Budget about fifteen minutes.
This puts the control plane on a host you own. Budget about fifteen minutes.
## Before you start
You need:
- A Linux host with **Docker** and the **Compose plugin**.
- A DNS name pointing at it. You will use it for both the web UI and, with a
port, for agents.
- A reverse proxy terminating TLS in front of the web UI. gRPC on `:9090` is
reached directly by agents.
- Two ports reachable from every machine you intend to manage: the web port for
people, and **9090** for agents.
- Outbound access from the control plane, and from every managed machine, to
`gitea.hostxtra.co.uk`, which serves the agent releases.
- A DNS name pointing at that host. People use it for the web UI, and your
agents use it too.
- A reverse proxy in front of Vantage that terminates TLS. It needs to handle
both the web UI and the agent port, `9090`, which speaks HTTP/2.
- Those two ports reachable: the web port from wherever your people are, and
`9090` from every machine you intend to manage.
- Outbound access from the control plane, and from each managed machine, to
`gitea.hostxtra.co.uk`, which serves the agent downloads.
The stack itself brings MongoDB, Redis and guacd with it. You do not need to
provide a database.
The stack brings MongoDB, Redis and the console daemon with it, so there is no
database to provide.
## 1. Get the compose file
Put `deploy/docker-compose.yml` from the repository in a working directory, for
example `/opt/vantage`.
## 1. Get the Compose file
```bash
mkdir -p /opt/vantage/data && cd /opt/vantage
# copy docker-compose.yml here
mkdir -p /opt/vantage && cd /opt/vantage
curl -fsSLO https://gitea.hostxtra.co.uk/mrhid6/vantage/raw/branch/main/deploy/docker/docker-compose.yml
```
The `server` service bind-mounts `./data`, which is where workflow run logs are
written. Create it before first boot so it is not owned by root-in-container in
a way you did not intend.
## 2. Write the environment file
Create `/opt/vantage/.env`:
```bash
# The host:port agents dial. NOT the web URL this port speaks gRPC.
# The host:port your agents connect to. This is not the web URL;
# this port speaks gRPC.
GRPC_HOST=vantage.example.com:9090
# 32 bytes as 64 hex characters. Generate with the command below.
# 32 bytes as 64 hex characters. Generate it with the command below.
KEY_ENCRYPTION_KEY=
# Optional: where workflow run logs are written inside the container.
# The host serving agent downloads.
GITEA_HOST=gitea.hostxtra.co.uk
```
Generate the encryption key:
@@ -58,17 +52,27 @@ Generate the encryption key:
openssl rand -hex 32
```
:::danger Keep the encryption key
`KEY_ENCRYPTION_KEY` encrypts SSH private keys, vault secrets, OIDC client
secrets and console credentials with AES-256-GCM. Lose it and every one of those
becomes unreadable there is no recovery path. Back it up somewhere other than
the server it protects, and never rotate it without a planned re-encryption.
Then make sure the `server` service passes `GITEA_HOST` through, by adding this
line to its `environment:` block in `docker-compose.yml`:
```yaml
GITEA_HOST: ${GITEA_HOST}
```
Without it, the install command you hand to a new server cannot work out which
agent to download.
:::danger Keep the encryption key safe
`KEY_ENCRYPTION_KEY` encrypts SSH private keys, vault secrets, single sign-on
client secrets and console credentials. If you lose it, all of those become
unreadable and there is no way to recover them. Back it up somewhere other than
the server it protects, and do not change it once the install is in use.
:::
:::warning `GRPC_HOST` has no default
The server refuses to boot without it. There is deliberately no fallback to the
web host: that would hand every agent a port that does not speak gRPC, and the
failure would only surface later, on each agent, as a connection error.
The server will not start without it. There is deliberately no fallback to your
web address, because that port does not speak the protocol agents use, and the
mistake would only show up later as every agent failing to connect.
:::
## 3. Start the stack
@@ -78,31 +82,37 @@ docker compose up -d
docker compose ps
```
Five services come up: `mongo`, `redis`, `guacd`, `server` and `web`.
Five services start: `mongo`, `redis`, `guacd`, `server` and `web`.
Check the server got through boot:
Check the server got through startup:
```bash
docker compose logs -f server
```
Boot runs database migrations, builds indexes and seeds the default workflow
step library. Index builders for auth and settings are **fatal on failure**
they enforce tenant isolation, so the server would rather not start than start
without them.
On first boot it prepares the database and loads the built-in workflow step
library. If it stops during that, it will say why, and it is meant to stop
rather than run in a half-prepared state.
## 4. Put a proxy in front
Point your reverse proxy at `web` on port `3000` and terminate TLS there. The
web app calls the REST API through a Next rewrite, so you do not need to expose
`8080` publicly.
web app reaches the API internally, so there is no need to publish port `8080`.
Do **not** proxy `9090`. Agents connect to it directly over TLS.
Agents connect to port `9090`. Vantage does not terminate TLS itself, so put
that port behind your proxy too, with a certificate valid for the name in
`GRPC_HOST`. The proxy must speak HTTP/2 through to Vantage. Many do not do so
by default, and the symptom is agents that register once and then stop
responding.
For a private network where TLS is not required, you can instead set
`tls: false` in each [agent's config](../reference/agent-config.md) and let
agents reach the port directly.
## 5. First sign-in
Open your hostname in a browser. With no users in the database, you are sent to
`/setup`.
Open your hostname in a browser. With no users in the database yet, you are sent
to the setup page.
Continue with [First login](./first-login.md).
@@ -113,23 +123,24 @@ Continue with [First login](./first-login.md).
| `docker compose ps` | five services `running` |
| `curl -s localhost:8080/auth/bootstrap-status` | JSON saying bootstrap is needed |
| `nc -z your-host 9090` | open |
| `docker compose logs server \| grep -i fatal` | nothing |
| `docker compose logs server` | no fatal errors |
## Common install problems
**Server exits immediately.** Almost always a missing `GRPC_HOST`. The log line
**The server exits immediately.** Almost always a missing `GRPC_HOST`. The log
names it.
**Agents register but never go active.** They reached `:9090` for `Register` but
cannot sustain the poll, or `GRPC_HOST` names a host they resolve differently.
Check from the managed machine, not from the control plane host.
**Agents register but never go active.** They reached port `9090` once but
cannot hold the connection, or your proxy is not passing HTTP/2 through. Test
from the managed machine, not from the control plane host.
**Secrets pages error.** `KEY_ENCRYPTION_KEY` is empty or not 64 hex characters.
**Secrets pages show an error.** `KEY_ENCRYPTION_KEY` is empty or is not 64 hex
characters.
More in [Troubleshooting](../reference/troubleshooting.md).
## What this install does not include
## What is not included
The website, the HQ portal and this documentation site are hosted by us and are
not part of a self-hosted install. It deliberately runs none of them, and in
particular never holds the licence signing key.
The marketing site, the Vantage HQ portal and this documentation site are hosted
by us. A self-hosted install runs none of them, and it never holds the key that
signs licences.
+29 -36
View File
@@ -5,59 +5,52 @@ sidebar_label: What is Vantage
---
Vantage manages a fleet of servers from one place. It began as SSH key
management and grew outwards: key assignment, scripted workflow execution,
service monitoring, a secrets vault, a browser-based console and OS update
management.
management and grew outwards: key assignment, scripted workflows, service
monitoring, a secrets vault, a browser console and OS update management.
## The pieces
```mermaid
flowchart TD
W["Web UI<br/>servers · keys · workflows · monitors<br/>secrets · audit · console · settings"]
S["Server<br/>REST :8080 · gRPC :9090<br/>MongoDB · Redis · guacd"]
S["Vantage server<br/>the control plane"]
A["Agent<br/>one per managed server<br/>Linux and Windows"]
W -->|REST, cookie session| S
S -->|gRPC over TLS| A
A -.->|outbound only| S
W -->|you sign in here| S
S -->|sends work| A
A -.->|connects outbound| S
```
**The server** holds all state and does all decision-making. It exposes a REST
API on `:8080` for the web UI and a gRPC API on `:9090` for agents. MongoDB
stores everything durable; Redis stores sessions and nothing else.
**The server** is the control plane. It holds all your data and makes all the
decisions.
**The agent** is a single Go binary running as root on each managed server. It
polls the control plane every 30 seconds for desired key state, and holds a
bidirectional command stream so the server can push work run a workflow step,
generate a key, apply updates without waiting for the next poll.
**The agent** is a single small program running on each managed server. It asks
the control plane what it should be doing, and holds an open connection so
Vantage can send it work without waiting.
**The web UI** is the operator interface. Everything it does goes through the
REST API, which is the actual security boundary; the UI only ever makes things
convenient.
**The web UI** is what you use. Everything it can do goes through the same API
that enforces your permissions, so nothing is possible in the UI that would not
be permitted elsewhere.
## How agents connect
The agent dials **out** to the control plane. There is no inbound listener on a
managed server, no port to open and no NAT traversal to arrange. If the machine
can reach your Vantage host on the gRPC port, it can be managed.
The agent always connects **outbound**. There is no listener on a managed
server, no port to open and no NAT to work around. If the machine can reach your
Vantage address, it can be managed.
That direction is why `GRPC_HOST` exists as an explicit setting: the agent has
to be told a `host:port` it can reach, and there is no safe default the server
could guess on its behalf.
That is why you tell Vantage its own agent address (`GRPC_HOST`) when you install
it: the agent has to be given an address it can reach, and Vantage cannot guess
one for you.
## Two request patterns
## Keeping things current
| Pattern | Used for | Why |
| ----------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| Poll, every 30s | desired SSH key state | Key changes are not urgent, and polling survives a dropped connection with no reconnection logic |
| Push, over the command stream | workflow steps, key generation, updates, agent self-update | Clicking Run should not wait up to 30 seconds |
## Multi-tenancy
Every document in the database carries an instance ID, and every query is scoped
by it. One deployment can therefore host many independent tenants. On a
self-hosted install that mechanism is still there you simply have one tenant.
| What | How it works |
| ------------------------- | ------------------------------------------------------------------------- |
| SSH keys on a server | The agent checks every 30 seconds and only writes when something changed |
| Workflow steps, updates | Sent to the agent straight away, so clicking Run does not wait for a check |
| Inventory | Reported every 30 seconds, with a fuller snapshot every 15 minutes |
| Pending OS updates | Checked hourly |
## Next
- [Cloud or self-hosted](./cloud-vs-self-hosted.md) which one you want
- [Self-hosted install](./self-hosted-install.md) stand it up
- [Cloud or self-hosted](./cloud-vs-self-hosted.md), to pick which one you want
- [Self-hosted install](./self-hosted-install.md), to stand it up
+11 -6
View File
@@ -20,9 +20,9 @@ role:
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.
These are the same three words your instances use, but they are separate things.
Your account role controls what you can do in the portal. Your role inside an
instance controls what you can do there.
## Signing up
@@ -38,14 +38,19 @@ Verification links are valid for **24 hours**.
## Signing in
Use the Email and password used in the signup form to login to the HQ, signing in to HQ does not sign you in to an instance, and vice versa.
Sign in at [Vantage HQ](https://vantage-hq.hostxtra.co.uk) with the email and
password from the signup form.
HQ and your Vantage instances have separate sessions: signing in to HQ does not
sign you in to an instance, and signing in to an instance does not sign you in
to HQ.
## 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 licence 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) |
@@ -55,4 +60,4 @@ Three destinations: **Overview**, **People**, **Billing**.
- Overview lists your instances.
- People shows all the account members and their roles.
- Billing show the current subscriptions and subscription management.
- Billing shows your current subscriptions and lets you manage them.
+16 -12
View File
@@ -12,25 +12,29 @@ customer reference and nothing sensitive.
The Billing page requires the **owner-only** account role.
:::
## Buying A Plan
## Buying a plan
Buying a plan license can be found in Vantage HQ by clicking on the **Buy a Plan** button on the **Overview** page.
In Vantage HQ, click **Buy a plan** on the **Overview** page.
### Cloud
On the **Buy A Plan** page you will need to select the **Deployment** to **Cloud** then chose your **Billing** cycle (Monthly or Annually).
1. Set **Deployment** to **Cloud**.
2. Choose a **Billing** cycle, monthly or annual.
3. Choose a **Plan** and configure its features and server allowance.
4. Enter an **Instance name** and click **Continue to payment**.
Then select your desired **Plan** and configure the features.
Finally specify the **Instance Name** and click the **Continue to payment** button.
The instance is created and licensed as soon as payment confirms.
### Self-hosted
On the **Buy A Plan** page you will need to select the **Deployment** to **Self-Hosted** then chose your **Billing** cycle (Monthly or Annually).
1. Set **Deployment** to **Self-hosted**.
2. Choose a **Billing** cycle, monthly or annual.
3. Choose a **Plan** and configure its features and server allowance.
4. Enter an **Instance name** and click **Continue to payment**.
Then select your desired **Plan** and configure the features.
Finally specify the **Instance Name** and click the **Continue to payment** button.
Payment creates a placeholder instance with no licence yet. You then paste your
install's instance ID to have the licence issued. See
[Self-hosted instances](./self-hosted-instances.md).
## Cancelling and failed payments
@@ -38,7 +42,7 @@ 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
For a cloud Free instance, lapsing eventually leads to deletion. See
[Free tier](./free-tier.md). Paid instances are not deleted.
## Renewals
@@ -47,4 +51,4 @@ 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: the license is automatically linked to the instance.
- Cloud customers: nothing to do. The licence is written to the instance for you.
+16 -16
View File
@@ -9,18 +9,19 @@ A cloud instance is a Vantage control plane we run for you, reachable at
## Creating one
1. **Overview → New instance**.
2. Choose a name and a slug.
3. Create.
1. On **Overview**, choose **Create a cloud instance**. Once you already have an
instance, the same page offers **Buy a plan** instead.
2. Choose **Cloud**, then the plan you want.
3. Give the instance a name and confirm.
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).
immediately. Your Vantage HQ password gets you into it, though the two are kept
in step rather than 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.
Your instance's address comes from the name you choose, lowercased, with some
names reserved. Pick something short that 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
@@ -41,9 +42,9 @@ affect anyone signing in or any agent syncing.
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.
- **Licence contents**: tier, server allowance, features and expiry.
- **Members**: who has access, and with what role.
- **Actions**: grant access, change configuration, renew.
## Members
@@ -53,14 +54,15 @@ Granting access writes a real user into the instance. Covered fully in
## 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
part of the instance's **entitlement**, the configuration your licence is cut
from. 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.
The display name is free to change. The slug is part of your hostname, so ask
support if you need that changed.
## What happens if the licence lapses
@@ -73,6 +75,4 @@ 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.
Ask support.
+9 -15
View File
@@ -4,7 +4,8 @@ title: Free tier
sidebar_label: Free tier
---
Free is a real tier in both deployments not a trial that turns into nothing.
Free is a permanent tier. It is available whether we host Vantage for you or you
host it yourself.
## What you get
@@ -23,16 +24,15 @@ 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.
not prevent a Free self-hosted one; they are separate slots.
## Renewing
Free licences have a term and must be renewed from the portal.
Free licences run for a year and are 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.
- It stays available **after** expiry, right up until a lapsed cloud instance is
deleted, so the same button rescues one.
- Renewing outside that window is refused, and the message names the date it
opens.
@@ -55,19 +55,13 @@ 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
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.
Free instances get the same allowance as paid ones: three relinks per term. If
you genuinely need more, ask support.
+26 -16
View File
@@ -10,7 +10,7 @@ A **licence** is a signed statement of what one instance may do. An
## 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.
self-hosted; what differs is the term on offer, not what you get.
| | Free | Professional | Enterprise |
| --------------------- | --------- | ------------ | --------------------- |
@@ -23,17 +23,18 @@ self-hosted what differs is the term on offer, not what you get.
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
rather than "unlimited": the number you actually have is the one in your
entitlement.
## Features
Two are per-instance toggles rather than tier bundles:
Three features are enabled per instance rather than bundled into a tier:
| Feature | What it enables |
| --------- | -------------------------------------------------------------------- |
| `console` | The [browser console](../vantage/browser-console.md) |
| `oidc` | Per-instance [single sign-on](../vantage/settings.md#single-sign-on) |
| Browser console | The [browser console](../vantage/browser-console.md) |
| Single sign-on | [Sign-in through your identity provider](../vantage/settings.md#single-sign-on) |
| Vulnerability scanning| [Package vulnerability scanning](../vantage/vulnerabilities.md) |
No tier includes them by default; you enable them on the instances that need
them.
@@ -62,26 +63,35 @@ happens at renewal.
## What a licence carries
Instance UUID, deployment, tier, resolved limits, features, term and expiry —
all signed.
Your instance ID, whether it is cloud or self-hosted, the tier, your limits,
which features are enabled, and when it expires. All of it is 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.
- **A licence works for one instance only.** Moving it to a rebuilt install
takes a [relink](./self-hosted-instances.md#relinking).
- **A licence is a snapshot.** Changing a plan later does not rewrite a licence
already issued.
Verification is local. Your instance does not call HQ to check a licence, and
signing happens only in HQ.
## Expiry and grace
## Expiry, grace and degraded mode
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.
Expiry is padded with a few days' grace. Past that, an instance goes into
**degraded mode**, which means:
The way out is a current licence renew or purchase, then paste it (self-hosted)
or let it be written for you (cloud).
- It keeps running, and all of your data stays exactly where it is.
- You can still sign in and read everything.
- Adding or changing anything is refused.
- Deleting things still works, so you can get back under a reduced allowance.
- Applying OS updates still works, because security patching is never blocked.
A brand-new self-hosted install behaves the same way until you install its first
licence.
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
+24 -31
View File
@@ -26,14 +26,11 @@ Owners and admins invite; billing is owner-only.
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.
An invited person cannot sign in at all until they set their own password. If
you chose it for them, it would be a shared password to every instance they are
later given 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.
Their invitation link stays valid until they use it to set that password.
:::
### Removing someone
@@ -43,26 +40,26 @@ 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"`.
Granting access to a **cloud** instance creates a real account inside that
instance, marked as managed by Vantage 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"]
P["Person in your Vantage HQ account"] -->|you grant access| U["Account inside the instance"]
U --> I["They sign in at the instance,<br/>like anyone else"]
```
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.
They then sign in at the instance itself, and that keeps working whether or not
Vantage HQ is reachable. Revoking removes the account outright, so access ends
immediately.
### 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.
One person gets one account per instance, so granting twice is refused rather
than quietly creating a second.
### Roles inside an instance
@@ -71,28 +68,24 @@ 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.
Removes their access immediately, and ends any session they have open.
:::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**.
Vantage HQ cannot add or remove people in a self-hosted install. Manage them in
the install 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.
One Vantage HQ password covers you and every cloud instance you have been given
access to. Change it in the portal and it changes everywhere, within about 15
minutes at worst if an instance is briefly unreachable.
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.
Those people cannot change that password inside an instance, so there is only
ever one place it is set.
:::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.
Changing the role of, or removing, someone managed by Vantage HQ has to be done
from the portal. Inside the instance those rows are read-only, with a link back
here.
:::
+32 -29
View File
@@ -19,54 +19,57 @@ 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"]
A["Buy in Vantage HQ"] --> B["Placeholder instance<br/>no licence yet"]
B --> C["Install Vantage<br/>find its instance ID"]
C --> D["Paste your install's ID"]
D --> E["Licence issued<br/>for that instance"]
```
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.
1. On **Overview**, choose **License my own install**, or **Buy a plan** if you
already have an instance. Pick **Self-hosted**, then your tier, billing cycle
and configuration.
2. Complete checkout. Vantage HQ creates a **placeholder** instance, waiting to
be linked, with no licence yet.
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.
already, and find its instance ID on the **Licence** page.
4. Back in Vantage HQ, open the placeholder and paste that ID.
5. The licence is issued. 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.
:::info Why there is a placeholder
A licence is issued to one instance, and at the moment you pay, that install may
not exist yet. The placeholder holds your purchase until it does.
:::
## Linking an existing install
## Linking an install you already have
If the install already exists, **Link an instance** takes the UUID directly. A
UUID already claimed by another account is refused with a conflict.
If the install exists before the purchase, the flow is the same: buy, then open
the placeholder from **Overview**, choose **Link install** and paste its instance
ID. An ID already claimed by another account is refused.
For a Free licence there is no placeholder step. See
[Claim a Free licence](../getting-started/claim-free-licence.md).
## 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.
Rebuilding the host gives you a new instance ID, which your old licence does not
match. **Relink** moves the licence across and reissues it.
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.
left. If you have used them all for a genuine 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.
Paste it on the **Licence** page in your install. It confirms the licence was
issued to that instance before applying it.
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.
Pasting works even while your current licence has expired, because that is how
you get 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.
Your install never downloads a licence by itself. Whenever one is reissued, on
renewal, on a configuration change or after a relink, download it from Vantage
HQ and paste it in.
:::warning Nothing reminds your install
The control plane knows only what its licence says. Expiry emails come from HQ,
+6 -4
View File
@@ -7,9 +7,9 @@ slug: /
# Vantage documentation
Vantage is a self-hosted, multi-tenant infrastructure control plane. It manages
SSH keys, runs scripted workflows, watches services, stores secrets, opens
browser consoles and applies OS updates across a fleet of servers.
Vantage manages a fleet of servers from one place: SSH keys, scripted workflows,
service monitoring, a secrets vault, browser consoles and OS updates. Run it
yourself, or let us run it for you.
A central server drives a lightweight agent installed on each managed machine.
The agent connects **outbound only**, so managed servers need no inbound
@@ -20,14 +20,16 @@ firewall holes.
| If you want to | Read |
| --------------------------------------- | --------------------------------------------------------------- |
| Understand what the pieces are | [What is Vantage](./getting-started/what-is-vantage.md) |
| Decide who should run it | [Cloud or self-hosted](./getting-started/cloud-vs-self-hosted.md) |
| Run it on your own hardware | [Self-hosted install](./getting-started/self-hosted-install.md) |
| Licence a self-hosted install | [Claim a Free licence](./getting-started/claim-free-licence.md) |
| Enrol your first machine | [Add your first server](./getting-started/first-server.md) |
| Manage your account, licence or billing | [Vantage HQ](./hq/accounts-and-signup.md) |
| Look something up | [Reference](./reference/environment-variables.md) |
## The two products
**Vantage** is the control plane the thing you sign in to in order to manage
**Vantage** is the control plane, the thing you sign in to in order to manage
servers. It runs either on your own infrastructure or as a cloud instance we
run for you.
+5 -7
View File
@@ -13,8 +13,7 @@ Each server's detail page shows the version it reported at its last sync.
## Updating from the UI
**Servers → _a server_ → Update agent** pushes `UpdateAgentCmd` with a target
version. The agent then:
Open a server and choose **Update agent**. The agent then:
1. Downloads the binary for its platform from the release.
2. Verifies the SHA-256 against `checksums.txt`.
@@ -35,8 +34,8 @@ irm https://vantage.example.com/update.ps1 | iex
```
It does the same download, checksum and replace, then restarts the service. Use
this when the control plane cannot push for example, when the machine is
reachable but its command stream is not.
this when Vantage cannot reach the agent to push the update, but you can still
reach the machine.
## Rolling out across a fleet
@@ -54,9 +53,8 @@ Do one, confirm it returns to `active`, then do the rest.
## Version compatibility
The agent API is versioned to tolerate an agent older than the control plane. The
reverse an agent newer than the control plane is not a case anyone tests.
Upgrade the control plane first.
An agent older than your control plane is supported. An agent newer than it is
not, so upgrade the control plane first.
Agents report their version on every poll, so a fleet running mixed
versions is visible in the server list rather than something you have to go
+5 -6
View File
@@ -11,9 +11,8 @@ either one restores to something unusable.
| Store | Contents | Back up |
| -------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| MongoDB | Everything durable servers, keys, assignments, workflows, runs, monitors, incidents, secrets, settings, audit | **Yes** |
| MongoDB | Everything durable: servers, keys, assignments, workflows, runs and their logs, monitors, incidents, secrets, settings, audit | **Yes** |
| Redis | Sessions only | No. Losing it signs everyone out and nothing else |
| `./data` bind mount | Workflow run logs | Optional |
| `KEY_ENCRYPTION_KEY` | Not stored anywhere by the app | **Yes, separately** |
:::danger The database alone is not a backup
@@ -50,7 +49,7 @@ nothing writes during the restore.
cp /opt/vantage/.env /secure-location/vantage.env
```
Treat it as a credential in its own right it holds the encryption key.
Treat it as a credential in its own right, since it holds the encryption key.
## What a restore gives you
@@ -62,7 +61,7 @@ What it does **not** do is reconcile the world. After a restore:
- Agents reconnect with their existing tokens, since the token hashes are in the
database.
- If the restore is older than an enrolment, that server's token hash is missing
and the agent will fail to authenticate re-enrol it.
and the agent will fail to authenticate. Re-enrol it.
- The next agent poll rewrites `authorized_keys` to match the restored desired
state, which may remove keys added since the backup.
@@ -74,8 +73,8 @@ What it does **not** do is reconcile the world. After a restore:
| Environment file | On change, held in a password manager or secret store |
| Restore rehearsal | Occasionally, into a throwaway host |
The rehearsal is the part that gets skipped and the part that finds the
problems.
Rehearse a restore now and again. It is the step most often skipped, and the one
that finds the problems.
## Cloud instances
+18 -9
View File
@@ -5,7 +5,7 @@ sidebar_label: Upgrading
---
Upgrading the control plane is a pull and a recreate. Agents are versioned and
upgraded separately see [Agent updates](./agent-updates.md).
upgraded separately. See [Agent updates](./agent-updates.md).
:::info Cloud instances upgrade themselves
This page is for self-hosted installs. If your instance is hosted by us, there
@@ -26,11 +26,11 @@ renamed or removed.
## What happens on boot
1. **Migrations** run, recording markers so each runs once.
2. **Indexes** are ensured. Auth and settings index builders are fatal on
failure; secret and workflow ones only warn.
3. **Default steps** are reseeded from the image, overwriting the `default`
library which is why those steps are read-only.
1. The database is brought up to date. Each change runs once.
2. The built-in workflow steps are reinstalled, which is why those steps cannot
be edited.
If Vantage cannot complete either safely, it stops rather than run half-prepared.
Watch it:
@@ -46,6 +46,16 @@ docker compose logs -f server
- **Check your `.env`** still supplies everything required. A newly required
variable stops the boot rather than defaulting to something unsafe.
## Single sign-on after an upgrade
Each identity provider now has its own callback URL. If you configured single
sign-on on an older version it was carried over, but its callback URL changed,
and sign-in through it fails until you copy the new one from its card in
**Settings** and register it with your identity provider. The card shows a
reminder until you dismiss it.
Password sign-in is unaffected, so you can always sign in locally to fix this.
## Downgrading
There is no automatic downgrade. Migrations do not roll back, so returning to an
@@ -57,7 +67,7 @@ is the reason the backup is not optional.
The stack is not designed for it. `docker compose up -d` recreates the server
container, which is a short interruption:
- Agents reconnect on their own they retry, and the poll loop is idempotent.
- Agents reconnect on their own.
- Workflow runs in progress lose their command stream. Steps already dispatched
finish on the agent, but their results have nowhere to go. **Do not upgrade
during a run.**
@@ -67,5 +77,4 @@ container, which is a short interruption:
- Confirm every service is `running`.
- Confirm servers return to `active` within a couple of poll intervals.
- Open a page that touches encryption a secret group to confirm
`KEY_ENCRYPTION_KEY` came through.
- Open a secret group, to confirm `KEY_ENCRYPTION_KEY` came through.
+7 -7
View File
@@ -20,15 +20,15 @@ Directory `0700`, file `0600`. The install script sets both.
```yaml
server_url: "vantage.yourdomain.com:9090"
server_id: "<uuid>"
pre_reg_token: "<token>" # removed after the first successful Register()
agent_token: "" # written by the agent after Register()
pre_reg_token: "<token>" # cleared once the agent has registered
agent_token: "" # written by the agent when it registers
poll_interval: 30s
tls: true
```
| Field | Meaning |
| --------------- | --------------------------------------------------------------------- |
| `server_url` | `host:port` of the gRPC endpoint. Comes from the server's `GRPC_HOST` |
| `server_url` | The `host:port` the agent connects to. Comes from your `GRPC_HOST` |
| `server_id` | The identity issued when the enrolment was created |
| `pre_reg_token` | Single-use, one hour. Cleared once registration succeeds |
| `agent_token` | The permanent credential, written by the agent itself |
@@ -36,8 +36,8 @@ tls: true
| `tls` | Whether to use TLS. Leave `true` |
:::danger This file is the credential
`agent_token` is plaintext here and nowhere else the control plane holds only
its SHA-256. Anyone who can read this file can act as this agent.
`agent_token` exists in full only in this file. Anyone who can read it can act
as this agent.
:::
## Service management
@@ -77,5 +77,5 @@ rm -rf /etc/vantage
systemctl daemon-reload
```
Keys already written to `authorized_keys` remain on disk the agent is no
longer running to remove them. Revoke first if that matters.
Keys already written to `authorized_keys` remain on disk, because the agent is
no longer running to remove them. Revoke first if that matters.
+8 -6
View File
@@ -13,16 +13,18 @@ it is absent.
| -------------------------- | --------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `GRPC_HOST` | **yes** | | The `host:port` agents dial. Boot fails without it. There is deliberately no fallback to the web host: that would hand every agent a port that does not speak gRPC |
| `MONGO_URI` | no | `mongodb://localhost:27017` | The database name is taken from the URI path, falling back to `vantage`. There is no separate `MONGO_DB` |
| `REDIS_ADDR` | no | `localhost:6379` | Sessions, and the bus that routes agent commands between server replicas. Every replica must point at the **same** Redis |
| `REDIS_ADDR` | no | `localhost:6379` | Where sessions are held. If you run more than one copy of Vantage, they must all point at the same Redis |
| `REDIS_USERNAME` | no | | Redis 6+ ACL user. Leave empty against a legacy `requirepass` instance, which authenticates with the password alone |
| `REDIS_PASSWORD` | no | | Leave empty for an unauthenticated Redis. Both of these exist so an install can use a managed Redis rather than the bundled one |
| `KEY_ENCRYPTION_KEY` | yes in practice | | 64 hex characters (32 bytes) for AES-256-GCM. Required for private keys, vault secrets, OIDC client secrets and console credentials |
| `GITEA_HOST` | yes | `gitea.example.com` | Used to build the install scripts and agent download URLs. The default is a placeholder that will not resolve |
| `GITEA_HOST` | yes in practice | `gitea.example.com` | Host serving agent releases; used to build the install scripts and download URLs. The default is a placeholder that will not resolve, so set it to `gitea.hostxtra.co.uk` |
| `GUACD_ADDR` | no | `guacd:4822` | The [browser console](../vantage/browser-console.md) daemon |
| `PROXY_ADVERTISE_HOST` | no | `server` | The hostname **guacd** uses to reach the control plane's console relay. Wrong here and every console session fails at connect with guacd unable to resolve the relay |
| `PROXY_LISTEN_HOST` | no | `0.0.0.0` | Interface the ephemeral relay listeners bind. Narrow it only if guacd shares a known interface |
| `POD_IP` | no | | Kubernetes only, set by the Helm chart from the downward API. Overrides `PROXY_ADVERTISE_HOST`, because a console relay belongs to one replica and a Service address names all of them |
| `APP_ROOT_LABEL` | no | `vantage` | The app root label for the host and session organisation guard |
| `APP_ROOT_LABEL` | no | `vantage` | The label Vantage expects in its own hostname, used to match a browser session to the right instance |
| `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 |
:::danger `KEY_ENCRYPTION_KEY` has no recovery path
It encrypts SSH private keys, vault secrets, OIDC client secrets and console
@@ -38,8 +40,8 @@ protecting anything.
### Not configurable
The HTTP port (`8080`) and the gRPC port (`9090`) are fixed in the server. The
`HTTP_PORT` and `GRPC_PORT` entries in the shipped Compose file are inert —
remap with Docker's port publishing instead.
`HTTP_PORT` and `GRPC_PORT` entries in the shipped Compose file have no effect.
Remap the ports with Docker instead.
## Agent
+18 -16
View File
@@ -9,9 +9,9 @@ sidebar_label: Ports and networking
| Port | Service | Who connects | Expose publicly |
| ------- | ----------- | -------------------------------- | --------------- |
| `3000` | web | Browsers, via your reverse proxy | Yes, behind TLS |
| `8080` | server REST | The web app | No |
| `8080` | server API | The web app | No, firewall it |
| `9090` | server gRPC | Agents | **Yes** |
| `4822` | guacd | The server | No |
| `4822` | guacd | The server | No, firewall it |
| `27017` | MongoDB | The server | No |
| `6379` | Redis | The server | No |
@@ -35,11 +35,10 @@ NAT is not an obstacle. The only requirement is that the machine can reach
`GRPC_HOST`.
**The console rides the agent's connection too.** guacd never dials the target
directly; the server pushes a command down the agent's existing outbound gRPC
stream on `9090`, and the agent relays the protocol traffic from its own
loopback. No route from the control plane to the target's address is needed,
and no new inbound port opens on the target — the same connection that carries
key sync carries console traffic. This is what makes the console work for a
directly. Vantage sends the request down the connection the agent already holds
on port `9090`, and the agent connects to the service locally on that machine. No route from the control plane to the target's address is needed,
and no new inbound port opens on the target. The same connection that keeps keys
in sync carries console traffic, which is what makes the console work for a
machine behind NAT on a private subnet, as long as its agent is online.
## What to open
@@ -55,8 +54,8 @@ machine behind NAT on a private subnet, as long as its agent is online.
- Anything a server-run [monitor](../vantage/monitors.md) checks.
- SMTP, if you use an SMTP notification channel.
No route to the machines you intend to console is needed — that traffic rides
the agent's existing outbound `9090` connection instead.
No route to the machines you intend to console is needed. That traffic uses the
connection the agent already holds.
### Outbound from a managed machine
@@ -68,14 +67,17 @@ the agent's existing outbound `9090` connection instead.
Terminate TLS for the web UI at your reverse proxy.
gRPC on `9090` is reached directly by agents with `tls: true`, so that port needs
a valid certificate for the name in `GRPC_HOST`. If you proxy it, the proxy must
speak HTTP/2 end to end many do not by default, and the symptom is agents that
register and then fail to hold the command stream.
Vantage does not terminate TLS itself, so port `9090` needs the same treatment:
put it behind your proxy with a certificate valid for the name in `GRPC_HOST`.
The proxy has to pass HTTP/2 through to Vantage. Many do not do that by default,
and the symptom is agents that register once and then stop responding.
On a private network you can skip TLS instead, by setting `tls: false` in each
[agent's config](./agent-config.md).
## Reverse proxy notes
- Point the proxy at `web:3000`. The web app reaches the REST API internally, so
- Point the proxy at `web:3000`. The web app reaches the API internally, so
`8080` does not need publishing.
- The console uses a **WebSocket** at `/api/console/tunnel`. A proxy that does
not forward upgrade headers breaks the console and nothing else.
@@ -85,8 +87,8 @@ register and then fail to hold the command stream.
## Air-gapped and restricted networks
The control plane needs outbound access to fetch agent releases. Managed
machines need it too, unless you distribute the agent binary yourself and write
the config by hand the install script's only job is to do those two things.
machines need it too, unless you distribute the agent yourself and write its
config by hand, which is all the install script does.
Licence verification is entirely local, so a licensed install works with no
outbound access to HQ at all.
+42 -137
View File
@@ -1,154 +1,59 @@
---
id: rest-api
title: REST API
sidebar_label: REST API
title: Automating Vantage
sidebar_label: Automating Vantage
---
The control plane's HTTP API, on port `8080`. The web UI is a client of it and
has no privileges it does not.
Everything the web UI does, it does through Vantage's own API, so anything you
can do on screen you can also do from a script.
## Authentication
The routes mirror the product: `/api/servers`, `/api/keys`, `/api/workflows`,
`/api/monitors`, `/api/secrets`, `/api/audit`, and so on.
Most endpoints take a session: an opaque 32-byte token in the `km_session`
cookie, with the body in Redis for 24 hours.
## Where it is
One endpoint takes a bearer token instead the External Secrets Operator read
path.
On a self-hosted install the API is served on port `8080`, behind the same
reverse proxy as the web UI, under `/api` and `/auth`. On a cloud instance it is
your instance hostname.
## Unauthenticated
## Signing in
```
GET /install /install.ps1 # dynamic agent install scripts
GET /update /update.ps1
GET /auth/bootstrap-status
POST /auth/bootstrap /auth/login /auth/logout
GET /auth/me
GET /auth/providers # {local_enabled, providers:[{id,name,preset}]} — no issuer, client ID or secret
GET /auth/oidc/:providerId/start · /auth/oidc/:providerId/callback
GET /api/secrets/:group/values # bearer token (ESO)
Most calls use a session, exactly as the browser does:
```bash
curl -c cookies.txt -X POST https://vantage.example.com/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"..."}'
curl -b cookies.txt https://vantage.example.com/api/servers
```
`/install` and `/install.ps1` take `server_id` and `token` as query parameters
and return a shell script with the newest agent version substituted in.
Sessions last 24 hours. Your role applies exactly as it does in the UI: a
`member` calling an owner-only route is refused.
`POST /auth/bootstrap` works only while the database has no users.
## The one exception
## Session-authenticated, under `/api`
Kubernetes reads secret groups with a token instead of a session, so that it
does not need an account. See
[Secrets](../vantage/secrets.md#kubernetes-external-secrets-operator).
### Servers
## Things worth knowing
```
GET,POST /servers
GET,POST /servers/new
GET,DELETE /servers/:id
POST /servers/:id/generate-key
POST /servers/:id/update-agent
POST /servers/:id/apply-updates
```
- **Reads always work. Changes need a valid licence.** Without one, the instance
is read-only and any call that changes something is refused. Deleting things,
applying OS updates and installing a licence are always allowed, so you can
always get back under your allowance or out of read-only mode.
- **Some features are licensed.** The browser console, single sign-on and
vulnerability scanning are refused if your licence does not include them.
- **Some things cannot be changed here.** A cloud instance refuses a pasted
licence, and people managed by Vantage HQ cannot be re-roled or deleted inside
the instance.
- **Errors are JSON**, with an `error` field naming the reason.
- **Everything that changes something is audited**, whether it came from the UI
or from a script. See [Audit log](../vantage/audit-log.md).
### Keys
## Vantage HQ
```
GET,POST /keys
GET,DELETE /keys/:id
GET /keys/:id/private-key
POST /keys/:id/assign
DELETE /keys/:id/assign/:serverId
```
### Workflows and steps
```
GET,POST /steps
PUT,DELETE /steps/:id
GET /steps/:id/export
POST /steps/import · /steps/seed-defaults · /steps/parse
GET /steps/usage
GET,POST /workflows
GET,PUT,DELETE /workflows/:id
POST /workflows/:id/run
GET /workflows/:id/runs
GET /runs/:runId
POST /runs/:runId/cancel
GET /runs/:runId/servers/:serverId/logs
GET /runs/:runId/servers/:serverId/logs/stream
```
`PUT` and `DELETE` on a step whose source is `default` answer `409`. See
[Workflows](../vantage/workflows.md#default-steps).
### Monitors and channels
```
GET,POST /monitors
GET,PUT,DELETE /monitors/:id
GET /monitors/:id/incidents · /monitors/:id/uptime
GET,POST /channels
PUT,DELETE /channels/:id
POST /channels/:id/test
```
### Secrets
```
GET,POST /secrets
GET,PUT,DELETE /secrets/:group
POST /secrets/:group/reveal
DELETE /secrets/:group/:key
```
### Console
```
POST /console/connect
GET /console/tunnel # websocket
```
### Other
```
GET /audit
GET /agent/latest-version
GET,PUT /settings (owner|admin)
POST /settings/secrets-token (owner|admin)
GET /license
POST /license (self-hosted only)
GET,POST /org/users
PUT /org/users/:id/role
DELETE /org/users/:id
GET,POST /auth/providers (owner|admin)
PUT,DELETE /auth/providers/:id (owner|admin)
POST /auth/providers/:id/test · /auth/providers/:id/ack-notice (owner|admin)
GET /auth/presets (owner|admin)
```
## Notable refusals
| Endpoint | Condition | Status |
| -------------------------------------------------- | ------------------------------ | ------------------- |
| `POST /license` | deployment is `cloud` | `409 cloud_managed` |
| `PUT,DELETE /steps/:id` | the step's source is `default` | `409` |
| `PUT /org/users/:id/role`, `DELETE /org/users/:id` | the user's auth source is `hq` | `409` |
`POST /license` is exempt from the licence check, so pasting a valid licence
works while the current one is expired that is the way out of degraded mode.
## Multi-tenancy
Every request is scoped to the instance resolved from the session. On a
multi-tenant deployment, a request arriving at `<slug>.vantage.<tld>` also has
its host checked against the session's instance, and a mismatch is rejected.
## Errors
Errors are JSON with an `error` field. Customer-facing endpoints in the HQ API
answer `404` rather than `403` for another account's resource, because a `403`
confirms the resource exists; the control plane's own API is single-tenant per
session and does not need that distinction.
## Admin API
Vantage HQ is a separate hosted service with its own API and its own session.
Its behaviour is described in the [Vantage HQ](../hq/accounts-and-signup.md)
section rather than here; the two services share no session and no
authentication.
The portal is a separate service with its own sign-in, described in the
[Vantage HQ](../hq/accounts-and-signup.md) section. A Vantage session does not
work there, and an HQ session does not work in your instance.
+24 -25
View File
@@ -8,13 +8,12 @@ Symptoms, in the order people hit them.
## The server will not start
**Exits immediately on boot.** Almost always a missing `GRPC_HOST` the server
**Exits immediately on boot.** Almost always a missing `GRPC_HOST`. The server
refuses to start rather than guess a value that would break every agent later.
**Fails during index creation.** The auth and settings index builders are fatal
on failure by design: those unique indexes are what enforce tenant isolation,
so starting without them is worse than not starting. Check the MongoDB user's
permissions and whether a conflicting index already exists.
**Fails while preparing the database.** Vantage stops rather than run without
the safeguards it sets up at startup. Check the MongoDB user's permissions and
whether an old, conflicting index is already there.
**Starts, but every secret operation errors.** `KEY_ENCRYPTION_KEY` is missing
or is not 64 hex characters.
@@ -22,15 +21,15 @@ or is not 64 hex characters.
## Nobody can sign in
**`/setup` appears when users already exist.** The server is pointed at a
different database than you think. Check the database name in `MONGO_URI`
it comes from the URI path, not a separate variable.
different database than you think. Check the database name in `MONGO_URI`,
which is taken from the end of the URI.
**Sessions do not stick.** Redis is unreachable, or the cookie is being dropped
because the site is served over plain HTTP.
**"Wrong organisation" style rejections.** The host and session guard is
comparing the request host's label against the session's organisation. Check
`APP_ROOT_LABEL`.
**"Wrong organisation" style rejections.** Vantage compares the address you
browsed to against the instance your session belongs to. On a custom domain,
check `APP_ROOT_LABEL`.
**OIDC redirects and then fails.** The callback URL registered with the provider
must match exactly. Keep one local owner account so a broken provider is not a
@@ -44,12 +43,12 @@ Work through it in this order:
2. What does it say? `journalctl -u vantage-agent -f`.
3. Can that machine reach the endpoint? Test `GRPC_HOST` **from the machine**,
not from the control plane host.
4. Was the token already used or expired? It is single-use and lives one hour —
create a fresh enrolment rather than reusing the old command.
4. Was the token already used, or older than an hour? Generate a fresh install
command rather than reusing the old one.
| Symptom | Cause |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Registers, then goes `offline` within minutes | Something permits the short `Register` call but drops the long-lived stream. Usually a proxy or idle-timeout middlebox |
| Registers, then goes `offline` within minutes | The short registration call gets through but the long-lived connection is dropped, usually by a proxy or an idle timeout |
| Stays `pending` forever | Registration never happened. Token spent, or the endpoint unreachable |
| Flaps between `active` and `offline` | Intermittent path, or a poll interval longer than the offline threshold |
@@ -67,15 +66,15 @@ instantaneous.
## A workflow run fails or hangs
- **Hangs at dispatch.** The target's command stream is not connected the
- **Hangs at dispatch.** The target's command stream is not connected; the
server may be `offline`.
- **Fails immediately with an interpreter error.** A bash step on a Windows
target, or PowerShell on Linux.
- **A value does not reach the next step.** Values pass through the file at
`$WORKFLOW_ENV`, one `KEY=value` per line. Declaring an output does not export
it.
- **A secret is empty.** The group is not in the step's `secret_refs`, or the
key name differs from the environment variable you are reading.
`$WORKFLOW_ENV`, one `KEY=value` per line. Listing an output does not pass it
on by itself.
- **A secret is empty.** The group is not attached to that step, or the key name
differs from the variable you are reading.
- **Logs stop mid-run.** A reverse proxy read timeout cut the stream. The run
itself continues; reload the page.
@@ -85,8 +84,8 @@ instantaneous.
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Connects, then closes at once | guacd unreachable. Check `GUACD_ADDR` and that the container is running |
| SSH rejects the key | The stored key has no private half, or is not on the target |
| RDP fails on retry | Credentials are single-use and consumed at tunnel open enter them again |
| Hangs, then disconnects | The agent never claimed the relay, nothing is listening on the protocol port on the target's own loopback address, or guacd never dialled in time. Check the audit log for `console.proxy_failed` — its reason (`agent_timeout`, `dial_refused`, `guacd_timeout`, `rejected`) names which |
| RDP fails on retry | Credentials are single-use and consumed at tunnel open. Enter them again |
| Hangs, then disconnects | The agent could not reach the service on that machine, or setting up the session timed out. The audit log records which |
| Fails only in production | The reverse proxy is not forwarding WebSocket upgrade headers |
## Monitors report down when the service is up
@@ -98,7 +97,7 @@ instantaneous.
## Notifications are not arriving
Use the channel **Test** button it goes through the real delivery path, so a
Use the channel **Test** button. It goes through the real delivery path, so a
test that arrives proves credentials, network path and destination.
If the test fails: a webhook returning 300 or above counts as a failure, SMTP
@@ -109,9 +108,9 @@ needs `host`, `port`, `from` and `to`, and Telegram needs both `token` and
| Symptom | Cause |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `409 cloud_managed` when pasting | It is a cloud instance. Licences are written by HQ; there is nothing to paste |
| Licence rejected as not matching | It is bound to a different instance UUID. Relink in HQ |
| Instance degraded despite a valid-looking licence | It has expired past its grace period. Pasting still works that endpoint stays available specifically so it can |
| "Managed by Vantage HQ" when pasting | It is a cloud instance, which is licensed for you. There is nothing to paste |
| Licence rejected as not matching | It was issued to a different instance ID. Relink it in Vantage HQ |
| Instance degraded despite a valid-looking licence | It expired more than a few days ago. Pasting a new one still works, which is how you recover |
| Cannot enrol another server | The server allowance is reached. Raise it in HQ or remove one |
## HQ portal problems
@@ -129,5 +128,5 @@ docker compose logs --tail=200 server
journalctl -u vantage-agent --no-pager -n 200 # on the affected machine
```
Include your instance UUID from **Settings → Licence** it is the reference
Include your instance ID from the **Licence** page, which is the reference
support works from.
+17 -20
View File
@@ -4,7 +4,8 @@ title: Audit log
sidebar_label: Audit log
---
Every mutating API path writes an audit event. The log is at **Audit**.
Anything that changes something is recorded, whether it was done in the UI or by
a script. The log is at **Audit Log**.
## What an event carries
@@ -23,21 +24,24 @@ keys and assignments, workflow and step changes, runs triggered, monitors and
channels, secret groups and reveals, console sessions opened, settings and
member changes, licence installs.
Reads are not recorded, with one deliberate exception: **revealing a secret**
writes an event, because reading that particular thing is an act rather than a
lookup.
Simply looking at something is not recorded, with one exception: **revealing a
secret** is.
## What is not recorded
- Sign-ins and sign-out.
- Sign-ins and sign-outs.
- Anything inside a console session.
- Step output. That lives in the run log, kept under the workflow retention
setting rather than with the audit log.
## Retention
Audit events are not swept by the workflow log retention setting that setting
governs run logs only. Audit history stays until the instance does.
How long audit events are kept comes from your licence: 30 days on Free, a year
on Professional, and unlimited on Enterprise. See
[Licensing and entitlements](../hq/licensing-and-entitlements.md).
This is separate from the workflow log retention setting, which covers run logs
only.
:::warning It is a log, not a control
The audit log tells you what happened. It does not restrict what can happen, and
@@ -45,18 +49,11 @@ an admin can do anything an admin can do. Use roles for restriction and the log
for accountability.
:::
## Getting events out
## Searching and exporting
`GET /api/audit` returns one page of events as JSON:
The page searches by actor, detail and event type, and filters by category, such
as `workflow`, `key` or `server`. The count shown is the number of matching
events, not the number on screen.
```json
{ "events": [ ... ], "total": 3214 }
```
It accepts `limit` (default 50, maximum 200), `skip`, `q` to search actor,
details and event type, and `category` to match the part of an event type
before the dot — `workflow`, `key`, `server`. `total` counts everything
matching the filter, not the page, so a short page is not the end of the log.
There is no streaming or push export; if you need events in a SIEM, poll that
endpoint, walking `skip` until you have `total`.
The same events are available from the API if you want them in a log system of
your own. See [Automating Vantage](../reference/rest-api.md).
+36 -47
View File
@@ -4,70 +4,59 @@ title: Browser console
sidebar_label: Browser console
---
An SSH, RDP or VNC session in a browser tab, with no client software and no
inbound port on the target beyond the one the protocol already uses.
An SSH, RDP or VNC session in a browser tab, with no client software to install
and no new port to open on the target machine.
Protocol handling is Apache Guacamole's the control plane proxies a WebSocket
to a **guacd** daemon and manages credentials around it.
:::info Requires the console feature on your licence
The console is a per-instance feature you enable on a paid plan. Without it, the
Console button is unavailable. See
[Licensing and entitlements](../hq/licensing-and-entitlements.md).
:::
## Requirements
## What you need
- `guacd` running and reachable from the server. The bundled Compose stack
includes it; `GUACD_ADDR` defaults to `guacd:4822`.
- `KEY_ENCRYPTION_KEY` set, since every credential involved is stored encrypted.
- The target's **agent must be online**. Console traffic is relayed over the
agent's existing outbound connection, so the control plane never needs a route
to the server's address — but it does need the agent.
- No inbound port on the target, beyond what the protocol already listens on
locally. A service bound only to `127.0.0.1` works, because the agent dials
loopback on the target itself.
- The target server's **agent must be online**. Console traffic travels over the
connection the agent already holds, so an offline agent means no session.
- `KEY_ENCRYPTION_KEY` set on a self-hosted install, since every credential
involved is stored encrypted.
- The service you are connecting to listening on the machine itself. It does not
have to be reachable from anywhere else, because the agent connects to it
locally.
## Opening a session
From a server's page, choose **Console**. Then:
1. The UI calls `POST /api/console/connect`, which mints a **one-time** session
token. If the target's agent is not connected, this fails immediately with
`409 agent_offline` rather than hanging.
2. The browser opens a WebSocket to `GET /api/console/tunnel` with that token.
3. The server marks the token consumed atomically, so a second use cannot
race and proxies the connection to guacd.
From a server's page, choose **Console**, pick the protocol and connect. Vantage
issues a one-time ticket for that session, and the connection is refused rather
than left hanging if the agent is not online.
## Credentials
### SSH
Authenticates with a private key stored in the [key library](./ssh-keys.md). The
key must have its private half uploaded; a public-only key cannot open a
session.
Uses a private key from your [key library](./ssh-keys.md). The key must have had
its private half uploaded; a public key alone cannot open a session.
### RDP and VNC
You supply credentials when connecting. They are encrypted, **single-use**, and
consumed when the tunnel opens. They are not retained for the next session.
You type the credentials when you connect. They are encrypted, used once and
discarded, so the next session asks again.
:::info Why single-use
A stored console credential is a standing grant to that machine for anyone who
can reach the endpoint. Consuming it at tunnel-open means a leaked session token
is worth one connection at most, and only until it is used.
:::
## During and after a session
## Session behaviour
Closing the tab ends the session. There is no reconnect: opening it again starts
a fresh session.
Closing the tab ends the session. There is no reconnect and no session
persistence reopening mints a new token and a new connection.
## Auditing
Opening a console is an audited action, with actor, server and time. What
happens _inside_ the session is not recorded: there is no session capture or
keystroke log. If you need that, it has to come from the target machine.
Opening a console is recorded in the [audit log](./audit-log.md), with who did
it, which server and when. What happens inside the session is not recorded.
There is no session replay or keystroke capture, so if you need that, it has to
come from the target machine itself.
## When it does not work
| Symptom | Cause |
| -------------------------------- | ------------------------------------------------------------------------------- |
| Connects then closes immediately | guacd unreachable check `GUACD_ADDR` and that the container is up |
| SSH refuses the key | The stored key has no private half, or is not in the target's `authorized_keys` |
| RDP fails on a fresh credential | Credentials are consumed on open; a retry needs them entered again |
| Hangs, then disconnects | The agent never claimed the relay, nothing is listening on the protocol port on the target's own loopback address, or guacd never dialled in time. Check the audit log for `console.proxy_failed` — its reason (`agent_timeout`, `dial_refused`, `guacd_timeout`, `rejected`) names which |
| Symptom | What to check |
| -------------------------------- | ------------------------------------------------------------------------------------------ |
| Connects, then closes at once | The console daemon is unreachable. On a self-hosted install, check that `guacd` is running |
| SSH refuses the key | The stored key has no private half, or is not assigned to that server |
| RDP fails when you retry | Credentials are used once. Enter them again |
| Hangs, then disconnects | The agent could not reach the service on the machine, or the session timed out setting up. The audit log records the reason |
| Works locally, fails in production | Your reverse proxy is not forwarding WebSocket connections |
+15 -16
View File
@@ -28,22 +28,22 @@ Every monitor has a **runner**:
| `server` | The control plane's scheduler performs the check |
| a server ID | That server's agent performs it locally and reports the result |
Use `server` for anything reachable from the control plane public endpoints,
your own front door. Use an agent for anything only reachable from inside the
Use `server` for anything Vantage itself can reach, such as your public website
or API. Use an agent for anything only reachable from inside the
target network: a database on a private subnet, a service bound to localhost, a
device on a management VLAN.
:::tip Agent-run monitors measure what your users can't
A check run from the control plane tells you the service is reachable from
there. A check run on the machine tells you the process is alive. Those are
different questions, and outages usually live in the gap.
:::tip The two answer different questions
A check from Vantage tells you the service is reachable over the network. A
check on the machine tells you the process is running. Watch both where it
matters.
:::
## Interval, retries and state
- **Interval** how often to check.
- **Retries** how many consecutive failures are tolerated before the state
flips.
- **Interval** is how often to check.
- **Retries** is how many failures in a row are tolerated before the state
changes.
A monitor sits in `pending` until its first result. Failures accumulate; once
they exceed `retries`, the monitor goes `down`, an **incident** opens and the
@@ -58,21 +58,20 @@ Attach one or more [notification channels](./notification-channels.md) to a
monitor. Channels are shared, so one Slack destination can serve every monitor
you have.
Notification state is tracked per monitor, so a service that is down for six
hours does not send a message per interval.
You get a message when a monitor goes down and another when it recovers, not one
per check while it stays down.
## Uptime and incidents
The monitor detail page shows:
- **Uptime**, from hourly rollup records checks performed, how many were up,
and mean latency per hour. Rollups are what make the graph cheap to draw over
long windows.
- **Uptime**, summarised per hour: how many checks ran, how many passed and the
average response time.
- **Incidents**, each with a start, a resolution and the cause recorded at the
moment it opened.
## Disabling versus deleting
Disabling stops the checks and keeps the history. Deleting removes the monitor.
Prefer disabling for anything seasonal the uptime record is usually the part
you wanted.
Disable anything seasonal rather than deleting it, since the uptime record is
usually the part worth keeping.
+6 -8
View File
@@ -4,8 +4,8 @@ title: Notification channels
sidebar_label: Notification channels
---
A channel is a destination for alerts. [Monitors](./monitors.md) reference
channels by ID, so one destination serves as many monitors as you like.
A channel is a destination for alerts. One channel serves as many
[monitors](./monitors.md) as you like.
Manage them at **Settings → Notifications**.
@@ -64,9 +64,7 @@ Posts the alert as message content.
Port `465` uses implicit TLS; anything else uses STARTTLS.
Alert emails are rendered by the same email system that sends licence and
account mail, so a monitor alert and an account email look like the same
product.
Alert emails look like the rest of the mail Vantage sends you.
## The message
@@ -83,9 +81,9 @@ something that needs to branch on status.
## Testing
Every channel has a **Test** button. It dispatches a fabricated down event for a
monitor called "Test monitor", through the real delivery path so a test that
arrives proves the credentials, the network path and the destination, not just
the configuration form.
monitor called "Test monitor" and sends it the same way a real alert goes out, so
a test that arrives proves the credentials, the network path and the destination
as well as the form.
:::tip Test after every change
Channel settings are only exercised when something breaks, which is the worst
+22 -23
View File
@@ -10,54 +10,53 @@ External Secrets Operator.
## Groups and values
A **group** is a named bundle `prod-db`, `registry`, `acme-api`. Inside it are
key/value pairs.
A **group** is a named bundle, such as `prod-db`, `registry` or `acme-api`.
Inside it are key/value pairs.
Group by consumer, not by type. A group is the unit a workflow step references
and the unit ESO reads, so a group that matches one consumer is one reference;
a group holding everything is over-sharing to every step that needs any of it.
Group by who uses them rather than by what they are. A workflow step references
a whole group, so a group that matches one job stays tidy, while a group holding
everything hands all of it to every step that needs any of it.
## Managing them
**Secrets → New group**, then add keys.
Values are write-then-hidden. The list shows keys, never values. **Reveal** is a
separate action on a separate endpoint, and it writes an audit event so
looking at a secret is a recorded act.
Once saved, a value is hidden. The list shows key names only. **Reveal** is a
separate action, and it is written to the audit log.
Deleting a single key and deleting the whole group are separate operations.
## Using secrets in workflows
Add the group name to a step's `secret_refs`. At execution the group's pairs are
injected into the step's environment:
Add the group to a step's secret references. When the step runs, the group's
pairs are available to it as environment variables:
```bash
# secret_refs: ["registry"]
# with the "registry" group attached to this step
echo "$REGISTRY_PASSWORD" | docker login registry.example.com -u "$REGISTRY_USER" --password-stdin
```
A workflow can also override `secret_refs` per step, without changing the
library entry.
A workflow can change which groups a step uses without changing the step in the
library.
:::warning A step can print its own secrets
Injection puts values in the environment. If your script echoes them, or runs
with `set -x`, they land in the run log which is stored on disk and readable
in the UI. Vantage does not scrub step output.
Values arrive as environment variables. If your script prints them, or runs with
`set -x`, they end up in the run log, which anyone who can see the run can read.
Vantage does not filter step output.
:::
## Kubernetes External Secrets Operator
`GET /api/secrets/:group/values` returns a group's pairs for ESO, authenticated
with a **bearer token** rather than a session.
Kubernetes can read a secret group directly, using a token rather than a
sign-in.
1. Generate the token at **Settings → Integrations**. It is shown once; only its
SHA-256 is stored.
1. Generate the token at **Settings → Integrations**. It is shown once, and
Vantage stores only a fingerprint of it.
2. Put it in a Kubernetes secret.
3. Point an ESO `SecretStore` at the endpoint with that bearer token.
3. Point an External Secrets Operator `SecretStore` at your Vantage address with
that token.
The token is rotatable: generating a new one replaces the stored hash and
invalidates the old one immediately.
Generating a new token replaces the old one immediately.
:::danger This token reads every group
It is instance-wide, not scoped to one group. Treat it as a credential to the
+24 -34
View File
@@ -5,8 +5,8 @@ sidebar_label: Servers
---
The fleet. Every managed machine runs an agent that connects outbound to the
control plane, and everything else in Vantage keys, workflows, monitors,
consoles targets these records.
control plane. Keys, workflows, monitors and consoles all point at these
records.
## Enrolling a server
@@ -23,18 +23,17 @@ a one-liner to run as root on the target machine.
| `offline` | Last-seen passed the threshold |
The offline sweep runs every two minutes, so a machine that has just gone away
takes a little while to be marked as such. That delay is intentional a single
missed poll is not an outage.
takes a little while to be marked as such.
## Tags
A tag is a `key:value` label you put on a server. Tags are how you say what a
machine **is** `env:prod`, `role:web`, `team:core-infra` so that you can find
it later, and so that a [workflow](./workflows.md) can target it without you
machine is, such as `env:prod`, `role:web` or `team:core-infra`, so that you can
find it later and so a [workflow](./workflows.md) can target it without you
naming it by hand.
There is no tag library to manage first. A tag exists because a server carries
it, and it stops existing when the last server carrying it drops it.
There is no tag library to set up first. A tag exists as soon as a server
carries it, and disappears when the last server carrying it drops it.
### The rules
@@ -45,25 +44,22 @@ it, and it stops existing when the last server carrying it drops it.
| Value length | up to 64 characters |
| Per server | up to 20 tags |
Neither half may be empty, and keys beginning `sys:` are reserved for tags
Vantage may derive from inventory later, so a tag you write today can never
collide with one invented for you tomorrow.
Neither half may be empty, and keys beginning `sys:` are reserved for Vantage's
own use.
Anything outside those rules is refused with a message naming the rule, rather
than quietly saved in a shape you did not intend. Uppercase is not folded to
lowercase for you `Env` is a mistake, not a synonym for `env`.
Anything outside those rules is refused, with a message naming the rule.
Uppercase is not corrected for you, so `Env` and `env` are different tags.
### Editing a server's tags
On the server detail page, **Edit** beside the tag chips. Saving replaces the
whole set: what you see in the editor is exactly what the server will have.
There is no per-tag merge, so if two people edit the same server at once, the
last save wins outright rather than producing a blend of the two.
On the server's page, click **Edit** beside the tags. Saving replaces the whole
set, so what you see in the editor is exactly what the server ends up with. If
two people edit the same server at once, the last save wins.
### Filtering the fleet
The **Servers** list has a picker per tag key in use. Choosing values from more
than one key narrows the list a server must match **all** of them, not any.
than one key narrows the list, because a server must match **all** of them.
Untagged servers appear only when no filter is set.
:::tip A filtered fleet view is a link
@@ -96,16 +92,13 @@ metrics is normal rather than a fault.
Agents check for pending package updates hourly and report the count. From the
server page you can:
- **Apply updates** pushes `ApplyUpdatesCmd` down the command stream. The
agent runs the platform's package manager and reports back.
- **Update agent** pushes `UpdateAgentCmd` with a target version; the agent
downloads the release, verifies it and replaces itself. See
- **Apply updates** runs the machine's own package manager and reports back.
- **Update agent** upgrades the Vantage agent on that machine. See
[Agent updates](../operations/agent-updates.md).
:::warning Applying updates is not scheduled or staged
It runs now, on that machine. If you need ordering, health gates or a canary,
build it as a [workflow](./workflows.md) instead that is what workflows exist
for.
It runs immediately, on that machine. If you need ordering, health checks or a
test machine first, build it as a [workflow](./workflows.md) instead.
:::
### Console
@@ -114,10 +107,8 @@ Opens a browser SSH, RDP or VNC session. See [Browser console](./browser-console
## Windows servers
Windows agents register, heartbeat, run workflow steps and report inventory.
They do not manage `authorized_keys` the poll loop stops after the heartbeat
on any non-Linux host. This is a deliberate scope decision, not a gap being
worked on.
Windows agents register, run workflow steps and report inventory. They do not
manage `authorized_keys`.
## Removing a server
@@ -138,7 +129,6 @@ no longer running to remove them. Revoke and let the agent apply the change
## Agent tokens
Each server has its own token. The control plane stores only its SHA-256; the
plaintext exists in the agent's `0600` config and nowhere else. There is no way
to read a token back out of the control plane if one is lost, re-enrol the
machine.
Each server has its own token, which exists in full only in the agent's config
file on that machine. Vantage stores a fingerprint of it and cannot show it to
you again. If a token is lost, enrol the machine again.
+65 -91
View File
@@ -4,31 +4,14 @@ title: Settings
sidebar_label: Settings
---
One page, three groups: **Access**, **Monitoring** and **Integrations**. Plus
the licence, which has its own page.
One page, three groups: **Access**, **Monitoring** and **Integrations**. Your
licence has its own page.
Settings require the `owner` or `admin` role.
:::info Where instance settings went
Members and single sign-on used to live at `/settings/instance`. They are now
the Access group at the top of this page splitting "who can sign in" from "how
this instance behaves" produced two half-pages and a nav entry nobody could
distinguish from Settings. The old path still redirects.
:::
:::danger Upgrading breaks existing single sign-on until you re-register the callback URL
Callback URLs are now per provider instead of one shared URL for the whole
instance. If you already had single sign-on configured, it was carried
forward automatically, but its callback URL changed and **sign-in through it
will fail until you copy the new callback URL from its settings card and
register it with your identity provider**. The migrated provider's card shows
a dismissable warning as a reminder. Password sign-in is not affected by this
change, so an administrator can always sign in locally to make the update.
:::
Settings need the `owner` or `admin` role.
## Access
### Members
### People
Add, remove and re-role the people who can sign in.
@@ -38,112 +21,103 @@ Add, remove and re-role the people who can sign in.
| `admin` | Everything except owner-only settings |
| `member` | Servers, keys, workflows, monitors, secrets, console |
Local members authenticate with email and a bcrypt-hashed password.
Local members sign in with an email address and a password.
#### Members managed by Vantage HQ
#### People managed by Vantage HQ
On a cloud instance, people granted access from the HQ portal appear here as
read-only rows with a link to the portal.
On a cloud instance, anyone granted access from the Vantage HQ portal appears
here as a read-only row with a link back to the portal.
:::warning HQ-managed users cannot be edited locally
Changing the role of, or deleting, an `hq`-sourced user is refused with `409`.
HQ owns their role, their password and whether they exist at all a local
change would be overwritten by the next sync and would leave two writers for one
password hash. Manage them from [People and roles](../hq/people-and-roles.md).
:::warning You cannot edit those people here
Their role, password and access are owned by Vantage HQ, so changing or removing
them has to be done there. See
[People and roles](../hq/people-and-roles.md).
:::
### Single sign-on
Add as many identity providers as you need: one instance can have several at
once, each with its own name, its own button on the login page and its own
callback URL.
:::info Requires the single sign-on feature on your licence
It is a per-instance feature you enable on a paid plan.
:::
Pick a provider from the list of presets:
Add as many identity providers as you need. Each has its own name, its own
button on the login page and its own callback URL.
| Preset | You provide |
| ---------------------- | ------------------------------------------- |
| Microsoft Entra ID | Directory (tenant) ID |
| Google Workspace | nothing further, the issuer is fixed |
| Okta | Your Okta org domain |
| GitHub | Client ID and client secret only |
| Other (OpenID Connect) | The issuer URL of your identity provider |
If you configured single sign-on on an older version, see
[Upgrading](../operations/upgrading.md#single-sign-on-after-an-upgrade).
Every provider also needs a **Client ID** and **Client secret**; the secret is
stored AES-256-GCM encrypted and never shown again after you save it.
Start from a preset:
:::info GitHub requires a verified primary email
Vantage signs a person in by their email address. GitHub is asked for the
account's addresses and only accepts one that is **both** the account's
primary address **and** marked verified: an address GitHub has not confirmed
is not proof anyone controls it.
| Preset | You provide |
| ---------------------- | ---------------------------------------- |
| Microsoft Entra ID | Directory (tenant) ID |
| Google Workspace | Nothing further |
| Okta | Your Okta org domain |
| GitHub | Client ID and client secret only |
| Other (OpenID Connect) | The issuer URL of your provider |
Every provider also needs a **Client ID** and **Client secret**. The secret is
stored encrypted and is never shown again after you save it.
:::info GitHub needs a verified primary email
Vantage identifies people by email address, and it only accepts a GitHub address
that is both the account's primary address and confirmed by GitHub.
:::
#### Callback URL
Each provider gets its own callback URL, shown on its settings card with a
copy button. This is the address you register with the identity provider when
you set up the application on their side: each provider is registered
separately, even if you have several with the same identity provider.
Each provider's card shows its callback URL with a copy button. That is the
address you register with the identity provider when you set up the application
on their side. Register each provider separately, even where several use the
same identity provider.
#### Turning off password sign-in
You can disable local (email and password) sign-in once at least one provider
is enabled. Vantage refuses to save a change that would leave nobody able to
sign in, whether that change comes from the local login toggle or from
disabling the last enabled provider. Keep at least one option open until every
person who needs access can reach the new one.
Once at least one provider is enabled you can turn off email and password
sign-in. Vantage refuses any change that would leave nobody able to sign in,
whether that is switching off passwords or disabling your last provider. Keep
one route open until everyone who needs access can use the new one.
## Monitoring
- **Alert defaults** for monitors.
- **Notification channels** their own page. See
[Notification channels](./notification-channels.md).
## Integrations
### Workflow log retention
How long run logs are kept.
- **Offline threshold**, how long a server may go unheard from before it is
marked offline. The default is 5 minutes.
- **Offline alerts**, the [notification channels](./notification-channels.md) to
tell when that happens.
- **Notification channels** have [their own page](./notification-channels.md).
- **Workflow log retention**, how long run logs are kept.
| Value | Meaning |
| -------- | -------------- |
| unset | 30 days |
| a number | that many days |
| `0` | forever |
| `0` | keep forever |
### ESO read token
## Integrations
The bearer token External Secrets Operator uses to read secret groups. Shown
once, stored as a SHA-256 hash, rotatable. See
### External Secrets Operator token
The token Kubernetes uses to read your secret groups. It is shown once, stored
only as a fingerprint, and can be replaced at any time. See
[Secrets](./secrets.md#kubernetes-external-secrets-operator).
## Licence
`/settings/license` shows the deployment, tier, server allowance, enabled
features and expiry.
The **Licence** page, in the sidebar, shows your instance ID, whether you are
cloud or self-hosted, your tier, server allowance, enabled features and expiry
date.
On **self-hosted**, paste a licence here. This works even while the current
licence is expired that is the way out of degraded mode.
On a **self-hosted** install you paste your licence here. This works even while
your current licence has expired, which is how you get an instance out of
read-only mode.
On **cloud**, there is no paste form. The endpoint answers `409 cloud_managed`,
because a cloud licence is written by HQ directly. The page links to the portal
instead.
On a **cloud** instance there is nothing to paste. Licences are installed for
you, and the page links to the portal instead.
See [Licensing and entitlements](../hq/licensing-and-entitlements.md).
## Sessions
Sessions are an opaque token in the `km_session` cookie, held in Redis with a
24-hour TTL. There is no per-session management UI; restarting Redis signs
everyone out and affects nothing else.
## Host and organisation guard
On a multi-tenant deployment, a request to `<slug>.vantage.<tld>` resolves the
organisation from the slug and rejects a session belonging to a different one.
The label it looks for comes from `APP_ROOT_LABEL`.
:::warning A wrong `APP_ROOT_LABEL` disables the guard
It does not fail loudly it simply stops matching, and the host check stops
protecting anything. If you serve the UI on a custom domain, set it to match.
:::
Signing in gives you a session that lasts 24 hours. There is no session list to
manage. On a self-hosted install, restarting Redis signs everyone out and affects
nothing else.
+15 -18
View File
@@ -22,14 +22,12 @@ fingerprint, and never needs the private half for this path.
### Generate one on a server
Vantage can have an agent generate a keypair on a managed machine
(`GenerateKeyCmd` over the command stream). The public half comes back to the
library. You may optionally upload the private half too, in which case it is
Vantage can have a managed machine generate a keypair for you. The public half
comes back to the library. You may optionally upload the private half too, in which case it is
stored **AES-256-GCM encrypted** under `KEY_ENCRYPTION_KEY`.
The JSON representation of a key exposes only `has_private_key` and
`has_passphrase` never the material. Retrieving a stored private key is its
own endpoint and its own audit event.
Vantage never displays stored private key material in a list. Retrieving one is
a separate, deliberate action, and it is written to the audit log.
:::tip Why store a private key at all
The [browser console](./browser-console.md) needs one to open an SSH session. If
@@ -38,13 +36,13 @@ you are not using the console, do not upload private halves.
## Assigning
Assign a key to one or more servers. Within one poll interval 30 seconds the
agent picks up the change.
Assign a key to one or more servers. The agent picks up the change within about
30 seconds.
## Revoking
Revocation is **soft**: the assignment gets a `revoked_at` timestamp rather than
being deleted, so the history of who had access to what, and when, survives.
Revoking marks the assignment revoked, with a timestamp, rather than erasing it,
so the record of who had access to what, and when, survives.
The agent treats a revoked assignment as "not desired" and removes the line from
`authorized_keys` on its next sync.
@@ -60,13 +58,12 @@ Each poll:
1. The control plane returns the desired set of public keys for that server.
2. The agent reads `/root/.ssh/authorized_keys` and computes fingerprints.
3. **If the sets match, it writes nothing.** No disk churn on unchanged state,
which is most polls.
4. If they differ, it writes a temporary file, then `os.Rename()`s it over the
real one and sets mode `0600`.
3. **If they match, it writes nothing.** That is true of almost every check.
4. If they differ, it writes the new file alongside the old one and swaps it in
one step.
The rename is atomic, so a machine that dies mid-write keeps the old file
intact. There is no window in which `authorized_keys` is truncated or partial.
The swap cannot be interrupted halfway, so a machine that loses power mid-change
keeps its old, working file.
:::danger Vantage owns the whole file
The agent rewrites `authorized_keys` to match the desired set. Keys added by
@@ -77,5 +74,5 @@ it in Vantage.
## Recovering from a lockout
If you have removed every key from a machine and cannot get in, you still have
the console provided a private key is stored or out-of-band access from your
hosting provider. Vantage has no backdoor and does not keep a break-glass key.
the console, provided a private key is stored, or whatever out-of-band access
your hosting provider offers. Vantage has no backdoor and does not keep a break-glass key.
+19 -22
View File
@@ -9,8 +9,8 @@ against the security advisories published by that server's own distribution and
raises a finding for anything not yet patched.
Requires the **vulnerability scanning** feature on your licence. Without it,
agents collect nothing at all — there is no inventory stored and no findings
page to read.
nothing is collected and there is no findings page. See
[Licensing and entitlements](../hq/licensing-and-entitlements.md).
## What gets scanned
@@ -20,10 +20,9 @@ report.
Windows servers are not scanned.
Some distributions publish no machine-readable advisory feed. Those servers
show **unsupported** on their own page rather than appearing as having no
vulnerabilities — the two are very different answers, and only one of them is
good news.
Some distributions publish no security advisories Vantage can read. Those
servers are shown as **unsupported**, rather than as having no vulnerabilities.
Those are very different answers, and only one of them is good news.
## Why versions look "wrong"
@@ -43,9 +42,9 @@ the accurate one for the package you are actually running.
## The board
`/vulnerabilities` groups findings by CVE. One row per CVE with the number of
affected servers, expandable to the individual servers — the same CVE across
forty machines is one decision, not forty.
The **Vulnerabilities** page groups findings by CVE, one row each, with the
number of servers affected. Expand a row to see them. The same CVE across forty
machines is usually one decision, not forty.
Severity counts at the top filter the list when clicked. The state tabs switch
between **open**, **accepted** and **fixed**.
@@ -60,8 +59,8 @@ A finding with a known fixed version gets an **Apply updates** button, which
runs the same OS update the server page offers. There is no separate patching
mechanism.
Vantage never patches automatically. An unattended upgrade triggered by a third
party's data feed is a fleet-wide change nobody chose.
Vantage never patches automatically. Applying updates is always something you
ask for.
## Accepting a finding
@@ -72,23 +71,21 @@ or one with no vendor fix published at all.
a reason that is recorded in the audit log along with your name. On that date it
reopens by itself.
The expiry is required. A dismissal with no end date is how a finding gets
forgotten, and it is exactly what an auditor will ask to see.
An expiry date is required, so nothing is dismissed permanently by accident.
## Alerts
Alert rules live with your notification channels, under
**Settings → Notification Channels**. A rule has a minimum severity, an optional
server tag filter, and one or more channels.
Alert rules live with your
[notification channels](./notification-channels.md). A rule has a minimum
severity, an optional server tag filter, and the channels to notify.
A rule sends **one digest per scan** summarising what newly opened — never one
message per finding. A database refresh can open several hundred findings at
once, and a message each would flood the channel.
A rule sends **one summary per scan** covering everything newly found, rather
than one message per finding. A single update to the security data can raise
hundreds at once.
Findings that were already open do not re-alert.
## Fleet-wide package search
`GET /api/packages/search?name=openssl` answers which servers run a given
package and at what version, across the whole fleet. Useful during an incident
before a finding exists for it.
Search your whole fleet for a package by name to see which servers have it and
at what version. Useful during an incident, before there is a finding for it.
+65 -82
View File
@@ -4,10 +4,9 @@ title: Workflows and steps
sidebar_label: Workflows
---
A **step** is a reusable script with declared inputs, outputs and secret
references. A **workflow** composes steps in order and targets a set of servers.
Running one dispatches the steps to each target's agent and streams the output
back live.
A **step** is a reusable script with its own inputs, outputs and secrets. A
**workflow** puts steps in order and aims them at a set of servers. Running one
sends the steps to each server and streams the output back as it happens.
## Steps
@@ -15,12 +14,12 @@ A step has:
| Field | Meaning |
| --------------------- | ----------------------------------------------- |
| `name`, `description` | Library identity |
| `interpreter` | `bash` or `powershell` |
| `script` | The body |
| `declared_inputs` | Named parameters with defaults and descriptions |
| `declared_outputs` | Names this step promises to export |
| `secret_refs` | Vault entries injected as environment variables |
| Name and description | How you recognise it in the library |
| Interpreter | `bash` or `powershell` |
| Script | What it runs |
| Inputs | Named parameters, with defaults |
| Outputs | Values this step passes on |
| Secrets | Vault groups made available to it |
### Passing values between steps
@@ -34,42 +33,36 @@ echo "$HOSTNAME"
echo "HOSTNAME=$HOSTNAME" >> $WORKFLOW_ENV
```
That is the whole mechanism. `declared_outputs` documents what a step exports so
the designer can show it; the file is what actually carries the value.
That is the whole mechanism. Listing a step's outputs documents them for the
designer, but writing to that file is what actually passes a value on.
### Secrets
List a vault group in `secret_refs` and its key/value pairs are injected as
environment variables when the step runs. They are not written to the run log
unless your own script echoes them. See [Secrets](./secrets.md).
Add a vault group to a step and its pairs are available as environment variables
while it runs. They do not appear in the run log unless your own script prints
them. See [Secrets](./secrets.md).
### The workspace
Every run gets a per-run working directory on each target. Steps share it, so
one step can leave a file for the next. The agent removes it at the end of the
run (`CleanupWorkspaceCmd`).
Every run gets its own working directory on each server. Steps share it, so one
step can leave a file for the next. It is deleted when the run finishes.
Do not use it for anything that must outlive the run.
## Default steps
A small library is seeded into every organisation at boot from the image, so a
new install is not staring at an empty page.
Vantage ships a small library of ready-made steps, so a new install is not
staring at an empty page.
:::warning Default steps are read-only
Editing or deleting one is refused with `409`. Seeding rewrites them on every
boot, so an edit would silently revert and a delete would come back at the next
restart refusing is the honest answer.
They are reinstalled every time Vantage restarts, so any edit or deletion would
come back anyway. Vantage refuses the change rather than letting it quietly
revert.
To customise one, use the per-step **script override** in the workflow designer,
which belongs to that workflow and is not touched by seeding. To add to the
shared library permanently, a file has to be committed to the repository and the
server image rebuilt.
To adapt one, override its script inside the workflow that uses it. That change
belongs to the workflow and is left alone.
:::
The UI mirrors this the step modal opens read-only and Delete is hidden but
the API is the boundary; the UI is the courtesy.
## Building a workflow
1. **Workflows → New**.
@@ -81,11 +74,11 @@ the API is the boundary; the UI is the courtesy.
### Failure behaviour
| `on_failure` | Effect |
| ------------ | --------------------------------------------------------------- |
| `stop` | Abort this server's run. Other servers continue |
| `continue` | Record the failure, run the next step anyway |
| `retry` | Re-run the step up to `max_retries`, then treat it as a failure |
| On failure | Effect |
| ---------- | ------------------------------------------------------------- |
| Stop | Stop this server's run. Other servers carry on |
| Continue | Record the failure and run the next step anyway |
| Retry | Try again up to the limit you set, then count it as a failure |
### Per-step overrides
@@ -97,27 +90,21 @@ scoped to that workflow.
A workflow names servers two ways, and it can use both at once:
- **Target servers** an explicit list you pick from the fleet.
- **Target tags** a `key:value` selector matched against
[server tags](./servers.md#tags). More than one key ANDs: a server must carry
every pair to match.
- **Target servers**, a list you pick by hand.
- **Target tags**, matched against [server tags](./servers.md#tags). Give more
than one tag and a server must carry all of them to match.
A run goes to the **union** of the two, with duplicates removed. A server that is
both named explicitly and matched by the selector runs once, not twice. This is
what lets a workflow say "every production web server, plus this one box I am
watching" without maintaining a list.
A run goes to both sets combined. A server that is named by hand and also matched
by a tag runs once. That is how a workflow can say "every production web server,
plus this one machine I am watching" without you keeping a list up to date.
The designer shows the resolved count as you edit, so you can see how many
machines a change to the selector just added or removed before you save.
:::warning An empty selector matches nothing
Clearing the tag selector does not mean "all servers". A workflow with no named
servers and no tags matches nothing and is refused at run time rather than
reported as a success over zero machines.
The alternative reading, where an empty field means the whole fleet, turns a
cleared box into a fleet-wide run. That is not a mistake anyone should be able to
make by deleting text.
Clearing the tags does not mean "all servers". A workflow with no servers and no
tags matches nothing, and running it is refused rather than reported as a
success over zero machines.
:::
Tags are read **at run time**, not when you save. Tag a new machine `env:prod`
@@ -127,31 +114,29 @@ machine from every workflow that selected on it.
### Offline servers are still targeted
A server matched by tag is dispatched to even if its agent is offline, and that
step fails visibly on that machine. Vantage does not quietly shrink your target
list to the machines that happened to be reachable a patch run that skipped
three servers and reported success is worse than one that failed on three and
said so.
A server is still targeted when its agent is offline, and the run fails visibly
on that machine. Vantage does not quietly drop unreachable servers from a run,
because a patch run that skipped three servers and called itself a success is
harder to spot than one that failed.
Re-run the workflow once they are back, or fix the agent first.
## Running
**Run** snapshots the resolved steps into the run record and dispatches each step
to the target's agent over the command stream no waiting for the next poll.
**Run** records the exact steps being run, then sends them to each server
straight away.
:::info Runs freeze their steps
The snapshot is why editing a step tomorrow never rewrites what happened today.
A run shows the script that actually executed, not the current library version.
Editing a step tomorrow never changes what a past run shows. A run always
displays the script that actually ran.
:::
Targets run **in parallel**; steps within one server run **in order**.
## Schedules
A workflow can carry a schedule, and Vantage will start it the same way a person
would — the same dispatch, the same snapshot, the same run page. A scheduled run
is an ordinary run with `schedule` recorded as who triggered it.
A workflow can run on a schedule. A scheduled run is an ordinary run, on the
same run page, with the schedule recorded as what started it.
Open a workflow, choose **Edit**, and tick **Run on a schedule**. The expression
is standard five-field cron:
@@ -175,10 +160,10 @@ than the browser, so what you see is exactly what will fire.
### Timezones
A schedule stores an IANA timezone by name `Europe/London`, not an offset.
That is what makes a 02:00 job stay at 02:00 across a daylight-saving change
instead of drifting an hour for half the year. An unknown zone is refused when
you save it, not at 2am.
A schedule stores a timezone by name, such as `Europe/London`, rather than an
offset. That keeps a 02:00 job at 02:00 across daylight-saving changes instead
of drifting by an hour for half the year. A timezone Vantage does not recognise
is refused when you save it.
### Overlaps are skipped, not queued
@@ -189,28 +174,27 @@ should fall behind visibly rather than pile up.
### Missed occurrences
If the control plane was not running when an occurrence was due, it still fires
when the control plane comes back — but only within **one hour** of the due
time. Anything older is recorded as missed and dropped. A job missed by ten
minutes during an upgrade should still run; one missed by two days should not
suddenly fire at lunchtime.
If Vantage was not running when a scheduled run was due, it still runs when
Vantage comes back, as long as that is within **one hour** of the due time.
Anything older is recorded as missed and skipped, so a job missed during a short
upgrade catches up, while one missed for two days does not suddenly start at
lunchtime.
Either kind of skip is shown on the workflow's schedule panel, with the time it
was due and why it did not run.
## Watching a run
Step stdout and stderr stream back as chunks, are appended to a log file on the
server, and the UI follows them live. Each step records status, attempts, exit
code and its exported environment.
Output streams back as it happens and the page follows it live. Each step
records its status, how many attempts it took, its exit code and any values it
passed on.
**Cancel** stops a run in progress. Steps already running on an agent finish;
nothing further is dispatched.
## Log retention
Run logs are swept on a schedule set by `workflow_log_retention_days` in
Settings:
How long run logs are kept is set under **Settings → Monitoring**:
| Value | Meaning |
| -------- | -------------- |
@@ -220,16 +204,15 @@ Settings:
## Import and export
Steps export to a JSON file (`vantage.step/v1`) and import back, which is how
you move a step between instances or keep one in version control. There is also
a parse endpoint that turns a pasted script into a draft step by reading its
declared inputs and outputs.
A step exports to a file and imports back, which is how you move one between
instances or keep it in version control. You can also paste a script and have
Vantage turn it into a draft step for you.
## Practical notes
- A step is a script. It runs as root, on the target, with no sandbox. Review
what you import.
- Keep steps small and single-purpose; compose them in the workflow. That is
what makes the library reusable rather than a folder of near-duplicates.
- Keep steps small and single-purpose, and combine them in the workflow. A
library of small steps stays reusable.
- PowerShell steps only make sense on Windows targets and bash steps on Linux
ones. Nothing stops you targeting the wrong one; the step simply fails.
+33 -43
View File
@@ -4,30 +4,27 @@ title: Workloads
sidebar_label: Workloads
---
A **workload** is one Docker container or one systemd service. Each Linux
server reports what it runs, and you can start, stop and restart those
workloads — and read a snapshot of their logs without opening a console.
A **workload** is one Docker container or one systemd service. Each Linux server
reports what it is running, and you can start, stop and restart those workloads,
and read their recent logs, without opening a console.
Available on every instance. No licence feature is required.
## What gets reported
Linux servers only. Agents report every 60 seconds, and an unchanged list costs
a single small message rather than the whole thing again.
Linux servers only, reported every 60 seconds.
- **Containers** every container, running or not, with its image, published
- **Containers**: every container, running or not, with its image, published
ports, health, restart count and the compose stack it belongs to.
- **Services** systemd units that are running or failed, plus units that are
enabled but currently stopped. The platform's own units (`systemd-*`,
`user@*`, `session-*`) are filtered out; a typical host has 300 of them and
they bury the ten you care about.
- **Services**: systemd units that are running, failed, or enabled but stopped.
The operating system's own units are hidden, since a typical host has hundreds
of them and they bury the ones you care about.
Windows servers report no workloads at all.
## Docker not in use is not an error
Three different things look identical if you are careless, and only one of them
is a problem:
Three states look similar, and only one of them is a problem:
| What you see | What it means |
| ------------ | ------------- |
@@ -37,54 +34,47 @@ is a problem:
## Stacks are grouped
Compose stacks appear first, grouped under the stack name, then loose
containers, then services. A stack is one thing even when it is six containers,
and a flat list turns one decision into six rows.
Compose stacks appear first, grouped under the stack name, then individual
containers, then services.
The stack name comes from Docker's own `com.docker.compose.project` label. No
compose file is read from disk — a file on disk may not be what is running.
The stack name comes from Docker itself, so it reflects what is actually
running.
## Controlling a workload
Start, stop and restart are **owner or admin only**, and every action is
written to the audit log naming you, the server and the target.
The agent refuses to act on itself. `vantage-agent.service` is shown with its
buttons disabled: a server that stops its own agent goes offline, and the only
way back is SSH or physical access — which is exactly what this page exists to
avoid needing.
The Vantage agent will not act on itself, and its buttons are disabled. A server
that stopped its own agent would go offline, and getting it back would need SSH
or physical access.
A stop that never finishes is not reported as success. Both `docker stop` and
`systemctl stop` run under a 90-second limit, and a timeout comes back as a
real error.
A stop that never finishes is not reported as a success. Vantage waits up to 90
seconds and then reports the failure.
## Reading logs
Logs are **owner or admin only** and every read is audited. Unlike workflow
logs, a container's output cannot be masked: it is arbitrary, and a startup
banner or a stack trace may contain credentials nobody declared.
Reading logs is **owner or admin only**, and every read is audited. A
container's output cannot be filtered the way a workflow's can, and a startup
banner or stack trace may contain credentials nobody expected.
A log read returns a snapshot of at most **500 lines or 256KB**, whichever
limit is reached first, with the most recent output kept. When either limit
binds, the dialog says so — a truncated log must never be read as a complete
one.
You get the most recent output, up to **500 lines or 256KB**, whichever comes
first. If it was cut short, the dialog says so.
There is no live following. The [browser console](./browser-console.md) already
gives you a real terminal on the same server, where `docker logs -f` works
properly with its own scrollback.
There is no live tail here. For that, use the
[browser console](./browser-console.md), which gives you a real terminal on the
same server.
## Refreshing
Opening a server's Workloads panel asks its agent to report immediately, so
what is on screen is current rather than up to a minute old. That matters
because the panel has a Restart button on it: a stale row is not just a wrong
impression, it is a wrong action aimed at something that already died.
Opening a server's Workloads panel asks its agent to report straight away, so
what you see is current rather than up to a minute old. That matters when the
next thing you click is Restart.
If the agent is offline the refresh fails visibly rather than queueing. A
command whose target cannot be reached must say so.
If the agent is offline, the refresh reports a failure rather than waiting.
## Fleet view
**Workloads** in the sidebar searches the whole fleet by image, stack or state
— "which of these servers is still on the old image" — and links each result
back to its server.
**Workloads** in the sidebar searches your whole fleet by image, stack or state,
which is how you answer questions like "which servers are still on the old
image". Each result links back to its server.
+1 -1
View File
@@ -14,8 +14,8 @@ const sidebars: SidebarsConfig = {
"getting-started/cloud-vs-self-hosted",
"getting-started/self-hosted-install",
"getting-started/first-login",
"getting-started/first-server",
"getting-started/claim-free-licence",
"getting-started/first-server",
],
},
{