agent/ becomes the repository root, so the module is gitea.hostxtra.co.uk/vantage/vantage-agent, and installer/ comes with it. Releases move to this repository, and internal/sync's self-update URLs move with them. Agents built before this point have the old mrhid6/vantage path compiled in and will fail the push-button update; re-running the server-generated /update one-liner on each host moves them onto a build that knows the new address.
7.3 KiB
Vantage agent (vantage-agent)
The lightweight Go agent installed on every managed server, and the Windows
installer that packages it. Extracted from the vantage monorepo with its
history; the agent is the repository root, so the module is
gitea.hostxtra.co.uk/vantage/vantage-agent.
vantage-agent/
├── cmd/main.go # flags: -generate-key
├── internal/
│ ├── checker/ # monitor check execution
│ ├── config/ # config.yaml load/save
│ ├── exec/ # workflow step execution
│ ├── grpc/ # client + the codec registration
│ ├── inventory/ # CPU/mem/disk collection (linux/other/windows)
│ ├── keys/ # authorized_keys read/diff/write
│ ├── monitors/ # agent-run monitor loop
│ ├── proxy/ # console relay, always from 127.0.0.1
│ ├── sync/ # poll loop + command stream + self-update
│ ├── updates/ # OS package update check/apply
│ ├── winexec/ # PowerShell invocation on Windows
│ └── workloads/ # containers and units/services
├── installer/ # Windows: setup.ps1, nssm.exe, WiX .wxs
└── .gitea/workflows/agent-release.yml
Relationship to the other repositories
| Repository | Relationship |
|---|---|
vantage-shared |
a private Go module. grpc/pb and grpc/codec are the wire contract; proto/ there documents them |
vantage |
the control plane. No import in either direction — the coupling is the gRPC wire, and it is entirely mediated by vantage-shared |
vantage-shared is private, so every Go build needs
GOPRIVATE=gitea.hostxtra.co.uk/* plus a credential. CI writes a netrc from
REGISTRY_USER + RELEASE_TOKEN (that token needs read access to the
vantage org) — twice, because the msi job is Windows and Go looks for
_netrc in the profile directory there, not .netrc. Locally, either a netrc
or git config --global url."git@gitea.hostxtra.co.uk:".insteadOf https://gitea.hostxtra.co.uk/.
A wire change is three steps, in order
shared/grpc/pb is hand-written and shared by both sides, so a new message is a
compile error rather than a silent disagreement — but only once each side moves:
- release
vantage-shared(and changeproto/vantage/v1/vantage.protoin the same commit as the Go types) - bump the pin in
vantage'sserver/go.mod— live at the next push to main - bump the pin here — live only at the next
agent/v*tag
The control plane runs ahead of the fleet in between. That was true before the
split too; it is now explicit in two go.mod files rather than implicit in a
shared directory.
Releases
Tags are agent/v*, not bare v*, even though this repository is only the
agent. The tag is not a private detail: it is what the fleet downloads by, what
UpdateAgentCmd carries, and what the control plane greps release tag names for
when answering GET /api/agent/latest-version. Renaming it would be a second
breaking change stacked on the repository move.
git tag agent/v1.2.0 && git push origin agent/v1.2.0
Builds linux/amd64, linux/arm64 and windows/amd64, writes checksums.txt,
creates the Gitea release. A second msi job on windows-2022 builds the exe
again, packages it with WiX and appends the MSI to the same release through
the API — gitea-release-action cannot find a tag with a slash in it.
The self-update path, and what the move broke
internal/sync downloads its own replacement from
<gitea>/vantage/vantage-agent/releases/download/<tag>/…, verifying the
SHA-256 from checksums.txt before swapping itself. That path is compiled
into the binary.
Every agent built before this move has the old path — mrhid6/vantage —
baked in, and releases are no longer published there. For those agents the
push-button update in the UI will fail: the download 404s. They are not
stranded, because /update and /update.ps1 are generated by the control plane
at request time and point wherever the current server says, so re-running the
update one-liner on a host moves it onto a build that knows the new address.
After that, self-update works again permanently.
This was a deliberate choice — the alternative was publishing releases to a repository that no longer holds the source — but it means the fleet needs one pass of the update one-liner, and the control plane must be redeployed with the new release paths first, or the one-liner points at the old repository too.
What the agent will not do
- It never reboots a host.
ApplyUpdatesCmdinstalls and stops there;inventory.reboot_requiredreports that one is owed. - It decides what it will not touch. The protected workload set is computed
and enforced agent-side —
vantage-agent.service,VantageAgenton Windows, and its own container ID from/proc/self/cgroup. The control plane may name a target; the agent decides what it will do to itself. A server-side denylist alone would be bypassed by the next dispatch path someone adds, and the failure is unrecoverable from the UI. - The console relay dials
127.0.0.1only. The host is hardcoded here, so the control plane can name a port and nothing else. - No
authorized_keysmanagement on Windows, and no package inventory: a Windows agent never callsReportPackages, so noserver_packagesdocument exists for it at all — a different, earlier state than theunsupporteda Linux distribution reaches when its family has no security feed.
Platform split
Windows support is build tags, not runtime branches — systemd_linux.go /
services_windows.go and the matching control_ and logs_ pairs. Windows
collection runs PowerShell through internal/winexec, and every script that
reports data emits JSON that a build-tag-free parser reads, so those parsers
are tested on Linux. This module has no Windows CI: the control verbs and
serviceDisplayName emit no JSON, have no parser, and are exercised only by
running the agent on Windows.
Windows updates go through the Windows Update COM API
(Microsoft.Update.Session) rather than the PSWindowsUpdate module, which would
need a PowerShell Gallery install on every host and fails on an air-gapped
fleet. CurrentVersion is empty on Windows and NewVersion carries the KB
article ID: a Windows update is not a version bump of a named package.
Two constants that mirror the control plane
Neither can be shared — this is a separate module and the control plane's are
under internal/ — so both must change in step, by hand:
- the workload log cap, 500 lines and 256KB whichever binds first, mirrored
in the control plane's
services.MaxWorkloadLogLines streamHealthyAfterand the 70s command-stream watchdog, which pair with the control plane's 20sPingCmd. The watchdog arms only after a first ping has been seen, so an older server that sends none is treated as working rather than put into a reconnect loop.