Baseline TypeScript version before the Go rewrite: CAKE fair sharing with
automatic deprioritisation of heavy devices, per-key priority, /metrics with
a metrics token, the built-in Prometheus install/remove, the Monitor tab,
grouped sidebar tabs and the dead-peer audit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- awg-agent sends its install's VERSION (from the CLI's env, else the
project's .env) in the auth frame; the hub keeps it in nodes.version
(added by migration), so an offline node still shows the last one.
- GET /ui/nodes returns version per server (0 is the panel's own) and the
panel's version at the top.
- ServerBar: a vX.Y.Z chip next to the AWG generation. A node older or
newer than the panel, or one that sends no version (agent before
0.3.4, shown as v?.?.?), gets a warm chip with an "update it" tooltip.
- App.css: tertiary-container tokens from design.md (light) and a warm
dark pair; they did not exist yet.
- Mock server: panel 0.3.3, Frankfurt behind on 0.3.2, Amsterdam with no
version.
API keys now see a set of servers instead of one, and /api/v1 gains the
master-key operations the panel already had.
- api_key_servers holds each key's servers (migrated from server_id,
which stays the default server for /api/v1/users). /users follows
X-Server-Id within the set; a server outside it is 404. PATCH
/ui/apikeys/:id changes the set without reissuing the key.
- /api/v1/masterkeys: create (returns the sen:// link), list, get with
link and devices, patch label/limit/servers, rotate, rekey, delete, and
nested device routes (unlink, rekey, PSK). A key sees a master key only
if all of its servers are in the key's set, so it cannot delete or
re-point service on a server it cannot see; anything else is 404,
servers outside the set are 403. Creating refuses with 503 before
writing anything if the subscription cannot issue links.
- sub.ts: the panel routes and the API share one service layer, the
panel's behaviour is unchanged.
- Node deletion: owners check in beforeDelete (no writes, a later hook
may still veto) and clean up in afterDelete. Previously a forced delete
could release a waiting master key and then be vetoed. A node that is
the only server of an API key is refused; other keys just lose it.
- Panel: server checkboxes are a shared ServerPicker, used by the master
key settings and the new API key settings row; the API keys table shows
each key's servers.
- README: the API section covers server sets and master keys.
A node is a VPN server without a panel. awg-agent on it dials the panel
(core) over an outbound WSS connection and runs the panel's requests on its
own awg-ctrl, which stays on 127.0.0.1 and is unchanged. No inbound port on
the node, works behind NAT.
- awg-ui/nodes.ts: node registry in ui.db, WSS hub on NODE_PORT with a
self-signed cert whose SPKI pin goes into the join string, RPC with
timeouts, health polling, /ui/nodes routes.
- Nodes enroll with a one-time awgjoin:// string (secret stored hashed,
1 hour) and then authenticate by their own Ed25519 key. The agent checks
the pin before sending anything and only forwards /health, /api/*, /awg/*.
- server_id 0 is the panel's own awg-ctrl; LOCAL_NODE=off makes the panel
run without a VPN of its own. The panel picks a server with X-Server-Id,
API keys use their server_id. sen:// still serves one server.
- UI: a card per server in the server bar, switching remounts the tab,
«Добавить сервер» gives the join command.
- CLI: agent service, roles from cli.env, join / unjoin commands.
- Release archives include awg-agent.
A master key is a sen:// link with a device limit. Each device
generates its own WireGuard keypair and registers only the public key,
then polls a signed /sub/v1/config, so params and generation changes
reach users without re-importing a key.
- awg-ctrl: peers table and /api/peers*, /api/profile; nextIp and
rebuildConf take peers into account (output unchanged without them)
- awg-ui: link codec and signatures (sen.ts), registry, panel routes
and the public listener on SUB_PORT (sub.ts)
- responses are always Ed25519-signed; TLS with a pinned self-signed
certificate is optional
- devices report a client version, shown as platform-version
- "Мастер-ключи" tab: a table with the devices nested under each key,
a device-limit meter, per-key settings and a placeholder for the
servers a key serves; cards on mobile
- install.sh/CLI: subscription signing key, optional certificate, port;
kept across reinstalls with KEEP_DATA
- add the missing mock-server.ts used by npm run dev:mock
Upstream AmneziaWG 3.x is still shifting under active development
(unanswered kernel-module issues around header protection and
handshake-without-data regressions), so give operators an explicit
opt-out/opt-in instead of always auto-selecting 3.1, and pin the
installed package so unattended-upgrades can't swap the module under
a live server. Surface the running module/tools version and protocol
generation through /health, the CLI status command, and the panel's
server card so a mismatch is visible instead of silent.
Protocol generation is now derived from awg1.conf rather than stored: an
interface speaks 3.1 when HeaderProtectionKey is set and 2.0 otherwise. The
3.0/3.1-only keys (HeaderProtectionKey, ContentPaddingAddition, the
Rekey/Reject/Keepalive timers, MaxHandshakeAttempts, RandomTrailers,
DisableCookies) are read from the conf and emitted only when non-empty, so 2.0
output stays byte-identical — verified by diffing both the client .conf and the
full vpn:// string against the previous implementation.
Changing generation invalidates every issued vpn:// key, so add
POST /api/users/reissue. It recovers the client private key from inside the
stored blob (the only place it exists), keeping ip/pub_key/psk_key intact, so
nothing changes on the wire and users only need to re-import. users gains
key_gen and vpn_key_prev, and users.db is snapshotted before the pass.
install.sh gates 3.1 on module 3.x and kernel >= 5.5 (header protection needs
the chacha library API, absent before 5.5) and falls back to writing a 2.0 conf
instead of aborting, so older kernels keep working as before.
HeaderProtectionKey is generated with awg genpsk and preserved on KEEP_DATA.
The panel marks keys issued on a different generation and offers to reissue
them; /api/v1 now returns gen alongside name/ip/vpn_key.
Also add MIT license headers across the awg-ui sources.