Commit graph

17 commits

Author SHA1 Message Date
Ivan Bondarev
013d3c012e
release: v5.30.0 (#248)
Some checks are pending
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Install and update commands move to releases/latest/download, so a copied line
no longer goes stale with the next release and serves a signed asset. Four pins
stay on purpose and the docs guard now explains which and why.

A guard against oversized I1-I5, which make the interface dump loop forever
upstream. Adding it exposed the same mistake in four neighbouring places: the
guard sets a flag and the code after it kept building positive claims from the
empty string left behind. diagnose said "I1=absent" at the moment the guard
fired because I1 was oversized, and with a carrier profile turned that into a
green tick; list, given a timeout in this same branch but no failure handling,
turned a visible hang into a complete and entirely wrong table. All eighteen
interface calls across the four scripts are bounded now, timeouts are named
separately from other failures, and the honest states survive.

The size parser no longer returns zero with a success status for input it did
not parse, since zero means "no masking packets" and switches the protection
off.

ADVANCED gains a client generation matrix answering which generation a client
speaks, with the weaker rows marked as inferences. Nine claims that did not
match the code or the world were corrected.
2026-09-01 12:20:49 +03:00
Ivan Bondarev
fea8e74bab
docs: a dated facts block in both READMEs, generated from repository data (#241)
Some checks are pending
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Waiting to run
The block sits at the top of README.md and README.en.md and carries a date, so
it promises the reader that the numbers are fresh. A hand-written block breaks
that promise silently: it starts lying the day someone forgets to update it, and
the date next to it makes it look more trustworthy than ordinary prose.

So every value is derived instead of typed:

  installer version and release date -> SCRIPT_VERSION + the CHANGELOG heading
  the OS lists                       -> docs/support-matrix.json project_policy
  the date the OS data was verified  -> verification.last_verified
  architectures                      -> the arch: fields in arm-build.yml

The one claim that cannot be derived is the config profile, so it is tied to the
code instead: if a third-line parameter reaches a config template, the generator
stops rather than quietly emitting a promise that configs are still 2.0.

The date comes from the repository, never from the clock. A generated block
stamped with today would turn the check red the day after every commit, and a
check that fails by default is read as broken and then ignored.

check-docs-consistency.sh verifies the stored block against the generator, so
this runs in CI and in preflight, and docs/RELEASE_PROCESS.md gains the
regeneration step, which is now on the release path: bumping the version changes
what the block must say.

Four defects were found by review after the first version, all the same shape,
a guard reporting success having checked nothing:

- The check trusted the generator's exit code alone, so an empty file in place
  of the generator produced PASS. Worse, dropping the --check token made the
  checker run in write mode: it rewrote the working tree in CI, printed PASS and
  destroyed the evidence. Writing is now explicit (--write), a call with no
  argument refuses and exits 2, and the check additionally requires the success
  signature in the output.
- Write mode could have destroyed both READMEs and stayed green, because the
  check and the tests only ever looked at the text between the markers. The
  generator now verifies the rest of the file itself before writing, writes
  atomically through a temp file as the project convention requires, and a test
  compares a hash of everything outside the block across the write.
- The third-line parameter guard knew one of the two ways a parameter reaches a
  config, and five of the nine names ADVANCED.md documents, so it protected its
  own claim more narrowly than the claim is stated. Optional parameters are
  written as echo "Name = ...", which is how I1-I5 are emitted and how
  HeaderProtectionKey would be added.
- The write path validated and wrote one file at a time, so drift in README.md
  together with broken markers in README.en.md left the repository half updated,
  the state the surrounding comment claimed to prevent. Both files are now
  parsed, rendered and self-checked before either is replaced.

The module row itself was wrong in both languages until someone read the
installer: it credited the Amnezia PPA, which is false on kernels older than
6.7, a stock Debian 12 among them, where the installer deliberately declines the
PPA module and builds a pinned 2.0 module from source. The block was stating the
kind of untruth it exists to prevent.

tests/test_facts_block.bats mutation-tests the guard in 28 cases: each case
breaks exactly one link the check holds, including the inverse case where those
parameter names appear legitimately and the guard must stay quiet.
2026-08-30 22:36:34 +03:00
Ivan Bondarev
3fda0c9f92
release: v5.29.0 (#240)
* chore(release): prepare v5.29.0

Version bumped in all three kinds of place at once, because a partial bump is
rejected by preflight and, worse, ships an installer whose helper download
fails: 6 SCRIPT_VERSION/AWG_COMMON_VERSION assignments, 6 file headers with
their dates, 36 pinned raw-URL tags across the documentation, 2 README badges.
The four helper SHA pins were recomputed afterwards, against the working tree,
which is the correct source while the tag does not exist yet.

CHANGELOG.md and CHANGELOG.en.md gain the 5.29.0 section and its comparator
link; SECURITY.md moves the supported window to 5.29.x.

build-release-notes.sh now accepts a semver pre-release suffix and reads the
base version's changelog section. RELEASE_PROCESS.md prescribes rehearsing the
pipeline on a throwaway vX.Y.Z-rc1 tag, and release.yml passes the tag name
through verbatim, so the previous X.Y.Z-only check would have killed that
rehearsal at the notes step, before it reached the draft, the upload, the asset
count and the publish - everything the rehearsal exists to exercise. A
pre-release has no changelog section of its own and should not have one. Three
test cases cover it, two of which fail against the previous script.

update-sha-pins.sh prints the "no tag, comparing against the tree" notice once
per run rather than once per pin. The first attempt at this kept the flag
inside the function that produces the value, which runs inside a command
substitution, so the flag died with the subshell and the notice still appeared
four times; the flag now lives in the caller.

* feat(release): sign a release with one command instead of a manual loop

The trusted comment has to read exactly "amneziawg-installer <tag> <file>";
a typo there yields a signature that verifies cryptographically and is still
rejected by the release, which is found only after the tag is pushed. The
script composes it, validates the tag before asking for the password, asks
once for all six signings, and ends by running verify-signatures.sh so that
"signed 6" does not rest on minisign merely not crashing.

It refuses to run without a terminal. That is the regression it was written
for: the documented manual loop was run where stdin was not a terminal, its
read failed at once, the && chain skipped the signing, and the line finished
with no signatures written and nothing said. Six test cases cover the refusal
paths, because the happy path needs the private key and the happy path is not
where this went wrong.

* fix(release): find the signing key when HOME is not the Windows profile

bash launched from PowerShell or cmd reports HOME as /home/<user>, a path that
does not exist on disk, so a lookup anchored on HOME alone reported the key
missing while it sat in the Windows profile. USERPROFILE and HOMEDRIVE with
HOMEPATH are now tried as well.

The Windows to POSIX translation uses tr rather than ${x//\//}: measured on
bash 5 under Git Bash, that substitution returns the string untouched. The bug
would have been quiet, because MSYS happily opens the half converted path
C:\Users\biv/.minisign/..., so -f succeeds and the defect hides until an
environment that rejects such a path. cygpath is not used: a shell started from
PowerShell inherits the Windows PATH, where cygpath is usually absent, which is
precisely the environment the fallback exists for.

* fix(release): name the WSL trap instead of looking like a broken script

On Windows, `bash` typed in PowerShell or cmd is usually the WSL launcher at
C:\Windows\System32\bash.exe, not Git Bash. WSL inherits none of the
Windows environment: HOME points at a Linux home with no key in it, USERPROFILE
and HOMEDRIVE are empty, and MINISIGN_KEY exported on the same command line
never arrives either. Every fallback the script has was therefore invisible,
and the failure read as a defect in the script rather than as the wrong shell.

The key lookup now says so when it detects WSL, and RELEASE_PROCESS.md gives
the explicit Git Bash invocation. Verified by running the script under real
WSL, not by simulating one; a companion test guards the other direction, that
an ordinary shell is not told it is something it is not.

* chore(release): sign v5.29.0

* fix(docs): repair backspace bytes in the signing instructions, and guard the class

A doc edit assembled through a Python string containing System32\bash.exe wrote
a literal backspace (0x08) where the backslash-b was: the documented command was
unusable, and invisible - a terminal does not render the byte, grep over text
does not look for it, and the diff shows the line as correct. Python warned
about the neighbouring invalid escape and said nothing about this one, because
backslash-b is a valid escape. The warning named the harmless case and hid the
harmful one. Found by an outside reviewer.

check-docs-consistency.sh gains a check for control bytes in tracked text files,
deliberately written without hex escapes: a first attempt at the same guard,
assembled the same way, put fifteen raw control bytes including a NUL into the
checker itself and made it flag untouched files. tr understands its own escapes,
so the shell has nothing to mangle. Mutation tested: a planted backspace in
CHANGELOG.md fails the run, a clean tree passes.

sign-release.sh now keeps minisign's stderr and prints it when a signing fails.
Sending it to /dev/null unconditionally turned a wrong password, a full disk and
a corrupt key into one faceless line. The capture idiom was measured: a wrong
password now reports "Wrong password for that key".

* fix(tests): stop testing the runner's package list, and clean the public tree

Three sign-release cases were red in CI and green locally. The script checks
that minisign exists before it checks anything else, and the test workflow
installs only bats, so those cases reported on what the runner had installed
rather than on the script. A stub on PATH removes the dependency; it exits
non-zero on purpose, so a case that ever reached the signing loop would fail
loudly instead of claiming six signatures with no files behind them.

Two more cases asserted the docs checker prints "13 passed". Adding a check
turned that into a failure, which punishes strengthening the gate. They now
assert that nothing failed, which is what the tests are actually about.

Private tracker identifiers had returned to the public tree in two test
comments, one of them added by this very branch, while this release's changelog
claims they were removed. Nothing guarded that: the marker lists in
preflight-check.sh and commit-hygiene.yml did not include them. They do now, and
the comments say what they mean instead of pointing at an identifier a reader
outside cannot resolve.

The release title was 179 characters against a 70 character guideline and would
have been cut mid-word in the sidebar. Both changelog lead sentences are now
short, with the detail moved to the paragraph below; the title comes out at 61.

* fix(release): close four ways the tooling could report work it had not done

Empty notes published green. build-release-notes.sh checked only that the
version heading existed; a heading with no bullets under it - the ordinary
result of opening the section during prep and writing it later - produced a
body that was the bilingual skeleton and nothing else, with rc 0 behind it.

Notes cut at a quoted heading. The section ended on any line starting with
"## [", so a bullet quoting the prescribed "## [X.Y.Z] - YYYY-MM-DD" format
truncated everything after it, silently. The terminator now requires a digit.

A pin reported as written when it was not. _write_pin took sed's exit status as
proof, and sed returns 0 having replaced nothing. The printed value was the
DESIRED hash, not the one in the file, so the line read as confirmation. The pin
is now read back and compared.

An interrupted signing run downgraded to a warning. Preflight judged the
signature tag by the first file the glob returned, and the signing order differs
from the alphabet, so a run interrupted early looked like "all of these are
simply from the previous release" - a warning, which does not block a tag. The
count could not see it either, because the old signatures are always present.
All six tags are read now, and a mixture fails.

sign-release.sh runs the verification even after a partial failure: the mixture
is the dangerous state, and the operator needs to see which files carry which
tag rather than a count.

Each is covered by a test or by a recorded mutation run; the two release-notes
cases fail against the previous script.
2026-08-30 19:25:21 +03:00
Ivan Bondarev
56d61b8d71
fix: compare SHA pins against the tag the installer downloads from (#239)
Some checks are pending
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Waiting to run
The lockstep test compared each pin with the helper file sitting next to it
in the working tree. That is a proxy for the real contract, and it holds
only while the tree happens to equal the tag, so it was wrong in both
directions: red on main after a helper was touched without a version bump,
even though every published installer was correct, and green when the pins
had been recomputed from the tree while the tag already existed - the one
state in which a shipped installer refuses the download it should accept.

The installers fetch helpers from
raw.githubusercontent.com/<repo>/${AWG_BRANCH}/<helper>, with AWG_BRANCH
defaulting to v$SCRIPT_VERSION, so a pin's source of truth is the helper's
bytes at that tag. The test and scripts/update-sha-pins.sh now both compare
against the tag when it exists and against the working tree when it does
not, and both state which of the two comparisons they made.

update-sha-pins.sh had to move with the test: preflight runs the bats suite
as step 3 and "update-sha-pins.sh --verify" as step 8, so two different
answers about one state would make preflight green and red at once.

test.yml gets fetch-depth: 0 because the default checkout brings no tags on
branch and pull_request events, leaving the test nothing to compare with.
Measured: full history costs 1 s and 11 MB here.

New tests/test_sha_pin_source_of_truth.bats covers update-sha-pins.sh, which
decides what a pin ought to be and had no tests at all. Six of its seven
cases fail against the previous version of the script.

Known limit, written down rather than hidden: both tools read the local tag
set, so a checkout that never fetched the tag, or whose tag moved under
git tag -f, falls back to the tree comparison. That fallback announces
itself, and RELEASE_PROCESS.md now asks for git fetch --tags --force before
the preflight.
2026-08-30 10:58:51 +03:00
Ivan Bondarev
c71fc5d73b
docs: one source for the OS support matrix, and the same warning in both languages (#238)
Some checks are pending
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Waiting to run
The Russian install guide warned that Ubuntu 25.10 is out of support and that
there is no reason to pick it for a new server. The English one presented the
same release as an ordinary choice and advertised its ARM prebuilt. One language
was warned and the other was not, about a machine that faces the internet on a
VPN port. Debian 12 was worse: nothing was said in either language, although its
regular support ended on 2026-07-11, six weeks before a documentation review
walked through these files without noticing, because that review compared the
documents with each other rather than with a calendar.

Two states could not describe this. Ubuntu 25.10 is an interim release with no
extended phase and receives no security updates at all; Debian 12 keeps
receiving them through Debian LTS until 2028-06-30. One bucket marked end of
life is wrong in both directions. The compatibility table now carries three
states with the vendor dates in their own column, and the two languages say the
same thing row for row. The surfaces where a reader picks an operating system
list what this project suggests, and a line beneath them states that the other
two still install and run, so that dropping them from a badge cannot be read as
dropping support.

The checker held its own literal copy of the matrix, which made a green result
mean that the copies agreed rather than that any of them was true. It now
derives the set from docs/support-matrix.json, and a second check recomputes
each platform's lifecycle from its dates. That catches the class comparing
documents cannot catch in principle, where an external fact changes and no
document notices. It will sit quiet for nearly two years, long enough to break
unnoticed, so it validates its own classifier against nine cases before trusting
it, including both sides of the boundary the matrix declares.

Inside the installer only advice changed. Two messages recommended reinstalling
on Ubuntu 25.10 or Debian 12, which is what a stranded operator reads at the
moment of choosing a new system. The gate, the tested list and the workaround
machinery for those releases are untouched, so existing installations keep
working. No lifecycle notice was added at runtime, because that would mean
hardcoding vendor dates into the installer, which is another copy of the matrix
and the thing this change exists to remove.

Four review rounds found defects in the change itself, each reproduced as a
mutation before and after the fix. The staleness warning called a function that
does not exist in this script and could never fire. The lifecycle check reported
the number of platforms in the file rather than the number compared, so an empty
list produced agreement having compared none. Reading the token list with a
here-string yielded one empty string rather than an empty array, which made the
emptiness guard unreachable and let an empty token reach grep, where it matches
every file; a matrix with an empty version passed the entire run. A failure to
produce that list left the array undeclared and the ARM coverage check reported
agreement over nothing. One exit code meant both a real disagreement and any
uncaught exception, so a malformed file sent the reader to recompute dates that
were fine. The rule requiring one default platform per family walked the
families that were found, which cannot notice one that is absent. Three dates
disagreed with the source the matrix names as its own, and one had already
reached both READMEs.

tests/test_support_matrix_guard.bats covers all of it against deliberately
broken copies, with a control on the untouched file. Run against the revision
before the last fix, seven of its twelve cases fail.
2026-08-30 01:04:34 +03:00
Ivan Bondarev
bb6fb794af
docs: add a machine readable support matrix as the single source of truth (#236)
Some checks are pending
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Facts about which systems this installer targets currently live in twenty
three files: both READMEs, both install guides, ADVANCED, CASCADE, WARP-RU,
the roadmap, three GitHub templates, two workflows, the installer scripts
themselves, and the consistency checker. Nothing arbitrates between them, so
they drift apart quietly and a reader cannot tell which copy is current.

The matrix carries values only, no prose, so it cannot diverge between the
Russian and English documentation the way every other copy can.

It stores the basis rather than the conclusion. Vendor dates are recorded and
lifecycle is derived from them by a rule written into the file, so a stale
lifecycle can be recomputed and caught instead of believed. The same idea
covers verification: last_verified names everything that must be rechecked
before it may be advanced, not just the field that prompted the edit.

Deriving the states from vendor data surfaced something the documentation
never said in either language. Debian 12 left regular support on 11 July
2026 and now runs on extended support only. Ubuntu 25.10 left support on
1 July and has no extended phase at all. Those are different situations and
they need different advice, which is why the file separates lifecycle from
project policy: a platform can be perfectly supported upstream and still not
be the pick we suggest by default.

The dates come from endoflife.date, the PPA suites match the codename
mapping in the installer, and the seven ARM prebuilt ids match the build
matrix exactly in both directions.

No consumer reads the file yet. Wiring it in is deliberately left separate,
because the repository has neither jq nor python anywhere in scripts or
workflows, and choosing whether to add one is a decision about the project,
not a detail of this file.
2026-08-28 23:48:03 +03:00
Ivan Bondarev
b9f8a70190
feat: sign releases and publish them atomically (#233)
Release artifacts now carry detached minisign signatures. The private key
stays on the maintainer's machine and never reaches Actions: signing happens
offline and the signatures are committed under signing/ before the tag, so the
workflow only verifies. A signature that is missing or wrong stops the
publication instead of leaving a half-published release behind.

release.yml creates the release as a draft, attaches the scripts, their
signatures and KEYS.txt, checks that every expected asset is there, and only
then publishes. Publishing first and attaching afterwards used to leave
releases/latest/download answering 404 for as long as it took somebody to
remember the manual step, which is precisely the address users are given.

The trusted comment on each signature names the tag and the filename. A
signature verifies a byte sequence, not an intention, so without that binding
an old and perfectly valid signature paired with its old and perfectly valid
file would pass for any tag.

commit-hygiene.yml closes the window preflight cannot see: preflight runs at
tag time and diffs against main, so a bad commit message is long since merged
by then. The new workflow checks the same things while the commit can still be
amended.

Review of the branch found three defects that would have fired on the first
signed tag, all fixed here. verify-signatures.sh could report success having
opened no file, because it compared the number of files checked against a
number derived from the same list it was checking; the expected set now comes
from the scripts at the repository root. The release title was interpolated
into a shell command, and this changelog writes flags in backticks, so the
title of v5.27.1 would have published with two phrases executed and removed;
it now travels through the environment. Publish re-applies the rebuilt title
and notes, so fixing a changelog and pushing the tag again no longer
republishes the failed run's text.

preflight tells a stale signature apart from a wrong one, because signatures
stay in the tree between releases and editing any signed script would
otherwise turn the gate red on ordinary branches.

tests/test_release_signature_guards.bats covers the guard that was silent,
control case included: with an intact list the guards must stay quiet.
2026-08-28 23:14:08 +03:00
Ivan Bondarev
f1e1447b02 docs(install-vps): add the Russian deployment guide
README, ADVANCED, CHANGELOG, CASCADE and WARP-RU all ship as RU+EN pairs.
INSTALL_VPS was English only, so the Russian half of the install and
troubleshooting search traffic had nowhere to land.

The Russian guide is not a translation of the English one. It keeps the same
deployment skeleton but follows what people actually look for: client configs
get their own section instead of a subsection, and the ARM prebuilt path is
spelled out where it differs from DKMS. On that path there is no dkms, no
kernel headers and no ensure-module unit at all, so after a kernel change the
tunnel does not come back on its own and repair-module does not help either;
the usual way back is to run the installer again.

INSTALL_VPS.ru.md joins URL_DOCS in check-docs-consistency.sh so its pinned
raw-URL tag is checked against SCRIPT_VERSION like every other document.
Without that entry the pin would go stale at the next release with the checks
still green. RELEASE_PROCESS now names INSTALL_VPS*.md, which covers the next
paired document by construction, and the Russian README links to the Russian
guide instead of sending Russian readers to the English one.
2026-08-25 12:22:33 +03:00
Ivan Bondarev
702d3aff5b
Release v5.18.1: --force port change, full-tunnel iOS ::/0, dual DNS, low ports
Some checks failed
Docs consistency / docs-consistency (push) Has been cancelled
ShellCheck / Lint and syntax check (push) Has been cancelled
Tests / unit-tests (push) Has been cancelled
Bug-fix release. Four fixes plus documentation.

- install --force --port=N now changes the server port (was silently ignored: the render re-read the old ListenPort).
- full-tunnel clients get 0.0.0.0/0, ::/0 so AmneziaVPN on iOS accepts the all-traffic mode; manage regen upgrades non-customized clients too.
- default client DNS is now the 1.1.1.1, 1.0.0.1 pair.
- --port accepts ports 1-65535 (was 1024-65535) so 443/80/53 work for DPI evasion on mobile carriers.

Docs: CHANGELOG, S3/S4 range fix, stale issue link cleanup, Ubuntu 26.04 in the subtitle, --force re-run FAQ, a mobile-port note.
Live-tested on Debian 13 (x86_64) and Ubuntu 26.04 (ARM64, native IPv6).
2026-06-28 09:20:07 +03:00
Ivan Bondarev
ed8945d54d
feat(obfuscation): pass special-junk params I2-I5 through to clients (#71)
Some checks are pending
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Waiting to run
Wire all five CPS special-junk params I1-I5 from the server awg0.conf into client configs (.conf, QR, vpn://). Previously only I1 reached clients; I2-I5 were hard-coded empty and undeliverable. Scope = variant 1 of #71: the admin sets I2-I5 in awg0.conf and manage regen distributes them; no new CLI flags. RU/EN in lockstep, SHA pins recomputed, docs (ADVANCED/ROADMAP/CHANGELOG RU+EN), new bats coverage. Release v5.18.0.
2026-06-27 11:26:07 +03:00
Ivan Bondarev
313b0007d1
feat(firewall): clamp TCP MSS to the tunnel MTU (v5.17.0) (#139)
Some checks failed
Docs consistency / docs-consistency (push) Waiting to run
ShellCheck / Lint and syntax check (push) Waiting to run
Tests / unit-tests (push) Has been cancelled
Add an automatic TCPMSS --set-mss rule (mangle FORWARD, both directions, SYN only) to the server PostUp/PostDown, derived from AWG_MTU (1240 v4 / 1220 v6), fixing the PMTU blackhole on mobile/double-NAT/cascade paths. Ships v5.17.0 with tests, SHA pins, CHANGELOG and docs. Smoke-tested on x86_64 and ARM64 (IPv4 + IPv6).
2026-06-24 19:03:24 +03:00
Ivan Bondarev
8152bd21c0
docs: refresh ROADMAP recently-shipped with v5.16 themes and companion guides (#126)
Add three evergreen themes to Recently shipped / Уже сделано (RU+EN):
security and reliability hardening, correct iOS behavior in the default
routing mode, and the companion guides (official-app comparison and the
two-server cascade). Keeps the page theme-based, not a dated changelog.
2026-06-16 17:50:00 +03:00
Ivan Bondarev
902f4d787b
release: v5.15.6 - input validation, atomicity and a JSON status field
A hardening and quality release that finalizes two accumulated workstreams.

Code-audit hardening (manage/awg_common/install):
- modify validates DNS as real IPv4/IPv6; octet-range check on the detected public IP
- --expires validated before client creation; --psk fails closed
- atomic .vpnuri and QR writes via the shared safe-temp helper; one shared client-artifact cleanup
- content-aware expiry cron; restore guards for an empty server/clients backup
- INT/TERM abort the script with exit 130/143 instead of falling through

Machine-readable status (additive, backward-compatible):
- list --json and stats --json also emit a stable status_code enum
  (active | recent | inactive | no_handshake | key_error | no_data);
  the localized status field is kept

Docs and process:
- accurate CLI help (--endpoint FQDN/IPv4/[IPv6], --no-tweaks note), README diagnose/repair-module rows
- ARM coverage for Ubuntu 26.04 (DKMS); docs-check enforces the OS x arch x prebuilt-target matrix
- CI badges, signing-doc cleanup, clearer issue/PR templates

Release mechanics: version and date bumped to 5.15.6 across the six scripts,
helper SHA pins recomputed, CHANGELOG (RU + EN) updated, pinned raw-URL tags and
README badges bumped. Default install behavior and the support matrix are unchanged.
2026-06-08 14:40:03 +03:00
Ivan Bondarev
13203c6293
v5.15.3 - maintenance release (validator hardening, audit fixes, CI tooling) (#100)
Hardening release after four rounds of external code and documentation audits: installer input validators brought to canonical parity, manage restore/modify correctness fixes, atomic file operations, reproducible ARM builds with a pinned module ref and build manifest, bilingual release notes built by release.yml, an 8x faster Unicode-aware docs consistency check, and documentation accuracy fixes across README, INSTALL_VPS, ADVANCED, RELEASE_PROCESS and the Code of Conduct. Test suite grows to 788 checks; live-tested on clean Ubuntu 24.04 x86_64, Ubuntu 26.04 ARM64 and Debian 13 VPSes including real cross-continent AWG 2.0 handshakes.
2026-06-04 05:14:46 +03:00
Ivan Bondarev
1cbee098ca
v5.15.1 - maintenance release (audit fixes, IPv6 hardening, release gate)
Maintenance release after external code and documentation audits. No new
features; hardens the v5.15.0 dual-stack IPv6 work, tightens several manage
commands, fixes correctness and robustness issues, and adds a release
preflight gate plus a docs-consistency check.

Behavior change: split tunnel + IPv6 mirrors the IPv4 intent - a custom
ALLOWED_IPS keeps that IPv4 split and adds only the tunnel ULA for IPv6
instead of forcing a full ::/0 route. A full tunnel still gets ::/0 (native
IPv6) or the ULA.

Fixed: vpn:// URI carries the server's real MTU/keepalive/DNS; stricter
native-IPv6 detection (global non-ULA address and default route) with the
host IPv6 stack re-enabled before detection; install fails fast on apt update
errors; manage modify validates Endpoint/AllowedIPs; manage add is safe
against name reuse and cleans partial artifacts; manage restore prunes stale
clients/keys and recreates the expiry dir; manage help exit codes; manage
--json keeps stdout pure JSON; log messages stop doubling percent signs.

Docs/process: subnet and OS help accuracy, list --json documented, changelog
compare-links and anchors restored, SECURITY/CONTRIBUTING refreshed, public
RELEASE_PROCESS.md, release publish gated behind a full preflight job, new
docs-consistency workflow, signing design aligned with the asset draft.

Version bumped to 5.15.1 across all six scripts; SHA pins recomputed.
2026-06-02 14:08:36 +03:00
Ivan Bondarev
3d974af9e7 fix: address second-pass review findings (HIGH/MEDIUM bundle)
Second independent review of the three preceding commits surfaced:

1. (HIGH) tests/test_v5140_public_ip_services.bats - SC2318 in curl mocks:
   `local args=("$@") url="${args[$((${#args[@]} - 1))]}"` references the
   array on the same `local` line where it is being assigned. ShellCheck
   flags this and the assignment is unreliable. Split into two `local`
   statements. Bats suite still passes 11/11 after the fix.

2. (MEDIUM) ADVANCED.md / ADVANCED.en.md - the legacy split-tunneling
   paragraph still claimed the default AllowedIPs = 0.0.0.0/0 covers the
   tunnel subnet automatically. README was already corrected in edf4f7b;
   ADVANCED is now mirrored: coverage depends on --route-all vs the
   default --route-amnezia (Amnezia List excludes 10.0.0.0/8) vs custom.

3. (HIGH) docs/SIGNING_DESIGN.md - threat model overclaimed coverage of a
   GitHub-account compromise. KEYS.txt is fetched from the same trust
   domain, so a first-time user in a single compromised session is in a
   TOFU window. Reworded "Covered" / "Partially covered" / "NOT covered"
   sections, added the recommendation to publish the public-key
   fingerprint via at least one out-of-band channel.

4. (MEDIUM) docs/SIGNING_DESIGN.md - added rollback / misbinding to NOT
   covered, plus a minisign trusted-comment binding (tag + filename) in
   the signing flow so an old signature paired with a different file or
   release fails verification.

5. (MEDIUM) docs/SIGNING_DESIGN.md - changed the README "Verifying
   releases" checklist item from DONE to TODO. README has no minisign
   section yet; will be added when KEYS.txt is published so the section
   is actionable rather than vapor.

6. (LOW) docs/SIGNING_DESIGN.md - narrowed the GPG comparison to "does
   not protect the curl-to-raw or release-asset download path" (verified
   tags do cover the blobs they point at if the user verifies the tag
   first). Replaced two broken references (NixOS signing-prefixes and
   minisign #why-not-gpg anchor) with the minisign source repo and
   OpenBSD signify man page - both stable and on-topic.

No behavior change in scripts; only test ergonomics + docs accuracy.
2026-05-18 22:48:01 +03:00
Ivan Bondarev
8c14921f32 docs(security): release signing design (minisign) - planning for v5.14+
Spec for adding detached minisign signatures to installer/management
scripts and tagged releases. The proposal:

- Keypair generated offline by the maintainer, public key published as
  KEYS.txt in the repo root, private key NEVER exposed to GitHub Actions
  (this is the whole point - signing must be local to the maintainer).
- Per-release signing happens on the maintainer's machine before
  'git push origin vX.Y.Z'; .minisig files attached to the GitHub Release
  as assets either manually (gh release upload) or via the draft workflow
  at docs/release-sign.yml.draft.
- User-side verification: 'minisign -V -p KEYS.txt -m <script> -x <sig>'.

This commit is design + drafts only. Workflow stays under docs/ until the
public key is committed and signatures of pre-release assets are tested.
SECURITY.md gains a 'Release integrity (planned)' section.

Activation is user-action-blocked (maintainer must generate the offline
keypair). Closes implementation gap surfaced by competitor analysis
2026-05-18 (pwnnex/ByeByeVPN ships signed releases + SBOM).

Files added:
  docs/SIGNING_DESIGN.md       - threat model, tool choice, signing flow
  docs/release-sign.yml.draft  - GitHub Actions workflow draft
  SECURITY.md +6                - 'Release integrity (planned)' section
2026-05-18 22:37:31 +03:00