The rollback claimed an invariant in its own comment - restore the original, confirm it,
and only then release staging - and did not enforce it. original_back was computed and then
ignored: the staging swapoff ran unconditionally.
The failure path that mattered: staging is active, the original is already off, reactivating
the original fails (swapon can fail under the memory pressure that made the resize
necessary), and the rollback then switched off the staging swap - the last thing carrying
load. Recovering from a failed resize left the host with no swap at all, which is worse
than the failure being recovered from.
original_back now gates the release. If the original cannot be brought back the rollback
returns early with the staging swap still active, and the error carries the two commands
that finish the job by hand. An extra temporary swap is always the better end state than
none. The staging file is also only unlinked once swapoff has actually succeeded - an
active swapfile cannot be removed, and attempting it produced an error that explained
nothing.
Asserting this needs a seam, because the invariant is a sequence of calls rather than a
value: a small SwapCommands trait covers swapon/swapoff, with the real implementation
behind it. Three tests: the original cannot be restored and swapoff is never called with
the message naming the preserved fallback; the original comes back and staging is released
in that order; nothing was taken down so nothing is touched.
Also from the same audit:
- StaleOwnership advice was wrong. It suggested deleting the ownership record, which turns
the file Foreign - refused just as firmly. The message now distinguishes the two cases: a
leftover .previous next to the swapfile is the signature of a replacement killed between
installing the new file and recording its identity, so the error says nothing is lost,
points at the intact previous swapfile, and gives the exact commands for going back or
keeping the new file. Full automatic crash recovery is deliberately not added here - it
needs a transition journal, and that belongs with the Debian acceptance work.
- The acceptance checklist still described the PID-file lock ("re-acquires by the absence
of /proc/<pid>"), which stopped being true when locking moved to flock. The kernel
releases it on process exit, and the leftover file means nothing.
- shellcheck was installed by --prepare-build-env but never run. It now runs in
prepare_build_gate: the script installs systemd units and writes to /usr/sbin, so it is
part of the release surface.
- The skip message for systemd-analyze verify now says what the check actually is - a
post-install gate, expected to be skipped on a clean builder - rather than reading like a
build gate that silently did not run.
One audit item did not reproduce: there is no duplicated -j in antitorrent_drop_counter,
the flag appears once.
Version raised to 0.3.0 in traffic_core/Cargo.toml, with the packaging examples in README
and INSTALLATION updated to match. The reboot scheduler keeps its own 0.2.0.
cargo fmt --check, clippy -D warnings, cargo test --locked --workspace --release: 191 + 4
passed. build.sh not syntax-checked here - no bash on this host; the shellcheck gate now
covers it on the builder.
The docs folder had drifted into a mixed-language contract: IMPLEMENTATION_SPEC.md was
entirely in English while the rest was Russian with English headings. Prose is now
Russian throughout; English is kept only where it is technically load-bearing (commands,
tool output, config keys, file/module/table names, tc handles) and for terms with no
settled Russian equivalent in this context (drift, reconcile, peer, namespace, handshake).
docs/README.md now states that contract explicitly.
Rewriting the spec surfaced several places where it described intent rather than the
code. Corrected against traffic_core/src:
- module layout: the spec showed traffic/ and firewall/ as directories with
model/inspect/apply submodules. They are single files, and sysctl_tuning.rs was
missing from the tree entirely.
- data model: Peer was documented as carrying public key, handshake and counters. It
carries only ipv4; runtime state lives in a separate PeerRuntime, and there is no
desired-state struct at all - it is derived inline from config plus sorted peers.
RuntimeTarget.netns_path is a String, not a PathBuf.
- interface detection: the spec listed a third step taking the first non-loopback
interface with an IPv4 address. No such fallback exists; detection bails with an
explicit error, which is safer and is now documented as deliberate.
- reconcile: the pseudo-flow folded firewall into reconcile_traffic. They are two
independent reconciles with separate locks.
- locking: only traffic.lock was documented. There are three locks, and
sysctl_tuning.lock has a 30s timeout rather than 10s. Stale-lock recovery via
/proc/<pid> was undocumented.
- sysctl_tuning: the module also manages /etc/modules-load.d/90-awg2-harness-bbr.conf
when BBR is active, and refuses to touch either managed file without its marker.
Both docs and the acceptance checklist claimed only the sysctl.d file was written.
- status contract: the documented output was outdated. All 15 printed sections are now
listed in the order status.rs emits them, including the degraded path when no container
is found, and the fact that the Network tuning block is printed between the System
service line and the container lines.
- swap: size was documented as a constant 2048 MB instead of swap.size_mb, and the
/etc/fstab.awg2-harness.bak backup was undocumented.
- apply/verify: the tolerated-error lists are now the actual closed sets from the code.
Acceptance checklist gained checks for the daemon startup failure policy (sysctl/swap
fatal, traffic/firewall not), lock contention and stale-lock recovery, degraded status,
the modules-load file, and the marker guard. The stale host wg requirement was dropped
from its host-tools item.
Every path, table, counter and log string quoted in the docs was verified to exist in
the source. cargo test --workspace: 110 + 4 passed.
Peer discovery is now the single owner of how wg is invoked and parsed.
wg.rs:
- resolve the binary through ordered amnezia.wg_binaries (default [awg, wg]);
awg is the real binary in the official image and wg is a symlink to it, so awg is
tried first and wg stays for plain WireGuard containers. No probe call: resolution
is a property of the command being run anyway.
- drop the nsenter fallback. AmneziaWG is userspace and its UAPI socket lives under
RUNSTATEDIR/amneziawg/ in the container mount namespace, while host wireguard-tools
looks under /var/run/wireguard/. nsenter -n enters only the network namespace, so
that path could never return peers.
- classify dump lines structurally instead of by column count. The AWG interface line
is longer than a peer line (14 columns on AWG 2.0, 28 on AWG 3.0) and its
handshake/rx/tx positions hold junk-packet sizes that parse as integers, so only the
allowed-ips column discriminates. Column 1 of an interface line is the device private
key, which the previous heuristic rejected only incidentally.
- fix allowed-ips parsing: `show all allowed-ips` separates a peer's addresses with
spaces, not commas, so peers with more than one allowed IP were shaped on their first
address only and the rest ran unlimited.
- collapse the three duplicated /32-in-CIDR filters into one tokenizer shared by the
allowed-ips, dump and pretty-print parsers.
config.rs:
- add amnezia.wg_binaries with serde default, validated as bare command names
(non-empty, no whitespace, no path separators) since they execute inside the
container. Existing deployed configs keep parsing unchanged.
- remove host wg from the required-tool preflight. It gated daemon startup on
wireguard-tools for a code path that produced nothing.
status.rs:
- discover peers once instead of twice per status call and degrade gracefully like the
neighbouring sections instead of aborting the whole output.
Tests: real AWG 3.0 / AWG 2.0 / plain WireGuard interface-line fixtures, AWG 3.0 range
keepalive, space-separated allowed-ips, binary resolution order, and a test that parses
and validates the shipped config.example.toml.
Добавлен отдельный host-level модуль sysctl_tuning без легаси-подходов: managed sysctl файл, точечный reconcile через sysctl -w только для drifted ключей, режимы tcp_bbr off/auto/require, безопасное удаление только managed-файла и атомарная запись конфигурации.
Интегрированы новые CLI-команды apply-network-tuning/inspect-network-tuning, startup reconcile в daemon и секция Network tuning в status. Расширены config/defaults/валидация, обновлен пример конфига и документация. Добавлены unit-тесты для config и sysctl_tuning.
- Полностью переписан swap-модуль в режиме exclusive ownership\n- Добавлены verify/checks, статус фактического swap-state и drift\n- Интегрирован ensure-swap в daemon и systemd ExecStartPre\n- Усилен lock API (таймауты) и применение в traffic/firewall/swap\n- Усилен parser firewall (JSON-first + устойчивый text fallback)\n- Обновлены config validation, runtime tools и default priority\n- Синхронизированы docs и build/package hygiene\n- Удалены member Cargo.lock в пользу корневого lockfile
Добавлен package-режим в tools/build.sh: --package, --dist-dir, --version. Реализована сборка Debian-пакета, metadata/control, postinst/postrm и SHA256SUMS. Установочные пути унифицированы под /usr/sbin и /lib/systemd/system для production-модели.