refactor: move the agent and its installer to vantage-agent

agent/ becomes the root of gitea.hostxtra.co.uk/vantage/vantage-agent,
with installer/ alongside it, and agent-release.yml goes with them.

Releases now come from that repository, so the six places this server
generates or reads a release URL are repointed: both install scripts,
both update scripts, and the latest-version lookup in dispatch.go. The
agent/v* tag prefix is unchanged — those scripts grep for it.

Agents built before this move have the old mrhid6/vantage path compiled
into their self-update and will 404 on the push-button update. The
remedy is the /update one-liner, which this server generates and which
therefore has to ship first.
This commit is contained in:
2026-09-08 09:00:22 +00:00
parent 1c6d9e8495
commit 44d9036440
51 changed files with 62 additions and 5129 deletions
+51 -44
View File
@@ -39,18 +39,6 @@ Multi-tenancy: every domain document carries `org_id`, and every service query i
```
vantage/
├── agent/
│ ├── cmd/main.go # flags: -generate-key
│ └── internal/
│ ├── checker/ # monitor check execution
│ ├── config/ # config.yaml load/save
│ ├── exec/ # workflow step execution
│ ├── grpc/ # client + generated pb
│ ├── inventory/ # CPU/mem/disk collection (linux/other)
│ ├── keys/ # authorized_keys read/diff/write
│ ├── monitors/ # agent-run monitor loop
│ ├── sync/ # poll loop + command stream
│ └── updates/ # OS package update check/apply
├── server/
│ ├── cmd/main.go
│ └── internal/
@@ -69,11 +57,11 @@ vantage/
│ ├── components/ # ui/, workflows/, monitors/, Sidebar
│ └── lib/ # api client, guac console, query client
├── installer/ # Windows: setup.ps1, nssm.exe, WiX .wxs
├── deploy/ # docker-compose.yml, agent.service
└── .gitea/workflows/ # agent-release.yml, server-deploy.yml
├── deploy/ # docker-compose.yml, Helm chart
└── .gitea/workflows/ # server-deploy.yml, chart-release.yml, vantagectl-release.yml
```
**Three repositories carry parts of Vantage that this one does not.**
**Four repositories carry parts of Vantage that this one does not.**
| Repository | What it holds |
| ---------------- | ------------------------------------------------------------------------------------------------- |
@@ -81,6 +69,7 @@ vantage/
| `vantage-admin` | Vantage HQ: the licensing authority (`server/`, was `admin/`) and its console (`web/`, was `adminsite/`) |
| `vantage-site` | the marketing site (`web/`, was `site/`) and its contact-form service (`server/`, was `sitesvc/`) |
| `vantage-docs` | the user documentation, at the repository root (was `docsite/`) |
| `vantage-agent` | the agent, at the repository root (was `agent/`), and the Windows `installer/` |
**None of the three is a build dependency of anything here**, and nothing here
is a dependency of them. `vantage-site` and `vantage-docs` are wholly
@@ -101,9 +90,9 @@ email system: transport plus templates), `license/` (payload, sign, verify,
trusted keys, plans), `models/` (Instance, User, Settings), `provision/`,
`backup/`, `cryptobox/`, `indexes/`, `grpc/pb` + `grpc/codec`, and
`cmd/lkctl/`, and `proto/vantage/v1/vantage.proto`, which documents `grpc/pb`
and moved there to sit beside it. Three modules here depend on it — `server`, `agent` and
and moved there to sit beside it. Two modules here depend on it — `server` and
`vantagectl` — each pinning a version in its own `go.mod`, as do
`vantage-admin` and `vantage-site`. It was a
`vantage-admin`, `vantage-site` and `vantage-agent`. It was a
directory in this repository until it was extracted with its history; the
`replace ../shared` directives and the `./shared` entry in `go.work` are gone
with it.
@@ -403,7 +392,7 @@ one wire shape, worded per platform in the UI, which is the only layer that
knows the host's OS. The platform split lives entirely in the agent, as build
tags (`systemd_linux.go` / `services_windows.go` and the matching `control_`
and `logs_` pairs); the control plane is OS-blind and needed no changes.
Windows collection runs PowerShell through `agent/internal/winexec`. Every
Windows collection runs PowerShell through the agent's `internal/winexec`. Every
script that reports data emits JSON that a build-tag-free parser reads, so
those parsers are tested on Linux — the agent module has no Windows CI. The
control verbs and `serviceDisplayName` emit no JSON and have no parser; they
@@ -459,9 +448,9 @@ than an empty list.
Logs are capped at **500 lines and 256KB, whichever binds first** — a line count
alone does not bound size, and 500 lines of 4KB JSON is 2MB across the bus. The
cap is mirrored in `services.MaxWorkloadLogLines` because `agent/` is a separate
module with an `internal/` tree and the constant cannot be shared; change one,
change the other. There is **no follow mode**: the browser console already gives
cap is mirrored in `services.MaxWorkloadLogLines` because the agent is a
separate module — a separate repository now — with an `internal/` tree, and the
constant cannot be shared; change one, change the other. There is **no follow mode**: the browser console already gives
a real terminal where `docker logs -f` works properly. Log reads and control
actions are **owner|admin and audited**, unlike the read-only snapshot — a
container's stdout is arbitrary and cannot be masked the way a workflow's can.
@@ -478,7 +467,7 @@ because that co-location is the only thing making "add the message to both in
the same commit" possible.
It is **one** `pb` package serving both sides. There used to be two
(`agent/internal/grpc/pb` and `server/internal/grpc/pb`) and they had already
(one in the agent, one in the server) and they had already
drifted: the agent's `UnimplementedVantageServer` was three methods stale and
carried no `ReportWorkloads` at all. The agent links the server half as dead
code, which the linker drops.
@@ -597,7 +586,25 @@ inserted in front. The same setting also decides the address recorded in
### Agent self-update
`UpdateAgentCmd` carries a target version and Gitea base URL; the agent downloads and replaces itself.
`UpdateAgentCmd` carries a target version and Gitea base URL; the agent
downloads and replaces itself, from
`<gitea>/vantage/vantage-agent/releases/download/<tag>/…`.
**That repository path is compiled into the agent, not sent to it**, and it
changed when the agent moved out of this repository. Agents built before that
move look for `mrhid6/vantage`, where releases are no longer published, so the
push-button update in the UI fails for them with a 404. They are not stranded:
`/install`, `/install.ps1`, `/update` and `/update.ps1` are generated **here**,
at request time, so re-running the update one-liner on a host moves it onto a
build that knows the new address, after which self-update works again.
The ordering matters. This server must be deployed with the new paths *before*
the one-liner is any use, because it is this server that hands out the URL.
The six generators — two install scripts, two update scripts,
`GET /api/agent/latest-version` in `services/dispatch.go`, and the tag lookup
inside each — all name that repository. They must agree with wherever
`agent-release.yml` actually publishes, and nothing checks that they do.
### Backup and restore
@@ -1088,9 +1095,13 @@ Index builders (`EnsureAuthIndexes`, `EnsureSettingsIndexes`) are fatal on failu
## Agent Lifecycle
The agent is `vantage-agent` now; its internals are documented there. What the
control plane depends on:
### Config file
Linux `/etc/vantage/config.yaml`, Windows `%ProgramData%\vantage\config.yaml`. Directory `0700`, file `0600`.
Linux `/etc/vantage/config.yaml`, Windows `%ProgramData%\vantage\config.yaml`.
Directory `0700`, file `0600`.
```yaml
server_url: "vantage.yourdomain.com:9090"
@@ -1121,8 +1132,10 @@ tls: true
### Install
Linux: systemd unit at `/etc/systemd/system/vantage-agent.service`, `Restart=always`, runs as root.
Windows: MSI built by CI (WiX), or `installer/setup.ps1` registering the agent as a service via NSSM.
Linux: systemd unit at `/etc/systemd/system/vantage-agent.service`,
`Restart=always`, runs as root — written by the install script this server
generates, not shipped as a file. Windows: MSI built by `vantage-agent`'s CI
(WiX), or its `installer/setup.ps1` registering the agent as a service via NSSM.
---
@@ -1314,16 +1327,6 @@ lives — one copy instead of the three that existed while they were apart.
## CI/CD — Gitea Actions
### `agent-release.yml` — triggered by `agent/v*` tags
Builds `linux/amd64`, `linux/arm64`, `windows/amd64`, writes `checksums.txt`, creates a Gitea release. A second `msi` job on `windows-2022` packages the WiX installer.
```bash
GOOS=linux GOARCH=amd64 go build \
-ldflags="-s -w -X main.Version=${VERSION}" \
-o dist/vantage-agent-linux-amd64 ./cmd
```
### `server-deploy.yml` — triggered on every push to `main`
Builds and pushes **two** images to the Gitea container registry: `server` and `web`. Everything else that used to be built here now belongs to the repository that owns it — `vantage-site`, `vantage-docs` and `vantage-admin` each publish their own. **`vantagectl` is also not among them** — it is a released tool rather than a running service, and its image is version-tagged by `vantagectl-release.yml`.
@@ -1355,10 +1358,11 @@ you a pin is stale.
Every Go build in these workflows writes a netrc from `REGISTRY_USER` +
`RELEASE_TOKEN` before it runs, and sets `GOPRIVATE=gitea.hostxtra.co.uk/*`.
There are **five** such places, and each needs its own because jobs do not share
a filesystem: `server-deploy.yml`'s single job, both jobs of
`agent-release.yml` (the `msi` job is Windows, where Go reads `%USERPROFILE%\_netrc`,
not `.netrc`) and both jobs of `vantagectl-release.yml`. The docker builds pass
There are **three** such places here, and each needs its own because jobs do not
share a filesystem: `server-deploy.yml`'s single job and both jobs of
`vantagectl-release.yml`. The other repositories each carry their own —
`vantage-agent`'s `msi` job is the one to remember, because it is Windows, where
Go reads `%USERPROFILE%\_netrc` and not `.netrc`. The docker builds pass
it on as `--secret id=netrc`, never a build arg. **`RELEASE_TOKEN` needs read
access to the `vantage` org** on top of its existing scopes; without it every Go
build fails at `go mod download` with a 404 on the module, which reads like a
@@ -1367,7 +1371,7 @@ a build arg is baked into the image. So does anything that leaves no
trustworthy base commit: a manual `workflow_dispatch`, a new branch, or a
force-push whose old head is gone.
The gap this leaves: **changing a repo variable pushes no commit, so nothing rebuilds.** After editing `ADMIN_API_URL`, `HQ_URL` or `ADMIN_ENV`, run the workflow manually — that is what `workflow_dispatch` is there for. Base images also stop being refreshed on a service nobody touches; a periodic manual run covers that.
The gap this leaves: **changing a repo variable pushes no commit, so nothing rebuilds.** After editing `HQ_URL`, run the workflow manually — that is what `workflow_dispatch` is there for. Base images also stop being refreshed on a service nobody touches; a periodic manual run covers that.
### `chart-release.yml` — validates on every chart change, publishes on `chart/v*` tags
@@ -1385,11 +1389,14 @@ helm install vantage vantage/vantage --version 0.1.0
### Tagging
```bash
git tag agent/v1.0.0 && git push origin agent/v1.0.0 # agent release
git tag chart/v0.1.0 && git push origin chart/v0.1.0 # helm chart package
git push origin main # server + web deploy
git tag chart/v0.1.0 && git push origin chart/v0.1.0 # helm chart package
git tag vantagectl/v0.1.0 && git push origin vantagectl/v0.1.0 # vantagectl release
git push origin main # server + web deploy
```
The agent is tagged in `vantage-agent`, still as `agent/v*` — that prefix is
what the control plane greps release tag names for, so it survived the move.
### Secrets / variables
| Name | Type | Value |