Tuning data describes the whole server, not a single protocol config.
Replaces the per-config block with a '📊 Host tuning' button in the
server status bar opening a modal with host sysctls (BBR, qdisc,
conntrack, backlog, somaxconn) and per-AWG-container live values.
Adds POST /api/servers/{id}/host_tuning and host_tuning translation key.
- Server config modal now shows container name/status, host BBR/qdisc and
conntrack usage, and live container sysctl values (buffers, fastopen,
mtu_probing, nofile).
- setup_host_tuning() also raises net.netfilter.nf_conntrack_max to
262144 (default ~7680 on small VMs causes 'nf_conntrack: table full'
packet drops on busy VPN gateways). Persisted via
/etc/sysctl.d/98-awp-conntrack.conf.
- Dockerfile now writes AmneziaVPN-style sysctl tuning (64MB buffers,
backlog, fastopen, mtu_probing, etc.) and nofile limits into the image,
and start.sh applies them via 'sysctl -p' at container start.
- New setup_host_tuning() step in install_protocol: loads tcp_bbr if
needed and persists BBR + fq qdisc via /etc/sysctl.d/99-awp-bbr.conf
and /etc/modules-load.d/awp-bbr.conf.
Poll-triggered loadConnections() rebuilt the whole list innerHTML and
destroyed the rename input mid-typing. Polls are now skipped while a
rename editor is open (manual refreshes still work).
- invoke parseHandshakeAgo/formatHandshakeCompact/computeRates again so
the online pulse dot, compact handshake and speed badge render
- startRenameConnection: strip the .client-ip span from the edited name
(previously saving produced 'Name 10.x.x.x 10.x.x.x')
Previously the install modal defaulted to a fixed port and only avoided
ports of same-family instances, so installing e.g. AWG3 on a host where
AWG2 already held 55424 failed mid-install with a half-created broken
container ('port is already allocated').
- GET /api/servers/{id}/used-ports: real listening UDP/TCP ports via ss
- api_install_protocol: reject a busy port upfront with a clear error
naming the owning process, before any docker run
- Install modal: fetches used ports, auto-suggests the first free port,
live-validates manual input (red field + hint + disabled Install button)
- new translation key port_in_use_by (en/ru/fr/fa/zh)
NAT for the VPN subnet happens in the container's network namespace, so the
host /proc/net/nf_conntrack only sees the container's own IP and per-peer
counts were always zero.
- Count conntrack entries per peer IP from /proc/net/nf_conntrack (no extra
packages on the host), matching against the instance subnet
- Record a warning when a peer exceeds 600 simultaneous connections,
at most one per hour per peer, keeping the last 5 events; persisted in
conn_warnings.json inside the container data dir (volume-backed)
- Render warnings under the client meta row (.conn-warnings/.conn-warning,
no inline styles), localized via new conn_warning_text key (en/ru/fr/fa/zh)
- Show peer internal IP next to the client name (.client-ip)
The awg3 card reused awg_desc, which talks about awg-go and S3/S4 — true
of 2.0 as well, so the card said nothing about what the newer protocol
actually changes. Give it its own key naming the 3.x additions: header
protection, content padding and randomized timings.
_t() falls back to the key itself rather than to English, so the string is
added to all five locales at once.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Installed protocols are rendered from static cards in the template that
applyInstalledAppsVisibility() only shows or hides, while the marketplace
builds its list dynamically from MARKETPLACE_APPS. With an entry in the
array but no static card, awg3 showed up as INSTALLED in Templates and
never got a tile on the server page.
Move the NEW badge to 3.1 as the newest protocol.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With HeaderProtectionKey set, the kernel module rejects any junk size
S1-S4 below HEADER_PROTECTION_NONCE_SIZE (12) — see the per-field checks
in netlink.c. The generator drew them from randint(10, 50), so roughly one
install in six produced an 10 or 11 and failed.
The failure was silent: `awg setconf` only reports "Invalid argument",
because the kernel explains itself through net_dbg_ratelimited, which is
off by default. The panel reported the install as successful while the
interface never came up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AmneziaWG 3.1 adds a header protection key, content padding and randomized
protocol timings on top of the 2.0 obfuscation set. The panel generated only
the 2.0 parameters, so servers were always installed as 2.0.
Register awg3 as a separate protocol (container amnezia-awg3) instead of
extending awg2: existing installations keep byte-identical configs, and
clients that predate 3.1 are unaffected.
The parameters are defined once in AWG3_PARAM_MAP and reused by the server
config builder, the config parser and both client config builders. Parsing
matters as much as generation — client configs are rebuilt from the server
config, so a missing parser entry would silently drop the parameters and
break the handshake.
The amneziavpn/amneziawg-go image already ships amneziawg-tools 3.1, so no
image change is needed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
awg/wg show reports peer transfer counters from the SERVER's perspective:
- 'received' = bytes the client uploaded
- 'sent' = bytes the client downloaded
The template rendered them the other way around, so the download badge (down arrow) showed the client's upload and vice versa. Swap the badges so arrows match the client's perspective.
With 'Address = 10.8.1.1/24, fd42:8:1::1/64' the CIDR parser returned
'24, fd42:8:1::1', crashing int() in _get_subnet_base when adding a peer.
Both helpers now take only the first comma-separated (IPv4) component.
- install_protocol detects host IPv6 (default route or global address)
and configures a ULA subnet (fd42:8:1::/64) alongside IPv4 when present
- start.sh learns the IPv6 subnet from the config Address line and adds
ip6tables forwarding + NAT66 (MASQUERADE) rules; IPv4-only setups are
untouched
- clients deterministically get an IPv6 derived from their IPv4 last
octet (10.8.1.N -> fd42:8:1::<hex N>), so existing peers receive IPv6
automatically when their config is regenerated
- peer AllowedIPs and client Address become dual-stack; clientIpv6 is
stored in clientsTable; Cloudflare IPv6 DNS is added for v6 clients
- get_client_config and toggle_client re-add path handle IPv6 as well
xray api adu in Xray-core v26 builds a full inbound config from the
payload and silently adds 0 users (exit code 0) when port/listen are
missing. Include the inbound's port and listen in the adu payload and
treat "Added 0 user(s)" output as a failure so the caller falls back
to the restart-required error path instead of reporting success.