- ranges 7d (30 min step) and 30d (2 h step); Prometheus keeps 31d
- rate() window is at least the step, so long views show averages
instead of a 2-minute sample; "now" values still use a 2m window
- overview returns used_down/used_up: increase() of the interface
counters over the whole range
- tiles merged: traffic (with volume for the period), devices, load
internal/cli reimplements cli/src: start/stop/restart/status with the
awg-ctrl health gate, credentials, join/unjoin, peers [drop], monitor
install/status/remove and the interactive menu. Same cli.env, pid files,
logs and messages. Without arguments or with an operator command the binary
acts as the CLI; ctrl/ui/agent stay service roles.
- services are started as the same binary (`awg-control ctrl|ui|agent`),
detached into their own session, from the same working directories
- processes started by the Node CLI (tsx … index.ts / server.ts) are still
recognised, so `stop` and `status` work across the switch
- after join the fresh restart gets the original environment, so a rejoin
picks up the new cli.env instead of the inherited old CORE_* values
- install root: AWG_ROOT, else the nearest parent of the binary with cli/ or
.env, else /opt/awg-control
Checked end to end on stubbed awg: start/status/stop, credentials, join to
the panel's own hub (with and without --no-panel), peers listing and drop,
unjoin, menu.
internal/ui reimplements the awg-ui backend with the same HTTP surface; the
React frontend is unchanged and served from awg-ui/dist.
- web.go: Express-like router (express.json semantics, mounts, :params,
HTML 400/404/413 pages, SPA fallback)
- server.go: JWT login with rate limit, API keys and their server sets,
awg-ctrl client and proxy, /api/v1/users, delete hooks
- nodes.go: WSS hub for agents (pin, challenge-response, join strings),
RPC with timeouts, health, /ui/nodes
- sub.go, audit.go, sub_admin.go: sen:// master keys and devices,
per-server reconcile with tombstones and /api/peers/sync, signed /sub/v1
listener, audit with two-pass confirmation and cleaning, panel and
/api/v1/masterkeys routers
- metrics.go, monitor.go: /metrics exposition with tokens, /ui/monitor
- sen.go: link codec and protocol signatures, tested byte for byte against
fixtures produced by awg-ui/sen.ts (tools/golden/sen.ts)
Differential bench (tools/diffbench/local-ui.sh, scenario-ui.ts,
compare-ui.mjs): 158 steps over every route group, including signed device
requests and a Go agent joining as a node, plus the resulting ui.db and
awg-ctrl state. TS and Go are identical after masking random values, across
repeated runs and with the race detector on.
The differential bench (GEN 2 and 3.1, with I1, S4=0, upgrade with a 3.x
module) now shows the Go awg-ctrl identical to the TS one. The only real
difference it found: host.cpu was a Go map, so its keys came out sorted
instead of in /proc/stat order.
- tools/diffbench/compare.mjs: decodes vpn:// (Node and Go zlib differ), masks
live host numbers, Express dev-mode stack traces, temp file names and the
order of calls from concurrent background work
- tools/diffbench/local.sh: the same run without Docker, stubs only on the
run's own PATH (for disposable machines)
- run.sh: wait for the background shaping apply before stopping the service,
otherwise the stub log is cut short
- internal/agent: outbound WSS to core with SPKI pin checked inside the TLS
handshake, Ed25519 challenge-response, one-shot join secret (dropped from
memory and cli.env after the first ready), forwarding of core requests to the
local awg-ctrl with an internal token, dead-connection timer on pings,
backoff up to a minute (a minute after 4003)
- path filter is stricter than the Node one where it relied on WHATWG URL
normalisation: backslashes, a leading "//" and %2e dot segments are rejected
- tests: end-to-end against a fake TLS core and a fake awg-ctrl; also checked
by hand against the real awg-ui/nodes.ts hub
- CI: Go build, vet and race tests on GitHub and Gitea
- internal/jsjson: JSON.stringify-compatible encoder and order-preserving decoder,
tested against fixtures produced by Node itself
- internal/ctrl: awg-ctrl in Go (params, vpn:// keys, users, peers, sync, 3.1
upgrade, CAKE shaping, metrics, monitor job, Express-compatible HTTP layer)
- internal/iauth, internal/logx: internal Ed25519 tokens and logging
- tools/diffbench: Docker bench with stubbed awg/ip/tc/nft that runs one scenario
against the TS and Go implementations for comparison