Releases: pereljon/dynavlan
Release list
v0.4.11
Consolidates the pre-1.0 gate-4 review hardening (0.4.3 through 0.4.11) into one release. Hardware-validated console-backed on the reference Protectli box (build b0317b6, 14/14 checks PASS): build-identity gate, M8 status dep-gate, H5 box-wide lease timing, M5 pending-delete drain, M6 restart-failure seen-set, P2 kill-mid-apply FIFO-EOF revert, and the L3-30 carrier-pull prune + re-add.
Highlights (full detail in CHANGELOG):
- P3: order-independent post-apply health check for equal-metric multi-uplink boxes
- M8:
--statusreportsunavailable+ non-zero when a detection tool is missing (no false "no trunk") - M6: a total restart failure no longer marks a new subnet "seen" (retries next run; a partial success consumes)
- M5: a failed post-accept
ip link deleteis recorded and retried, not orphaned until reboot - M4: additions are dropped on a trunk that lost carrier in the detection->apply window
- H5: the post-accept lease wait is one box-wide deadline, not per-VLAN in series
- H1: a failed capture method can no longer authorize a boot detection-diff removal
- M7:
VLAN_LIMIT_MODE=fillkeeps the lowest numeric IDs - H6/M1: interface-name contract (IFNAMSIZ 15-char limit; refuse dotted parents; injective interface key)
- Removed the reserved-but-unimplemented
PER_VLAN_MACkey (postinstmigrates old conffiles) - Packaging/CI: Debian
prermaborts on a lock timeout; the APT-repo rebuild no longer silently drops packages
v0.4.2
Pre-freeze hardening from the v0.4 adversarial review, plus earlier cheap fixes.
Fixed
- Apply-evidence (C2): removal-only /
--reapplyno longer accept anetplan tryon elapsed time. No-addition changes have no in-kernel signal, so they now use a larger bounded settle floor (APPLY_NOEVIDENCE_SETTLE) sized so the first health sample lands post-apply; netplan try's exit is logged on both paths. Hardware-validated (accept at 18.19s on a genuine carrier-down removal). - Config isolation (H2):
/etc/dynavlan.confis now parsed declaratively against an allowlist, neversourced — a config line can no longer execute code or override an internal/safety variable (NETPLAN_FILE,TRY_TIMEOUT,ver,PATH, ...). Unknown/protected keys refuse the run. - Routed-mode default-route guard (C1): dynavlan refuses a reconcile that would remove the interface currently serving the default route (routed mode), before any disk change — preventing an unrevertable strand. No-op at the default
VLAN_ROUTES=false. --dry-runexit code (M3): now exits non-zero when validation fails.- Debian upgrade timer-state (H3): upgrades preserve the operator's enabled/active timer state instead of re-arming a deliberately-disabled timer.
- Version binding (H7):
build-deb.shrefuses to build when the requested version disagrees with the script'sver=.
Added
- Public APT repository for
apt-based install/upgrade.
Full detail: CHANGELOG.md.
v0.4.0
Added
- Carrier-down VLAN removal (FR-41). When an owned trunk's physical link stays carrier-down across a two-sample debounce (
BOOT_SETTLE_SECONDSapart) - at boot or on the rescan timer - and the box still has a healthy default route, dynavlan removes all owned VLANs on that trunk through the standard validated + health-gated + auto-reverting apply chain. A carrier-down port cannot pass frames, so this is a full teardown of the trunk's set, not a per-VLAN detection diff. Independent ofRESET_ON_BOOT: governed solely by new config keyREMOVE_ON_CARRIER_LOSS(defaulttrue), so aRESET_ON_BOOT=falsebox still self-heals a dead trunk. Rescan (previously strictly add-only) gains its first removal path; the settle sleep is incurred only when an owned trunk is actually carrier-down, so the steady-state fast path is unchanged. Narrows the prior preservation invariant: preservation now protects only genuine detection uncertainty (carrier-up, no tags) - carrier-down is authoritative kernel evidence and is no longer blanket-preserved.--dry-runpreviews a would-be teardown;--statusreports carrier state per owned trunk. - New pure helpers
carrier_removals(the debounce decision) andhave_routing(the routing pre-condition).have_routingrequires a lowest-metric default route, carrier on its egress iface, AND carrier on at least one physical NIC, so a dead trunk that doubles as the box's own uplink with no redundant route is preserved rather than attempted. - Public APT repository (GitHub Pages + reprepro): this re-tag republishes v0.4.0 to trigger the new
publish-aptCI job for the first time - no dynavlan script change,ver=stays 0.4.0.
Hardware validation (2026-08-04)
All 6 sub-cases PASS on the Protectli box, console-driven: carrier-pull prune (rescan), re-plug re-add, flap-shorter-than-settle preserve, own-uplink-with-no-redundant-route preserve, --boot prune, post-boot re-add. Full detail in dev/features/dynavlan-tests.md (L3-30).
Full changelog: https://github.com/pereljon/dynavlan/blob/main/CHANGELOG.md#040---2026-08-04
v0.3.0 - restart on newly-appeared IPv4 subnet (FR-40)
Restart on a newly-appeared IPv4 subnet (FR-40)
dynavlan now restarts the nominated RESTART_SNAPS/RESTART_SERVICES whenever a new global-IPv4 subnet appears on any interface, not only on a tagged-VLAN change. This closes two gaps:
- An access port or native-only trunk that leases after boot (no tagged VLAN, so nothing was changing before).
- The monitoring agent starting before the base interface finishes DHCP at boot (dynavlan's boot run is deliberately not ordered after
network-online.target).
Both are the same underlying problem: the agent cares about IPv4 subnets it can scan, not VLAN tags.
Behavior
- New pure helper
ipv4_networkkeys subnet tokens on the network address, not the host address, so a same-pool DHCP renewal never restarts. - A monotonic per-uptime seen-set at
/run/dynavlan/seen(wiped on reboot): a flap back onto an already-seen subnet doesn't restart; an empty seen-set at boot restarts once after settle regardless of boot-vs-DHCP ordering. - At most one restart per run, deduped against any VLAN-driven restart.
--dry-run/--statusreport the would-restart delta without restarting or writing the seen-set.apply_change(the accept/rollback chain) is unchanged; the growth-check is an additive post-step inmain.
Config
- New key
RESTART_ON_NEW_SUBNET(defaulttrue). No-op when no restart targets are configured.
Validation
- Unit suite 139/0; per-task, architect, and whole-branch reviews clean.
- Hardware-validated on a Protectli box with the real Domotz snap: access-port and boot-race restarts, same-subnet/flap/disappearance no-restart, and read-only
--dry-run/--status, across UniFi and Meraki.
Full detail in CHANGELOG.md and docs/dynavlan-PRD.md (FR-40).
v0.2.1
What's new in 0.2.0
All-trunks provisioner redesign. dynavlan now provisions VLANs on every carrier-up trunk with detected tags, not one selected trunk. A box with two live trunks gets both provisioned in one unified netplan try. The universal token is now iface.id (e.g. enp1s0.100), so two trunks sharing the same VLAN id provision as distinct interfaces.
--reapply (FR-39). Regenerate and apply the current VLAN set with the running build's config. Use after an upgrade or a config change like VLAN_ROUTES=true.
--version / -V and build identity (FR-38). Every install is stamped with its git commit. Works unprivileged and with a broken config.
Opt-in routed mode (FR-37). VLAN_ROUTES=true accepts DHCP routes at per-VLAN metrics. DNS/NTP stay declined.
Hardware-validated 2026-07-30 on a Protectli/igb dual-trunk appliance with a Meraki switch: dual leasing (L3-29), carrier-pull preserve (L3-30), routed multi-trunk (L3-31), and unified revert (L3-32) all PASS.
0.2.1 fix
backend_list_managed_vlansawk assumedlink:beforeid:innetplan getoutput; netplan 0.107 emitsid:first. Found in code review; 3 tests added.
See CHANGELOG.md for the full list of changes, removals, and fixes.