Commit Graph
42 Commits
Author SHA1 Message Date
maeneko f0f2f9848a Give master keys a stable uuid
- master_keys.uuid (UUID v4, unique index): new keys get one on create,
  existing keys once on start. It survives rotate (new secret) and
  relabel, so it identifies the key itself rather than its link.
- /ui/masterkeys* and /api/v1/masterkeys* return uuid, and :id in those
  paths accepts the numeric id or the uuid.
- /sub/v1 register, peek and devices return key_uuid, so a client can
  recognise the same key behind a reissued link. It is not in the sen://
  link (that would need a new format) and not in config, so rev and
  existing clients are unaffected - checked against a 0.3.1 ui.db.
- Panel: the uuid sits in the key's row like a device ID (full value in
  the tooltip, click copies) and in the key settings with a copy button;
  on mobile it is shown under the key's name.
- README: uuid in the master key API.
2026-09-27 18:04:05 +03:00
maeneko 5c3bf21918 Show each server's Forgetting version in the panel
- 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.
2026-09-27 18:03:57 +03:00
maeneko eb0ec2878c Bump to Beta 0.3.3
ci / check (push) Successful in 7m12s
release / release (push) Successful in 3m34s
All seven places the version is written down: .env, the copy in
install.sh, the four package.json files and the /ui/brand example in the
README.

Deleted devices get a signed 410 revoked with an echo of the request, so
a client can tell a revoked key from a network failure; master-key peers
no longer outlive their devices; API keys can manage master keys.
v0.3.3
2026-09-27 16:23:02 +03:00
maeneko a85f5d87b2 Manage master keys through the external API
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.
2026-09-27 15:59:16 +03:00
maeneko c5e1dc9b32 Stop master-key peers from outliving their devices
A deleted master key could keep letting its devices in: ui.db learns what
is on a server only from awg-ctrl's reply, and a node that executed a
request whose 504/502 reply got lost, or a device deleted while a
reconcile pass was in flight, left a peer nothing remembered to remove.

- awg-ctrl: PUT /api/peers/sync takes a panel's full list of wanted keys
  and removes every other peer of that panel's area. Removal is driven
  only by such an explicit list, never by time or inactivity: a panel
  that is offline sends nothing, so nothing is deleted. Registered before
  PUT /api/peers/:pub, with its own 512 kB body limit.
- Every panel has a stable id (ui.db, mirrored to awg-ui/panel.id) and
  adds peers as "<panel>/m<key>/d<device>", so two panels on one awg-ctrl
  never touch each other's peers. Legacy "m1/d2" peers are claimed only
  when their key is in the panel's list.
- Each reconcile pass ends with the sync, built from ui.db at that moment.
  Its reply also self-heals: a device whose key is missing on a server
  (a rekey rolled back after a lost reply) is added again.
- Deleting a master key stops it at once but keeps it listed as
  "Удаляется — ждёт: …" until every server confirms, instead of saying
  "deleted" while an offline node still lets the devices in.
- Deleting a node reconciles it first; if it is offline or has not
  confirmed a deleted key, it takes ?force=1 and a warning. Tombstones
  are no longer dropped silently.
- /sub/v1: an unknown device gets a signed 410 revoked that does not count
  towards the failure limit (dead clients behind one node NAT used to
  lock everyone out), every response echoes the request's ts and device,
  and 401 reasons and the first 429 per window are logged. config and rev
  are unchanged, byte-identical on one server.
- CLI: `awg-ctrl peers [drop <area|legacy>]` lists and removes
  subscription peers by area; `join --no-panel` asks whether to keep or
  drop the panel's own peers (NODE_PEERS=keep|drop).
2026-09-27 15:30:53 +03:00
maeneko 3eede2f015 Bump to Beta 0.3.2
release / release (push) Successful in 3m35s
All seven places the version is written down: .env, the copy in
install.sh, the four package.json files and the /ui/brand example in the
README.

A master key can serve its devices from several servers; a server that
joins a core as a node keeps its own panel.
v0.3.2
2026-09-26 21:44:25 +03:00
maeneko 2054122110 Serve a master key's devices from several servers
A master key now has a set of servers (the panel's own and/or nodes). A
device's single WireGuard key becomes a peer on every server of its key,
each with its own IP and PSK, and config.servers[] lists them all.

- ui.db: master_servers (the key's set, never empty), device_peers (which
  key is on which server, with the ip/psk that server issued),
  peer_tombstones (peers to delete) and server_profiles (last known
  profile of each server).
- Nothing is pushed blindly: reconcile() brings one server to the desired
  state - deletes tombstones, adds peers, rekeys with PUT, fills ip/psk,
  rotates a pending PSK - one pass per server at a time. It runs right
  after every action, when a node connects and once a minute, so a node
  that was offline catches up on rekeys, PSK changes and removals.
- config is built from ui.db, so an offline node stays in it and rev does
  not flap; a server is listed once its peer carries the device's current
  key. An empty config is never sent (502 instead); register and rekey
  roll back if no server accepted the key.
- Existing keys and devices migrate on start. With one server the config
  and its rev are byte-identical to 0.3.1 (checked by running both
  versions on the same ui.db), so existing clients notice nothing.
- Panel: server checkboxes in the key settings instead of the placeholder
  (the last one cannot be removed), "на N из M серверов" on devices that
  have not reached every server yet, device stats summed over servers.
- SUB_HOST: the panel's public address for sen:// links and endpoints;
  install.sh writes it. Without it a panel with no VPN of its own used
  the address it was opened at.
- A node referenced by a key's server set cannot be deleted.
2026-09-26 21:43:58 +03:00
maeneko 129f29d6be Keep the node's own panel when it joins a core
A server that already works as panel + VPN can now also be a node of
another panel without losing its own: `awg-ctrl join` keeps awg-ui running
next to the agent and writes NODE_PANEL=on. `--no-panel` stops it as
before. Both panels talk to the same awg-ctrl, which stays the source of
truth, so they do not get in each other's way.

The CLI runs awg-ctrl, awg-ui and awg-agent on such a node, all behind the
awg-ctrl health gate; a node installed by install.sh has no UI_PASS and
still runs only awg-ctrl + agent. unjoin also drops NODE_PANEL.
2026-09-26 21:43:45 +03:00
maeneko 2f2f0e268a Bump to Beta 0.3.1
release / release (push) Successful in 3m18s
Covers all seven places the version is written down: .env, the copy
install.sh keeps because it prints its banner and builds the release URL
before the archive with .env exists, the four package.json files (awg-agent
is new), and the /ui/brand example in the README.

Multiple servers under one panel (core + nodes), the installer's role
question and its full log.
v0.3.1
2026-09-26 20:19:50 +03:00
maeneko 2e773deba2 Fix the installer's generated password, keep going if the log dies
Found by running install.sh end to end in a sandbox for all three roles.

- The suggested UI password and JWT_SECRET came from
  `tr < /dev/urandom | head -c N || openssl rand`. Under pipefail tr's
  SIGPIPE made the pipeline fail every time, so openssl ran too and the two
  outputs were glued together (a 24-character password). rand_alnum() reads
  a finite 1 KiB instead.
- The log filter runs sed with LC_ALL=C, so broken UTF-8 in someone else's
  output (make.log, dmesg) cannot stop it, and tee -p keeps writing to the
  screen if the log writer dies instead of taking the installer down with a
  silent SIGPIPE.
- Output: labels before awg-quick up and apt-mark hold, no double blank
  lines, "маршрут" only where there is a VPN, a hint when the agent log is
  still empty.
2026-09-26 20:19:44 +03:00
maeneko aa31b7b8a2 Ask for the server role in the installer, log everything
- install.sh asks the role first: panel + VPN (as before), panel only
  (skips AmneziaWG, the kernel module and the headers/virt checks) or node
  (no panel; takes the awgjoin:// string and checks the panel's cert pin
  with openssl before building anything). ROLE= / JOIN= skip the questions;
  an update keeps the previous role.
- The previous cli.env is read before a reinstall removes the project
  directory. Before this, keeping data silently lost SUB_PORT / SUB_TLS
  and broke every issued sen:// link.
- Output: a header, numbered steps, menus, a summary before installing and
  a final block per role. The full output of apt, DKMS, npm, awg-quick,
  ufw and systemctl is shown as a dimmed block under its step, including
  what used to go to /dev/null.
- The log starts with the first line: checks, questions with answers
  (password masked), all command output. It is written without colour
  codes, mode 600.
2026-09-26 19:48:34 +03:00
maeneko 789538756a Manage several servers from one panel: core + nodes
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.
2026-09-26 19:48:33 +03:00
maeneko 1278ad527d Add /sub/v1/devices and /sub/v1/peek, count the slots on register
release / release (push) Successful in 3m14s
- GET /sub/v1/devices: a device lists the devices of its own master key
  (name, platform, version, last seen, and which one is asking). Read-only:
  unbinding is still only DELETE /sub/v1/device for oneself.
- POST /sub/v1/peek: how many slots of a key are taken, asked with the
  link's secret before any device exists, so the client can show it while
  the link is only pasted. Wrong secrets count against the failure limit.
- register also answers with devices / device_limit, so the client needs no
  second request to show the slot it just took.
v0.3.0
2026-09-25 17:24:52 +03:00
maeneko 6e010a0141 Bump to Beta 0.3.0
Covers all six places the version is written down: .env, the copy install.sh
keeps because it prints its banner and builds the release URL before the
archive with .env exists, the three package.json files, and the /ui/brand
example in the README.

First release with the sen:// subscription and master keys.
2026-09-25 17:24:52 +03:00
maeneko 923bd87fe9 Add sen:// subscription with master keys
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
2026-09-25 17:24:52 +03:00
maeneko d5e00ddaf8 Bump to Beta 0.2.2
Covers all six places the version is written down: .env, the copy install.sh
keeps because it prints its banner and builds the release URL before the
archive with .env exists, the three package.json files, and the /ui/brand
example in the README.
2026-09-10 17:51:19 +03:00
maeneko 2b46d04af9 Support Debian in the installer
install.sh only knew Ubuntu. On Debian 13 the dependency step died on
software-properties-common, which trixie no longer ships, and on
linux-headers-generic, which Debian never had — so add-apt-repository and the
Ubuntu headers meta package are both off the table there.

The distro family now comes from /etc/os-release (derivatives via ID_LIKE) and
shows up as its own line in the compatibility checklist. On Debian the amnezia
PPA is wired by hand: the key is fetched from keyserver.ubuntu.com by its full
fingerprint and checked after download, then a signed-by source is written on
the Ubuntu series whose glibc is not newer than the Debian release's
(bullseye→focal, bookworm→jammy, trixie→noble; AMNEZIA_PPA_SUITE overrides).

Headers are installed as a separate step so a missing package gets precise
advice instead of a generic failure. Debian's archive keeps only the latest
kernel build, so a server that hasn't rebooted in a while has no headers for
its running kernel; the hint names linux-image/linux-headers for the kernel's
own flavour (cloud-amd64 and so on) plus a reboot.

iptables joins the dependencies — PostUp calls it, and a minimal Debian 13 on
nftables doesn't have it. IP forwarding moves to its own file in
/etc/sysctl.d: Debian 13 no longer reads /etc/sysctl.conf at boot, so the old
line would have held only until the first reboot.
2026-09-10 17:51:19 +03:00
maeneko a358504e2a Bump to Beta 0.2.1
release / release (push) Successful in 3m31s
ci / check (push) Successful in 6m8s
Covers all six places the version is written down: .env, the copy install.sh
keeps because it prints its banner and builds the release URL before the
archive with .env exists, the three package.json files, and the /ui/brand
example in the README.
v0.2.1
2026-09-10 00:24:18 +03:00
maeneko 66ae59e1d0 Add the admin panel scaffold
A self-contained template: an Express backend with login, JWT, rate limiting
and SPA serving, and a React 18 + Vite shell with the same tab registry and
warm Material 3 design system the panel uses. No domain logic — routes and tabs
are meant to be hung off it.

node_modules and the frontend build stay out through the existing ignores.
2026-09-10 00:24:18 +03:00
maeneko a11cfc88de Bring back the external API keys
Commit 0dbb13f removed the whole feature: the apikeys tab, the /ui/apikeys
routes, requireApiKey, the /api/v1 router, the ApiKey type, the better-sqlite3
dependency and UI_DB_FILE in the CLI and the installer. Only the untracked
awg-ui/API.md survived, so the README went on to claim there was no key-based
API at all.

Restores all of it: ui.db with the api_keys table, awgk_ key generation storing
only the SHA-256 and a display prefix, the JWT-guarded CRUD, and the /api/v1
router mounted before the JWT proxy on /api so the key auth is not swallowed.
The tab comes back as its own folder and the registry picks it up on its own.
UI_DB_FILE points at /etc/amnezia/amneziawg/ui.db, outside the install tree, so
reinstalling keeps the keys; a fresh install moves the old one aside with its
WAL sidecars.

better-sqlite3 takes the slot of jsonwebtoken, which arrived with the MA7
integration and has been unused since 068cab4 removed it.

One defect is not reproduced: the /api/v1 handlers called ctrl() without a
try/catch, and Express 4 does not catch async rejections, so a single request
with awg-ctrl down killed the panel process. They now answer 502 like proxy()
already did.

The API contract is documented in the README rather than linked: .gitignore
excludes every markdown but README.md, so awg-ui/API.md is not in the repo and
the link would break on a clone.

Checked against a running server: login, key create/list/delete, 400 on a bad
label, 401 without a JWT, "API key required" rather than "Unauthorized" on
/api/v1, "Invalid API key" for a deleted key, last_used updating, and all three
/api/v1 routes returning 502 with the process still alive.
2026-09-10 00:24:18 +03:00
maeneko 44c032b376 Document the panel API in the README
The public /api/v1 surface and its docs went away with the API keys, leaving
nothing written down about what the panel exposes — even though every route it
uses is reachable over HTTP with a login token.

Replaces the install-source section with an API chapter: the JWT flow and its
401/429 cases, the unauthenticated /ui/brand, health and interface status, the
user routes including why fetching a key is a POST, reissue, and the 2.0 to 3.1
upgrade with its 409 and 500 cases. What is left of the install-source notes is
one line pointing at the comments in install.sh and .env.

Shapes and status codes are taken from the handlers rather than from memory,
and the auth behaviour was checked against a running server.
2026-08-29 03:45:27 +03:00
maeneko 068cab4d24 Remove the MA7 integration
release / release (push) Successful in 2m47s
The integration was half-committed: server.ts imported ./ma7 and the profile
tab was documented, but neither file ever reached the repository, so awg-ui
could not start at all — `Error: Cannot find module './ma7'`.

Everything that referenced it is gone: the import and the /ui/ma7/profile
route, the three MA7_* variables the CLI passed to awg-ui, the installer
prompts and their cli.env block, and the README section. The panel login
prompt goes back to defaulting to admin instead of the MA7 subscriber login.

awg-ui type-checks, builds and starts again.
v0.2.0
2026-08-29 03:23:40 +03:00
maeneko 21c7409017 Check every push with CI
Nothing verified a push to main: the only workflow triggers on v* tags, so an
incomplete commit — one whose new files were never staged, leaving an import
pointing at a file that does not exist — sat on main for two days without a
signal.

This runs on every branch push and pull request: bash -n on the installer,
tsc across awg-ctrl, awg-ui and cli, and the Vite build. awg-ui's tsconfig
covers server.ts, so the missing-module case is exactly what the awg-ui step
reports. The same workflow is added for both hosts, since releases are now
published from GitHub and Gitea alike.

awg-ctrl's tsconfig had `include: [""]`, which matches no files at all
(TS18003) — the awg-ctrl step would have failed on the config rather than on
real errors, so it now points at index.ts.
2026-08-29 03:21:40 +03:00
maeneko ca1c8e8c68 Add a Gitea release workflow
Releases only existed on GitHub, while install.sh now defaults to pulling the
archive from the Gitea mirror. This builds the same archive on a v* tag and
publishes it through the Gitea API, since the runner has no gh CLI: create the
release, fall back to looking it up by tag when it already exists, then upload
the tarball as an asset.

Two details are easy to get wrong and are commented in place. The release id is
the first "id" in Gitea's JSON, so the match must be non-greedy — a greedy one
picks up the author or asset id instead. And the id helper needs `|| true`,
because an empty grep under pipefail would abort the step on the path where the
release already exists.

The workflow cannot run yet: the Gitea repo is a pull mirror, which neither
runs Actions nor accepts release creation. It starts working once the repo is
un-mirrored, Actions are enabled and a runner with the ubuntu-latest label is
registered; the header of the file lists those prerequisites.
2026-08-29 03:08:51 +03:00
maeneko 602c0416ba Move panel name and version into .env, bump to Beta 0.2.0
The product name, the channel word "Alpha" and the version were scattered
across install.sh, the CLI banner, two spots in App.tsx and three
package.json files, so a rename or a bump meant editing strings in code.

They now live in one committed .env at the project root (BRAND, CHANNEL,
VERSION), shipped inside the release archive and read at runtime: the CLI
loads it after cli.env — so real env and machine settings still win — and
injects the three values into awg-ui, which serves them from GET /ui/brand
for the wordmark. awg-ui also parses the same file directly, so
`npm run server` in dev shows what production shows.

install.sh keeps its own VERSION and CHANNEL and says why: it prints the
banner and builds the release URL before the archive holding .env has been
downloaded. It writes BRAND into cli.env only when the operator passed one
explicitly, so the file stays the single source otherwise.

Version bumped to Beta 0.2.0 across .env, install.sh and the three
package.json files.
2026-08-29 03:08:51 +03:00
maeneko 18972ed58f Make the distribution source and brand configurable
Nothing about the install was overridable: the archive could only come from
one GitHub repo, the connectivity precheck pinged github.com whatever the
source was, and the product name was duplicated across install.sh, the CLI
banner and two spots in App.tsx.

install.sh now reads REPO_BASE (default the Gitea mirror), REPO_FALLBACK
(default GitHub), ARCHIVE_URL and BRAND from the environment. Gitea and
GitHub share the same release path, so one scheme covers both:
download_extract tries the primary, warns and falls back to the secondary,
and fails with both URLs listed. The fallback carries real weight today —
the Gitea repo is a pull mirror that does not sync releases, so artifacts
live only on GitHub until release sync is enabled there. The precheck now
probes the hosts actually used.

BRAND flows install.sh -> cli.env -> CLI banner and awg-ui, which exposes
it via GET /ui/brand along with the package version. That route is
unauthenticated because the wordmark is on the login screen, before a JWT
exists; the SPA falls back to a constant if it fails. Renaming a fork is
one environment variable, and no version string is hardcoded in the UI or
the CLI any more.

Also adds /ui to the Vite dev proxy (GET /ui/brand and the API-keys tab
were unreachable in dev), documents the overrides in the README, fixes a
typo in the API.md base URL and states there that the API is not bound to
any host.
2026-08-29 03:08:51 +03:00
maeneko 38e76da84f Make AmneziaWG 3.1 the default generation
install.sh now defaults to AWG_GEN=3.1: an incompatible module or kernel
fails with diagnostics instead of silently falling back to 2.0 and handing
out a server with weaker obfuscation. AWG_GEN=auto keeps the old fallback
for old kernels, AWG_GEN=2.0 still forces 2.0.

Servers already installed on 2.0 no longer need a reinstall: POST
/awg/upgrade (button in the users tab) backs up awg1.conf, rewrites
[Interface] with HeaderProtectionKey and the 3.1 block install.sh writes,
raises any S1-S4 below 12 (header protection needs a 12-byte nonce and the
module only reports "Invalid argument"), restarts the interface and
reissues every vpn:// key. Bringing the interface up is the only real proof
the kernel accepted the params, so a failure restores the previous conf and
brings 2.0 back up. Reissue is not optional: 2.0-era keys stop working the
moment the generation changes.

restartAwg() now re-reads the obfuscation params, generation and keepalive
from awg1.conf before syncing peers. The conf can change while the
interface is down, and a stale CONFIG.gen meant new keys were built on the
old params.

README: install from git.ma7neko.ru (Gitea) instead of raw.githubusercontent,
and document the 3.1 default with the AWG_GEN=auto escape hatch.
2026-08-29 03:08:07 +03:00
maeneko 0dbb13f120 com 2026-08-27 01:39:06 +03:00
maeneko b097ccd5fb Bump version to 0.1.4.2
Ships the AWG_GEN/AWG_PIN installer switches and module-version
surfacing from the previous commit.
v0.1.4.2
2026-08-25 15:00:59 +03:00
maeneko c0e19f284d Add AWG_GEN/AWG_PIN switches and surface module version
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.
2026-08-25 14:57:41 +03:00
maeneko 6fdacd71c3 Fix 3.1 rollout on servers with an older loaded module and UFW
Three defects surfaced on a real upgrade from 2.0.

The module gate read modinfo, which reports the version on disk. apt
--only-upgrade rebuilds the DKMS module but does not replace the one already
loaded, so the installer wrote a 3.1 conf that the running 2.0 module rejected
with 'Unable to modify interface: Invalid argument'. It now compares
/sys/module/amneziawg/version, reloads the module when it lags behind the disk,
and decides the generation from the loaded version only.

Bringing the interface up is the only real proof the kernel accepted the 3.1
parameters, so a failure there no longer aborts the install: it enables dynamic
debug, retries, prints the amneziawg lines from dmesg, then strips the 3.x keys
and comes up on 2.0. The rejected conf is kept as awg1.conf.awg31.

PostUp appends 'iptables -A FORWARD', but UFW inserts its jumps at the head of
the chain, so client traffic was dropped with '[UFW BLOCK] IN=awg1 OUT=eth0' —
the tunnel connected and had no internet. When UFW is active the installer now
adds 'ufw route allow in on awg1 out on eth0' and opens the AWG and UI ports.

Version 0.1.4.1.
v0.1.4.1
2026-08-17 18:48:38 +03:00
maeneko af5c0e1b43 Publish releases as prereleases
The project is still Alpha, so a v* tag should not become the repo's latest
stable release.
v0.1.4
2026-08-17 18:06:59 +03:00
maeneko 01d8524f8c Bump version to 0.1.4
install.sh drives the release URL and tarball name off VERSION, so the
matching tag is v0.1.4. awg-ctrl/package.json was still on the default 1.0.0
and is now in step with cli and awg-ui.
2026-08-17 18:01:35 +03:00
maeneko 44c637f7af Add AmneziaWG 3.1 support with key reissue
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.
2026-08-17 17:58:00 +03:00
maeneko faef87a59d update: translate installer to github
# Conflicts:
#	README.md
v0.1.3.1
2026-07-10 02:50:45 +03:00
Ivan df1e8ce7a8 Update README.md 2026-07-10 01:42:06 +03:00
Ivan 2cfe8e60e2 Change installation script 2026-07-10 01:40:31 +03:00
Ivan 72747c6902 Add README documentation for Forgetting VPN project
Added detailed documentation for the Forgetting VPN project, including architecture, requirements, installation, management, and API usage.
2026-07-10 01:01:40 +03:00
maeneko 16cb9f5bd9 Merge branch 'main' of github.com:maeneko/forgetting
# Conflicts:
#	LICENSE
2026-07-10 00:32:35 +03:00
maeneko 54ffcf9bc0 Initial commit 2026-07-10 00:19:05 +03:00
Ivan 8474156e29 Update copyright holder name in LICENSE file 2026-07-09 21:30:38 +03:00
Ivan 4fc0812927 Initial commit 2026-07-09 21:02:16 +03:00