The automated -tags e2e suite runs in netns/netem emulation. This checklist is
the manual counterpart, run on the real deployment (Starlink + 5G edge box and a
concentrator VPS with a public IP). Each phase appends its own section; run the
phase's section after that phase lands. Record date, build (wanbond version),
and observed numbers next to each item.
Prerequisites (all phases):
- Edge box has both uplinks up; router pins source IP A → Starlink, source IP B → 5G (path selection is external to wanbond).
- Concentrator reachable on its public IP; UDP not blocked end to end.
-
wanbondrunning both ends from a0600config;/metricsreachable on localhost each end. - With Amnezia obfuscation enabled, run
(cd third_party/amneziawg-go && go test -race ./device/awg -run '^TestJunkCreatorConcurrentUse$' -count=5); simultaneous peer handshakes report no shared ChaCha8 access.
- Tunnel comes up edge ↔ concentrator (WG handshake completes).
-
pingand a TCP transfer pass through the tunnel. - Record single-path baseline throughput per uplink (iperf3).
- Optional: measure per-link bandwidth and RTT for pacing tuning (see install.md §3a if you plan to enable pacing).
The manual items above are automated end to end by the just p0-baseline
pre-pilot procedure against the two standing worker machines — see
§P0 — automated real-link baseline
below. Run that command to capture the baseline report, then read/interpret the
numbers by hand; the baseline is INFORMATIONAL (report-only), not a pass/fail
gate.
- Start a long-lived TCP flow (SSH session or iperf3) over the tunnel.
- Physically drop the active WAN (unplug / disable the Starlink uplink).
- Flow survives with NO reset; throughput restored within
P1RecoverySeconds. - Restore the WAN; no thrash. Repeat for the other uplink.
- Change the edge public IP on one path (carrier re-address); flow survives.
- Under saturating load, bonded throughput ≥
P2BondedMinFraction× (sum of per-path throughputs), read from/metrics. - While Starlink is healthy and load fits, 5G bytes <
P2MeteredMaxByteFractionof total (/metrics).
- Induce loss on a path; at the configured parity ratio, ≥
P3MinRecoveredFractionof lost DATA frames recovered without retransmit. - FEC overhead ≤
P3MaxOverheadFactor× parity ratio (/metrics).
- Run
go test ./internal/bind ./internal/reseq ./internal/telemetry -run 'TestAdaptiveControllerUsesAuthenticatedActiveCarrierDataLoss|TestAdaptiveControllerIgnoresSingleCarrierFeedbackWhileWeighted|TestDataLossFeedback|TestFeedbackOnly|TestMixedVersionFeedbackCadencePreservesBaseRecoveryWindow|TestLossObserverReportsExactFinalizedSequenceRange|TestProbePayload' -count=1and repeat with-race. This pins final-gap raising, inferred parity-recovered loss, exact outcome conservation, carrier-transition non-influence for final and recovered prior-epoch outcomes, clean dwell/shedding, stale/replay/identity rejection, per-peer isolation, bidirectional legacy recovery negotiation under ordered and reordered delivery, feedback-only evidence preservation and exact priority accounting, and the weighted multi-carrier boundary. - Run
sudo -E go test -tags e2e -run '^TestP4AdaptiveFEC$' -v ./test/e2eon the slowest supported worker (including a 1-vCPU aarch64 host); each 60-second phase must collect at least 2,000 DATA frames. The iperf client has a 90-second wall-clock limit (60-second transmit interval plus 30-second control/result grace). Fixture setup waits for the holder's network-namespace identity to differ from the test process before configuring loopback; a readiness failure terminates and reaps that holder while reporting its PID, argv, and process state. A later phase timeout reports both daemon logs instead of leaving the phase active indefinitely. - Under steady
P4SteadyLossRatepath loss, adaptive total overhead ≤ the fixed-FEC baseline for equal masking. - Post-recovery residual loss ≤
P4ResidualLossMax(/metrics). - On an active-backup real link, perform two independent loaded 30-second
cycles from a clean
M=0baseline. For each cycle recordwanbond_path_loss_ratio,wanbond_fec_eligible_path_loss,wanbond_fec_adaptive_parity,wanbond_fec_recovered_packets_total, andwanbond_fec_residual_loss_ratio: bounded active-carrier DATA loss must make eligible loss and M non-zero; a subsequently missing DATA frame must increment recovery without TCP-equivalent residual loss; after clearing the impairment, M must return to zero only after the configured controller dwell. Preserve both metric captures with the commit/config hashes.
- From a hostile-ish network (e.g. a hotel/guest Wi-Fi), the tunnel connects.
- Capture the flow; nDPI / Suricata do not classify it as WireGuard or any identified VPN.
The single, repeatable pre-pilot procedure that replaces the manual §P0 steps
above. It is a thin orchestration layer over the existing realhosts tier (which
drives the two standing worker machines over SSH: the amd64 edge behind symmetric
NAT ↔ the aarch64 concentrator on its public IP) — it does NOT re-implement any
test logic. One command provisions both ends, natively builds wanbond on each,
brings the tunnel up over the real internet path, runs the aggregation +
loaded-RTT + link/hub-failover smoke, and TEES a timestamped baseline report.
REPORT-ONLY / NON-BLOCKING (Q19): the orchestrated tests assert liveness only
(handshake completed, both paths reached up, every iperf3 sample returned a
positive rate, failover recovered) — no Mbit/s or ms threshold gates the run.
The emitted numbers are informational input to the operator's pilot-gate
decision, which stays a human judgement call, not an automated gate.
-
From the dev shell (
nix develop) at the repo root, run:``` WANBOND_SSH_KEY=/run/agenix/llm-ssh-key just p0-baseline ``` `WANBOND_SSH_KEY` defaults to `/run/agenix/llm-ssh-key`, so on a host where that key is already in place `just p0-baseline` alone suffices. Host addresses/public IP default to the two standing workers and can be overridden with `WANBOND_EDGE_HOST` / `WANBOND_CONC_HOST` / `WANBOND_CONC_PUBLIP`. No root is required. The command is NEVER part of `just test` or CI. -
The command orchestrates these EXISTING tests (
go test -tags realhosts -run '^(TestRealP0Smoke|TestRealAggregationBufferbloat|TestRealMidTransferWANKill)$' -v) and tees the full-voutput totest/realhosts/reports/p0-baseline-<UTC-timestamp>.log(gitignored). A non-zero exit means the run itself could not complete (a host was unreachable or the tunnel never came up) — NOT that a performance number missed a target.
-
TestRealP0Smoke— single-uplink bring-up: WG handshake OK, ping avg RTT (ms), and three iperf3 measurements (single-flow TCP Mbit/s + retransmits, 8×-parallel TCP Mbit/s + retransmits, UDP goodput/loss/jitter). See the=== P0 SMOKE RESULTS ===block. -
TestRealAggregationBufferbloat— per-path and bonded throughput and their aggregation ratio, plus the idle-vs-loaded RTT (bufferbloat) delta measured with a ping running inside a saturating transfer. -
TestRealMidTransferWANKill— mid-transfer LINK-failover and HUB-failover (T57) recovery: the observed gap/switch timings before the flow resumes over the surviving link / standby concentrator.
- Reading and interpreting the numbers. The command emits measurements; a human decides whether the aggregation ratio, loaded-RTT delta, and failover gaps look acceptable for the intended pilot.
- The pilot-gate decision itself is a NON-BLOCKING human call. The baseline informs it; it does not automate or gate it. Record the report path, date, and the go/no-go decision alongside the numbers.
- Exit criterion (Q19): the capped-fixture functional
impairment/counter check (netns,
TestFixtureImpairment, W2; no throughput or loaded-RTT claim) PLUS this report-only real-link baseline (just p0-baseline, W4) are SUFFICIENT to proceed to a SUPERVISED pilot. The longer soak runs DURING the pilot, NOT as a pre-gate. Full statement: runbook.md §7 Pilot exit criterion.
Scripted counterpart of the P1 section above for the real deployment. Install
per docs/install.md first (binary at /usr/local/bin/wanbond, 0600 configs,
systemd units enabled, concentrator tunnel-interface firewall ACCEPT in place).
Inner addresses below assume concentrator 10.77.0.1, edge 10.77.0.2; adjust
to your allowed_ips. Record date, wanbond version output, and observed
numbers next to each item.
- Concentrator:
systemctl start wanbond-concentrator, thensystemctl status wanbond-concentratorshows active andjournalctl -u wanbond-concentrator -n 20showstunnel interface up. - Concentrator firewall ordering verified:
iptables -S INPUTlists-i wanbond0 -j ACCEPTBEFORE any-j REJECTline (OCI default-REJECT caveat, docs/install.md §5) and a UDP ACCEPT for the listen port. - Edge:
systemctl start wanbond-edge; status active; journal showstunnel interface upwith both paths. - Handshake: edge
ping -c 3 10.77.0.1succeeds. - TCP through the tunnel: concentrator
iperf3 -s -B 10.77.0.1; edgeiperf3 -c 10.77.0.1 -t 5completes (guards the firewall caveat — if ping passes but iperf3 fails with "No route to host", the REJECT rule is ahead of the tunnel ACCEPT). - Both paths live: edge
curl -s http://127.0.0.1:9090/metrics | grep wanbond_pathshows starlink and 5g.
- Start the long-lived flow: edge
iperf3 -c 10.77.0.1 -t 120(or an interactive SSH session to 10.77.0.1) and, in a second terminal,ping -i 0.2 10.77.0.1. - Physically drop Starlink (unplug its ethernet/PoE — a real link drop,
not
ip link set down). - Flow survives with NO reset; ping gap and iperf3 stall ≤
P1RecoverySeconds(3 s). Record the observed gap. - Restore Starlink; wait ~30 s; journal shows the path recovering with no up/down thrash (no repeated failover lines).
- Repeat the block above dropping the 5G uplink (pull the modem's power or antenna). Same acceptance: no reset, gap ≤ 3 s, clean recovery.
- With the flow running, force a public-IP change on one path (5G: toggle
airplane mode /
mmcli -m 0 --simple-disconnect && --simple-connect; or power-cycle the Starlink router if it re-NATs). The edge's outbound source may also be changed at the router NAT. - Flow survives; concentrator journal shows the path's endpoint roaming to the new address; ping gap ≤ 3 s.
Distinct from the per-uplink drops above: here the concentrator is lost, so NO
uplink can reach it and the edge must move to a STANDBY concentrator. Requires a
SECOND concentrator VPS reachable from the edge, sharing the peer's SAME WireGuard
static key (the standby presents the same peer identity). Configure the edge peer
with an ORDERED list — endpoints = ["<hubA ip:port>", "<hubB ip:port>"] (index 0
= hubA active, hubB standby); IP:port only, no hostnames.
- Bring the tunnel up; confirm traffic flows via hubA (
ping -i 0.2 10.77.0.1steady; hubA journal shows the handshake + the edge endpoint learned). - Make hubA fully unreachable from the edge — stop
wanbond-concentratoron hubA, OR block itslisten_portat hubA's firewall (a REAL hub outage, so every path's liveness to hubA goes DOWN together, not just one uplink). - Within the hub-failover budget (all-paths-DOWN detection ≈
DownAfter+ thehubFailoverSettle3 s dwell), the edge journal shows ahub failover: all paths to active concentrator down; switched endpointline advancing to hubB, and hubB's journal shows a FRESH handshake (a new session — no hub-to-hub state handoff). Record the observed gap. - The flow re-establishes over hubB. (A long-lived TCP flow tied to the old session resets — a fresh session is deliberate; a NEW flow, or ping, resumes.)
- Single-concentrator GUARD: with the edge configured with only ONE endpoint
(legacy single
endpoint, or a one-elementendpoints), repeat the hub outage — the edge must take NO failover action (nohub failoverjournal line, no endpoint switch); behaviour is identical to pre-T57. Recovery happens only when hubA itself returns.
- Bring one uplink's interface DOWN (so its configured
source_addris not held by any interface), thensystemctl restart wanbond-edge. The daemon comes up instead of crash-looping: journal shows the tunnel bound on the surviving uplink and the absent path recorded as deferred /Down; a NEW flow passes end to end over the survivor. Then bring the interface back UP WITHOUT restarting: the background reconcile (T55) re-binds and promotes the deferred path automatically within ~1 s (DefaultReconcileInterval), with norestart— and both paths then carry traffic. - With EVERY uplink's
source_addrabsent,systemctl restart wanbond-edgeFAILS fast (journal shows a fatal "no configured path could bind" and the unit entersfailed/ restart-loops) — no transport means no tunnel. - A MALFORMED
source_addrin the config still fails at config load with a validation error, distinct from the tolerated not-yet-assignable case.
-
systemctl reload wanbond-edge(SIGHUP) with an unchanged config is a no-op: journal logsconfig reloaded, tunnel stays up, flow unaffected. -
systemctl restart wanbond-edgerecovers the tunnel within seconds; a NEW flow passes end to end afterwards.
Scripted counterpart of the P2 summary above for the real deployment. Requires the
P1 setup already validated (both uplinks up, both daemons running from 0600
configs, /metrics reachable on 127.0.0.1:9090 each end) AND the edge configured
with the weighted-aggregation scheduler so bonding engages under load:
[scheduler]
policy = "weighted"
# per_path_capacity_fps sizes the aggregation gate to ~one uplink's WIRE frame
# rate (data + any FEC parity); tune it to the slower uplink's sustained wire
# frame rate (bytes/s ÷ on-wire frame bytes, install.md §3a).Inner addresses assume concentrator 10.77.0.1, edge 10.77.0.2. THRU() below is
curl -s http://127.0.0.1:9090/metrics | grep wanbond_path_throughput; TX(path) is
... | grep wanbond_path_tx_bytes_total | grep <path>. Record date, wanbond version,
and observed numbers.
- Record each uplink's SOLO saturated throughput: bring the tunnel up with only
Starlink configured, run
iperf3 -c 10.77.0.1 -t 20, and read the Starlinkwanbond_path_throughput_bits_per_secondfrom/metrics. Repeat with only 5G. RecordT_starlinkandT_5g(Mbit/s, from/metrics).
- Bring the tunnel up with BOTH uplinks. Start a saturating flow:
concentrator
iperf3 -s -B 10.77.0.1; edgeiperf3 -c 10.77.0.1 -t 30. - Mid-flow, read BOTH paths'
wanbond_path_throughput_bits_per_secondfrom the edge/metricsand sum them:T_bonded. Confirm both paths are non-zero (aggregation engaged, not single-path fallback). - Cross-check the far end: the concentrator
/metricsshowswanbond_path_rx_bytes_totalclimbing on BOTH paths. - Assert
T_bonded ≥ P2BondedMinFraction × (T_starlink + T_5g)(0.85). Record the measured fraction.
- With both uplinks healthy, run a SUB-capacity flow that fits Starlink alone
(e.g. edge
iperf3 -c 10.77.0.1 -t 30 -b <~40% of T_starlink>). - Sample
wanbond_path_tx_bytes_totalfor both paths at the start and end of the flow; take the DELTA over the window. - Assert the 5G delta is
< P2MeteredMaxByteFraction × (Starlink + 5G deltas)(0.01) — the metered uplink carried effectively no bytes. Record the fraction. - Confirm
wanbond_path_up{path="starlink"}was1throughout (the thrift guarantee is conditioned on a healthy primary).
- Stop the saturating flow; within a few seconds the edge journal /
/metricsshow egress collapsing back to Starlink only (5G tx flat again). -
systemctl reload wanbond-edgeafter changing[metrics] listen: journal logsmetrics endpoint rebound; the new address serves/metrics, the old one stops; the tunnel and any running flow are unaffected.
Scripted counterpart of the P5 summary above for the real deployment. This is the
manual, real-link mirror of the automated TestP5DPI (netns) check: it confirms that
on a real access network the obfuscated wanbond flow is not classified as WireGuard
or any identified VPN by nDPI or Suricata, and it exercises the UDP-block limitation
(docs/install.md §8) as an understood failure mode — not a wanbond defect. Requires the
P1 setup validated (both daemons up from 0600 configs) AND an [amnezia] obfuscation
block set IDENTICALLY on both ends (obfuscation ON — plain WireGuard is trivially
classified and is NOT what ships). Run the capture from a realistic hostile-ish
network (hotel / guest / captive-portal Wi-Fi, or a lab uplink with a DPI appliance
in path). Install ndpi (ndpiReader) and suricata on the capture host. Record
date, wanbond version, the access-network description, and each tool's verdict.
- On the capture host, run the shipped positive-control capture through nDPI:
ndpiReader -i test/e2e/testdata/plain-wireguard.pcapand confirm the Detected protocols block lists WireGuard (and category VPN). If it does NOT, the tool/parse is broken and every "not classified" result below is vacuous — fix the tooling before trusting the negative checks. - (Informational) Run the same capture through Suricata
(
suricata -r test/e2e/testdata/plain-wireguard.pcap -l ./sur-pos -k none) and note whethereve.jsonreportsapp_proto: wireguard. The stock Suricata config ships no WireGuard app-layer parser, sofailed/unknownhere is EXPECTED — nDPI carries the WireGuard-specific positive control; Suricata provides the app-layer/anomaly negative check.
- From the hostile-ish network, bring the tunnel up (edge
systemctl start wanbond-edge); confirm handshake: edgeping -c 3 10.77.0.1succeeds. If the network blocks UDP wholesale, the handshake will NOT complete — see the UDP-block step below; that is the documented limitation, not a bug. - Capture the outer WAN UDP while driving representative traffic (a bulk transfer
+ interactive traffic for ~30 s): on the edge uplink interface,
tcpdump -i <wan-if> -n -p -U -w wanbond.pcap 'udp port 51820'(adjust the port to yourwireguard.listen_port/ concentrator endpoint). Confirmwanbond.pcapis non-empty.
-
ndpiReader -v 2 -i wanbond.pcap; on the per-flow line for the wanbond flow the[Confidence: …]field is NOT a payload/content match to WireGuard/VPN — a[proto: …/Unknown](or QUIC/DNS/etc.) is fine. Ignore aConfidence: Match by port"WireGuard/VPN" label if you captured on port 51820 — that is a port guess, not a payload classification (see docs/install.md §Limitations); to remove the ambiguity, deploy/capture on a non-registered UDP port so nDPI cannot port-guess. A WireGuard/VPN label withConfidence: DPI(a PAYLOAD match) is a requirement-6 DEFECT — file it; do not rationalise it away.
-
suricata -r wanbond.pcap -l ./sur-neg -k none; inspect./sur-neg/eve.json: no flow'sapp_protoand noalert.signature/alert.categorynames WireGuard or a VPN. Record the observedapp_proto(expected:failed/unknown). A WireGuard/VPN app-proto or alert is a requirement-6 DEFECT.
- On a network (or a test firewall rule) that blocks UDP wholesale, confirm the
tunnel FAILS to connect (no handshake,
ping 10.77.0.1fails) and the edge journal shows only outbound handshake attempts with no response. Confirm this is the EXPECTED behaviour: wanbond has no TCP/TLS fallback transport (explicit non-goal, docs/install.md §8). Record that the flow does not silently downgrade to an unobfuscated or plaintext fallback (there is none — it simply does not connect). - Where a UDP-allowing network is available again, confirm the tunnel reconnects once UDP egress is restored (no manual intervention beyond the network change).
Defect D65 identifies bufferbloat (standing queue build-up under sustained load)
on last-mile links (observed on Starlink, D65) that caps single-flow TCP throughput
at ~3.67 Mbps against a link independently capable of ≥6.9 Mbps. Pacing
(bounded per-(peer,path) exact-byte shapers, enabled via
[scheduler] pacing_enabled = true) applies backpressure instead of
pacer-induced loss, bounds the queue so loaded RTT stays near idle baseline,
and lets single-flow TCP use learned delivered capacity. link_bandwidth
supplies the initial active-backup seed; only an explicit
link_bandwidth_limit supplies a safety ceiling. This section
validates pacing's effectiveness on the real deployment
(Pi4-edge/Starlink/o3 topology).
IMPORTANT: The netns/e2e fixture test suite MUST NOT assert absolute
throughput thresholds (e.g., "single-flow TCP ≥ X Mbps"). This is a
manual/real-host tier validation only, because the netns fixture is
CPU-bound and cannot build the standing queues pacing is designed to prevent.
Absolute throughput assertions belong in this section (real links), never in
automated netns e2e tests. TestFixtureImpairment and any similar capped-fixture
netns tests remain throughput-measurement report-only (informational) with
no pass/fail gate on absolute numbers (see design.md pacing section).
- Edge and concentrator both have the tunnel up with both uplinks alive (per §P1
setup). Both daemons running from
0600configs,/metricsreachable on127.0.0.1:9090each end. - Record date,
wanbond versionoutput, current build (git log --oneline -1). - Measure and record the idle RTT and measured throughput per uplink
(instructions in install.md §3a);
these set the initial pacing seed/budget
(
link_bandwidth/link_rtt), not an absolute-goodput floor. Record any explicitlink_bandwidth_limitseparately.
This is the current Q91 acceptance and supersedes T304's fixed 8Mbit/5Mbit
condition matrix and absolute 5.6Mbit clause below. Retain the older section
as historical exact-byte-shaper evidence.
- Before deployment, run the non-privileged gate and
nix build. On an ARM64 Linux host, runTestLinuxTUNAQMReconciliationContractandTestLinuxTUNAQMNativeTUNFullMTUHandoffContract: the in-memory dummy and real privileged Linux adapter must pass the same topology/rate/queue/drift-restoration contract, and the native TUN must prove zero-drop full-MTU transient handoff and visible finite-overload placement. - Record both endpoint commit IDs, binary SHA-256 hashes, config hashes,
service states, TUN link details,
tc -j qdisc,tc class, and a full/metricsscrape before the candidate starts. - On both edge and concentrator, confirm active-backup+pacing starts only
after exact readback: HTB root and one
bfifochild withL=limit=max(P*U,ceil(R*Q))bytes, wherePis the peer count,U=gso_max_size,Q=20ms, andRis aggregate ingress. Confirm its persistent service bound is at mostmax(Q,P*U/R). With nominal complete-batch service budgetD=20ms, per-peer rater, maximum WireGuard datagramW=Mmax+32, conservatively rounded complete-batch service timeT<=max(D,W/r), current/maximum MTUsMcur/Mmax, and exact installed burstG, calculateA=ceil(R*T)+G,H=ceil(A/Mcur), andJ=ceil(A/20). Confirmwanbond0 txqueuelen>=J+1; a larger observed baseline is promoted to the target and retained as the live-interface high-water. Separately confirm per-peer engine admissionB+C, whereBis the maximum current active outer-shaper DATA budget andCis the complete GSO batch below. Confirm HTBburst=cburst=G>=gso_max_sizeand exact target/actual burst, epoch, rate, ring capacity, and queue-limit readback.wanbond_tun_aqm_actual_freshmust be 1. Record((H+1)*Mmax+L)/R; conditional on theT<=max(D,W/r)reader-service precondition, this full-MTU-valued transient handoff plus byte-leaf service bound must remain sub-second. Do not value allJslots atMmax: an arbitrary reader stall lies outside this invariant. - Restart each endpoint with no offered inner traffic. Startup must install and expose fresh TUN-AQM target/actual metrics without waiting for a TUN packet; an idle engine read must not block ptr-ring occupancy readback.
- From per-peer ingress rate
r, maximum configured inner MTUMmax, and current inner MTUMcur, calculateS=max(1,min(128,floor(65536/Mcur),floor(r*20ms/(Mmax+32)))). Confirm the link target/actual GSO readback is exactlygso_max_segs=S,gso_max_size=S*Mcur, and the per-peer engine byte limit isB+S*(Mmax+32). The complete batchC=S*(Mmax+32)must have service time no greater than 20 ms wheneverfloor(r*20ms/(Mmax+32))>=1. Below that threshold, confirmS=1and conservatively roundedT=(Mmax+32)/r>20ms; the additionalBpreserves one ACK-clocked BDP. - Run two independent synchronized field cycles. Each cycle contains
one 30-second Pi→o3 TCP upload and one 30-second o3→Pi TCP download
(
iperf3 -Rfrom the Pi). For each leg, start timestamped inner and outer pings before traffic; scrape controller/TUN-AQM/engine/shaper/FEC/ resequencer metrics plustc -sandip -s linkatt=0, at least once per second, and at leg end. Preserve iperf JSON with one-second intervals and retransmits. Quiesce traffic between legs so each direction receives an unambiguous boundary. - In every leg,
wanbond_path_congestion_outer_wire_bytes_totaladvances by actual successful outer IP+UDP/frame bytes, whileinner_data_bytes_totaladvances only for native DATA. Reconcile their ratio with FEC/control/probe overhead; do not infer capacity from inner payload alone. - Base RTT and queue delay reset on the first A→B or B→A carrier generation. Carrier epochs increase monotonically; delayed/stale/replayed/wrong-peer DATA-loss feedback never changes the new epoch's target. Once current authenticated feedback has been adopted, stale feedback cannot raise the target or cause a loss-based decrease; only current local queue delay may still drive a fail-closed decrease after continuous qualification.
- Queue delay at least
max(base RTT/2,10ms)+4*wanbond_path_jitter_secondsunder continuous load for one elapsed second reduces the target. A newly accepted fresh authenticated DATA-loss report of at least 0.5% also reduces it exactly once, even when the report arrives after a later unloaded byte interval; adjacent above-threshold reports from the same loss episode must hold the target. One continuous second of fresh below-threshold evidence must rearm a later episode. Stale evidence, counter regression, and pending settlement caused by an outer-rate change must break that clean dwell without clearing the current episode. Ingress-only expansion or headroom settlement must not break it: after one loss response, vary expansion while supplying fresh zero-loss feedback for one second and confirm outer additive recovery resumes. Repeat with pressure preempting the outer retarget; after exact combined-target readback settles and the next headroom-only change, clean dwell must resume. Fresh loss received on the carrier-initialization sample must remain actionable on the next observation. Fresh loss received while counters regress or another target settles must remain pending and apply one decrease once a valid observation or settlement permits a retarget. RTTVAR and dwell qualify only delay and must not suppress the first eligible fresh-loss response; sustained queue delay may still reduce during an active loss episode. A no-loss variable-RTT trace whose delay remains below that threshold must not reduce the target. A single crossing followed by a below-threshold or unloaded observation must not reduce it; counter regression, carrier transition, and pending retarget settlement must also reset the dwell. Sustained delay must reduce after the continuous second. One congested decision multiplies the prior target by exactly 0.85; a partially offered/emitted interval must not impose a second delivery-derived cap. Clean emitted service permits bounded additive increase above the seed. Withlink_bandwidth_limitset, the target never exceeds that explicit ceiling; with it omitted, confirm a clean loaded target can grow beyondlink_bandwidth. Confirm the TUN target follows the learned outer/inner ratio. After first driving the ratio above 2.5, reduce actual expansion to 1.1 while native DATA saturates the installed ingress target but outer service remains below half its target; the ratio must decline and the ingress target must rise. Exact kernel readback follows the target with an in-placebfifochange: packet/drop counters must remain monotonic and a correct live leaf must never be deleted/re-added. The first eligible fresh authenticated-loss tick must decrease promptly; a discontinuity or pending target must retain it until the next valid, settled tick. Before another target change,retarget_pendingmust remain 1 untilinstalled_fresh=1for the same rate/epoch and at leastmax(1s, active base RTT)has elapsed;target_changesmust not advance during that wait. Repeated exact readbacks must not restart the interval: under sustained queue-delay congestion, the next delay decision occurs promptly after its original deadline. Adjacent loss reports from the same episode continue to hold. A stale/mismatched readback re-arms the interval, and a carrier-epoch transition cancels the prior wait. - Confirm ingress-only local service headroom starts at 0.95 without
changing the outer target. A loaded interval whose per-controller engine
admission wait occupies at least half the interval must multiply
headroom by 0.85 before link drops rise. Ptr-ring occupancy remains
diagnostic: loaded ring occupancy with zero or sub-threshold admission
wait, and every unloaded/opposite-direction interval, must not reduce
headroom. Unchanged cumulative counters or a non-advancing interval must
not replay the
decrease. The first loaded local-pressure decrease may preempt settlement
for an unrelated outer-capacity retarget; repeated pressure must wait for
exact ingress rate/epoch readback and its own
max(1s, active base RTT)settlement. Recovery remains settlement-gated. Recovery adds 0.01 only after three consecutive loaded, clean, settled intervals. Idle intervals do not recover. A carrier epoch resets headroom to 0.95. - During a capacity shrink,
rate_freshmay become 1 whileactual_fresh=0only when a corresponding queue-limit, GSO-limit, or engine-admission deferred gauge is 1. The installed queue limit must remain at least the desired limit until byte backlog fits; GSO shrink waits for zero TUN backlog, with the aggregate leaf held at its installed value and the normalized HTB burst at least the installed link GSO maximum. Engine admission must remain at its old value while GSO shrink is deferred. After exact smaller-GSO readback, attempt the engine shrink; if any peer's retained bytes do not fit, restore the old downstream capacity envelope and leave admission deferred. While that engine shrink remains deferred,target_engine_admission_limit_bytesmust show the desired bound,actual_engine_admission_limit_bytesand actual downstream capacity must remain at the installed envelope, the desired rate/epoch must apply,actual_freshmust be 0, andrate_freshmust be 1. After both queues drain, downstream capacity must read back before the engine value shrinks. The online ptr-ring target and actual must retain the maximum of their observed interface baseline, later larger readbacks, and derivedJ+1across lower rate/MTU targets, and reset only on interface recreation. On growth, capacity must read back before the engine value rises. All deferred gauges must return to zero after drain, with exact target/actual readback and no new qdisc or link drop. - Do not use
TestP1Failoverto validate the preceding transition. Its generated config omits[scheduler], so pacing defaults off and TUN AQM never starts. Use an active-backup fixture with pacing enabled and per-path shapers, then confirm the TUN AQM gauges are present before interpreting transition order. - For a drained GSO reduction, verify the smaller GSO limits write and
read back before any HTB burst or
bfiforeduction. Inject one queued packet at the GSO-write seam: the subsequent occupancy read must retain the old aggregate leaf and burst for that reconciliation. Drain it and verify a later reconciliation applies the exact desired leaf and burst. - Exercise a mixed transition from
gso_max_size/gso_max_segs=64000/64to64170/46. Despite the segment-count decrease, verify leaf and burst grow to at least 64,170 bytes before the combined GSO write. A segment-only change at fixed maximum byte size must not trigger the byte-capacity shrink sequence. - Confirm the plaintext
bfifotarget equalsmax(peerCount*gso_max_size,ceil(ingress_target*20ms))and its persistent service time does not exceedmax(20ms,peerCount*gso_max_size/ingress_target). Holding ingress rate, peer count, and GSO size fixed while doubling the engine admission window must not change the leaf limit. At 40,000 B/s with one 1,284-byte GSO quantum, read back 1,284 bytes and at most 32.1 ms, not the prior 7,200-byte/180 ms engine-derived window. - Exercise independent native handoff phases on a fresh queue:
Hfull-MTU packets andceil(A/29)minimum legal UDP-over-IPv4 TUN packets over one bounded complete-batch service interval must each leave link and qdisc drops unchanged with ring readback at leastJ+1. The unit contract must separately prove the exact 20-byte slot boundary. A deliberate overload must increase at least one captured link/qdisc counter and report the layer. Do not claim arbitrary-stall losslessness from the bounded result. - For each single ACK-clocked TCP leg, the byte-bounded
bfifoleaf incurs zero local drops and its backlog returns to zero after the leg. An offered burst no larger than the calculated service backlog must also incur zero local drops. Overload beyond the explicit queue limit may tail-drop, but every such drop must advancewanbond_tun_aqm_drops_total. Confirm that metric against the maximum leaf/root value fromtc -j -s qdisc; a non-stattc -jread is insufficient. - The reproduced failure does not recur: the peer engine queue must not
reach its 1,024-container limit,
wanbond_engine_admission_retained_bytesmust stay at or belowwanbond_engine_admission_limit_bytes, andwanbond_engine_admission_oversize_batches_totalmust stay zero after fresh GSO readback. Shaped-FEC ownership must remain covered by that same retained reservation through terminal emission; do not interpret the simultaneously visible FEC-owned bytes as an independent capacity. Record bothwanbond_engine_{encryption,peer}_queue_high_water_containersto identify the exact queue owner. Hidden inner RTT must not grow into seconds while outer RTT stays near baseline, and TUN/shaper/socket/FEC accounting must show no unexplained loss. Evaluate receiver goodput, retransmits, AQM ECN/drops, authenticated loss, and queue-delay response together. Q91 has no fixed absolute throughput floor. - Stop/restart one endpoint and confirm a fresh controller/qdisc actual state appears before service returns. Restore the original binaries and configs if the candidate will not remain deployed; otherwise record that the candidate remains installed.
Run on the RPi4 edge with Starlink active, 5G standby, active-backup policy, and adaptive FEC enabled. Preserve the starting state before the first cycle:
Before the real-link cycles, validate the T309/T318/T323 FEC sender-owner invariants:
-
Run
go test -tags failfirst ./internal/bind -run '^TestFailFirstFEC' -count=1and the same command with-race; both pass. These deterministic checks assert that open-group DATA/PARITY stays hidden, exact deadline dispatch stays within the bind-local 10ms grace, an expired group wins over queued admissions, a 257-frameSendpublishes one owner batch and preserves exact payload/sequence order, writer-prefix failure consumes no suffix sequence numbers, shaped and exclusive unshaped-recovery serial sub-K sends acknowledge owned caller buffers before terminal group service, uncontracted direct sends stay synchronous, post-ack failures retire only the exact generation, and Close rejects unowned suffixes while joining every acknowledged owner-side completion. -
Run
go test ./internal/bind -run 'TestProductionBatch128CappedShapedSerialAdmissionFillsGroups|TestFECSendStreamsBeyondOwnerMailboxCapacity|TestMultipathFECDeadlineEmitsPartialGroupParity' -count=1; the capped production batch-128 fixture, 257-buffer offload batch, and underfilled deadline group all pass. The production fixture must report exact size-closed K+M fill, zero deadline decisions/misses and terminal errors, accepted=emitted conservation, and nonzero configured-vs-observed byte-rate/count evidence. -
During one Down/Up cycle with a deliberately underfilled FEC group, capture the outer UDP stream. Confirm no DATA/PARITY from the old group appears after the old transport generation closes; the first post-Up group starts from a fresh FEC sender generation.
-
Record the edge and o3 commit IDs, binary hashes, config hashes, service states, and the original pacing declarations. The last step must restore and re-hash this exact state.
-
Use one 30-second edge-to-o3 TCP upload per tunnel leg, discarding the first 5 seconds as warmup. During the whole leg collect timestamped loaded ping, one-second
iperf3intervals/retransmits, FEC counters, and everywanbond_path_shaper_*andwanbond_engine_*series. Take synchronized counter scrapes att=0, the final-window boundaryt=10s, and the leg endt=30s(or continuously sample with timestamps at least this precisely). Compute the final-20-second outer rate and counter deltas strictly fromt=10sthrought=30s; retain thet=0scrape for whole-leg error/cancellation checks. -
Run three cycles with condition order rotated to reduce order bias:
| cycle | first | second | third | |---|---|---|---| | 1 | pacing off | `8Mbit` | `5Mbit` | | 2 | `8Mbit` | `5Mbit` | pacing off | | 3 | `5Mbit` | pacing off | `8Mbit` | Pacing-off uses the direct complete-batch path. Each paced condition sets the active Starlink `link_bandwidth` to the named cap and keeps valid declarations on every path; wait for both paths and the WG session to return UP before the 5-second warmup. -
Immediately before and after every
8Mbitor5Mbittunnel leg, stop tunnel load and measure direct Starlink upload to o3 in the same direction. Treat that bracket as a valid raw pair only when both raw measurements are at least1.03 * capandabs(raw_before-raw_after)/max(raw_before,raw_after) <= 0.15. A raw-invalid interval is inconclusive and cannot close the criterion. Repeat as needed until at least two valid raw pairs per cap remain. -
Capture
iperf3 --jsonreceiver bytes/retransmits and timestampedping -i 0.1samples so loaded RTT p95 and retransmits per GiB of receiver payload can be calculated, rather than inferred from summary averages.
For each cap, at least two raw-valid paired cycles must satisfy all of these predeclared gates:
- Over each paced leg's final 20 seconds, total outer egress — shaped emitted bytes plus successful direct outer-priority bytes — stays within ±5% of the declared cap.
- No ordinary queue loss occurs: no scheduler-shedding record appears, and
deltas for admission-canceled datagrams, terminal shaped-call errors, and
asynchronous generic/
EMSGSIZEwriter errors are all zero. - TCP receiver goodput is at least 70% of emitted DATA bytes. Reconcile DATA, FEC parity, inner control, and direct outer-priority bytes rather than treating parity as application goodput.
- Each paced loaded-RTT p95 is at most 60% of its cycle's unpaced loaded-RTT p95.
- Across the retained valid runs, median retransmits/GiB at
5Mbitare at most 1.25× median retransmits/GiB at8Mbit. - While the paced upload fills the DATA budget, confirm every configured
path remains live and
wanbond_path_probe_send_errors_totaldoes not increase. Generated authenticated PROBE/echo frames reserve retained priority capacity and use the same serialized path writer as DATA/control/recovery. The counter includes unexpected socket-write failures for locally-originated ordinary and PMTU PROBE frames: an ordinary failure is counted but not returned to a caller, while an unexpected PMTU failure is counted and returned to discovery. Expected PMTUEMSGSIZEtoo-large verdicts and reactive reflected-echo write failures are excluded. A padded PMTU request substitutes in an eligible local probe slot; the following slot must be ordinary liveness before another PMTU attempt. PMTU does not add a second local producer, while reactive echo replies remain immediate. Confirm the PMTU timestamp begins in its selected slot (cadence wait does not inflate the RTT sample). - During a paced transfer, remove and re-add the standby path through the
normal config reload. Confirm the transfer on the retained active path
continues, the removed path emits no post-removal datagrams, and its
replacement begins with zero
wanbond_path_shaper_*counters (a fresh shaper/socket generation). - With pacing off, confirm
curl -s http://127.0.0.1:9090/metrics | grep wanbond_path_shaper_returns no series. Enable pacing and confirm the series appear for each paced path with fixedpath(and, where applicable,peer) labels. - During a paced saturated transfer, confirm
shaper_data_budget_bytes >= shaper_max_datagram_bytes(B>=Lmax),shaper_control_reserve_bytes = shaper_max_datagram_bytes(C=Lmax), andshaper_queue_budget_bytes = shaper_data_budget_bytes + shaper_control_reserve_bytes(Q=B+C). Also confirmshaper_queue_data_bytes <= shaper_data_budget_bytes,shaper_queue_control_bytes <= shaper_control_reserve_bytes, andshaper_queue_bytes <= shaper_queue_budget_bytes, withshaper_priority_retained_bytes <= shaper_priority_burst_bytes,shaper_fec_group_owned_bytesequal to either zero or the configuredFgroup, andshaper_memory_retained_bytes <= shaper_memory_bound_bytes(Mtotal=B+C+P+Fgroup+Lio). At full B/Q,shaper_admission_waits_totalandshaper_admission_wait_seconds_totalshould rise while asynchronous generic/EMSGSIZEerrors and canceled datagrams remain flat. - Read
shaper_priority_debt_bytes(P0),shaper_rate_bytes_per_second(R),shaper_priority_rate_bytes_per_second(Rp), andshaper_priority_burst_bytes(Pburst). Confirm the exportedshaper_priority_delay_bound_secondsequals(P0+Pburst)/(R-Rp), including on the configured path with the largestRp/Rratio. - After traffic quiesces, reconcile
accepted_bytes = emitted_bytes + async_write_error_bytes + async_write_emsgsize_bytes + queue_bytes + in_flight_bytes. Compare emitted parity bytes withwanbond_fec_repair_bytes_total; direct generated PROBE/echo bytes remain separately visible inshaper_outer_priority_bytes_total. Repeat during load: accepted bytes linearize at queue reservation, so a pending-copy placeholder appears in bothaccepted_bytesandqueue_bytes; cancellation counts only buffers that never reserved capacity. - With FEC active on a naturally single-path group, saturate B/C/P and
confirm the complete native DATA+parity tranche finishes before the
derived
Aand before 250 ms for first-, middle-, and last-DATA loss. Confirm later probe/echo arrivals do not enter the tranche and a second group backpressures. Mixed-path/shared-socket groups must report the finite contract disabled and retain the conservative fallback. - Capture ordinary unpadded PROBEs during FEC+pacing operation. Confirm each
contract-bearing OFFER/ACK is 102 outer bytes (75-byte base plus the
canonical 27-byte payload), remains within
P=2*Lmax, and incrementsshaper_outer_priority_bytes_totalby its exact wire size. Repeat with either FEC or pacing off and confirm ordinary probes remain the legacy 75-byte form; padded PMTU probes must carry no contract. While a conservative FEC gap is armed with no acknowledged receiver venue, send legacy probes faster than 250 ms and confirm they neither advance the receiver generation nor move the original deadline. After acknowledging a venue, confirm the first legacy probe revokes it and later legacy probes remain idempotent. Repeat while the ACK echo write is blocked after admission: the first legacy probe must invalidate that admission, and the stale write completion must not restore a venue. Fail an ACK write terminally and confirm the next legacy probe moves neither generation nor deadline. With two concurrent admissions, fail one and confirm the survivor remains revocable; fail both and confirm no revocable evidence remains. - During a saturated transfer, remove/re-add the standby path. Confirm DATA/inner-control pauses while PROBEs continue, the old staged FEC group completes, no old-service write appears during the 250 ms quiet interval, and the retained path resumes either after the fresh authenticated ACK or at the 250 ms legacy fallback. OuterSeq and FEC GroupID must remain monotonic. Replay the old OFFER and an inconsistent same-identity OFFER; neither may produce an ACK or re-enable fast recovery.
- Drop the first OFFER, the first renewal, and a later renewal at the 200 ms probe cadence. Confirm DATA resumes conservatively at 250 ms without retiring the live OFFER, a later exact ACK succeeds only while at least 250 ms of its own validity remains, and a lost renewal disables fast recovery before the prior lease becomes unsafe. With an incomplete receiver FEC group, accept a higher same-session ContractID carrying the identical service value and hold its ACK write. Confirm the prior ACK-completed venue, expiry, topology generation, and any live fast deadline remain unchanged while DATA-loss identity advances. Complete the ACK and confirm the new expiry becomes eligible; fail it and confirm the old evidence expires at its original time. Confirm the group still recovers. With active and standby venues, complete the standby renewal ACK first and confirm the active venue retains its old fast window until its own renewal ACK or original expiry. Repeat with a changed value or new SessionID and confirm the incomplete group clears before ACK. Hold an old OFFER's socket write across a ContractID rotation and confirm its completion cannot authorize the new identity.
- With active-backup FEC and an exact successfully emitted ACK, record
Aand the authenticated SRTT and RTTVAR of the current DATA carrier. An idle Up backup must neither inflateHnor gate freshness. Lose one DATA frame on the ACKed composite path/source and confirm the receiver releases atW=min(250ms,A+clamp(max(SRTT+4*RTTVAR),10ms,250ms)): repair atW-1nsfills exactly once in order, while repair atWloses the gap and releases the successor. Repeat with no ACK, wrong path/source, a second delivering key, absent/stale RTT, less than 250 ms contract/RTT validity,A>=250ms, weighted policy, rebaseline, and membership/session/service transitions; every case must use or re-arm a fresh 250 ms fallback. Hold an old ACK completion and an old window refresh across each transition, source roam, resequencer replacement, and teardown; neither may restore fast evidence. Force a bounded-window advance that exposes a second gap near the first gap's expiry and confirm the second gap receives a fresh full hold with strictly ascending, exactly-once delivery. - Pause one recovery-window refresh after it snapshots a primary-only ACK
venue and lower RTT. Complete a standby ACK and record a newer higher
authenticated RTT sample, publish the newer refresh, then release the old
refresh. Confirm the standby venue remains usable and
Hretains the new maximum RTT. Repeat with the old refresh paused after publication-revision reservation to cover reversed resequencer commit order. If a gap was already armed before the same-topology update, confirm its exact deadline does not change and the new venue/RTT applies only to the next gap. - Pause each topology transition after the coordinator generation advances
but before the explicit resequencer publication: service change,
SessionID adoption, membership add/remove, same-key roam,
rebaseline, resequencer replacement, and teardown. At the old
Wboundary, concurrent receive/Popmust not release; it must observe the authoritative generation and set the fallback deadline to the topology transition instant plus 250 ms, not the observation instant plus 250 ms. Pause receive until after that bound and confirm immediate release. Burst several advances while receive remains parked and confirm their wake coalesces to the latest coherent generation/time without allocating another timer or spinning; Close must still prevent post-close delivery. Repeat with FEC off and confirm its existing non-FEC gap deadline remains unchanged. - Change same-name pacing/FEC service inputs (
R/Rp/B/C/P/Lmax/Kdata/Mmax/I) and reload. Confirm the daemon warns that the change remains unapplied until restart and the running service retains its old ContractID. After a daemon restart, confirm the peer adopts a new authenticated SessionID. Separately force an engine Bind Close→Open within one process and confirm ContractID rotates while SessionID, OuterSeq, and FEC GroupID remain monotonic. - Delay a FEC deadline decision beyond the dispatch grace. Confirm the current group completes, the next DATA group blocks immediately, and a fresh OFFER appears only after the asynchronous drain plus 250 ms quiet interval; Close during that interval must cancel the waiter. Reopen before releasing an old-generation deadline worker and confirm it neither invalidates nor rotates the replacement contract. Repeat through two rapid Close/Open generations, queue a current-generation miss while the stale worker releases ownership, and confirm the current request still reaches an exact ACK or the 250 ms fallback rather than leaving DATA blocked.
- Force recovery deadline-install, clear, and running-writer failures.
Confirm each exact socket generation immediately rejects admission,
disappears from its peer/scheduler/remote view, and quiesces on Close;
every already-accepted owner-side terminal completion must preserve the
originating cause (a shaped caller already acknowledged at ownership is
not retroactively failed),
and accepted bytes must reconcile exactly into emitted plus generic or
EMSGSIZEterminal bytes with no retained remainder. For an interrupted in-flight syscall, confirm completion reports the published cause while call/byte metrics retain the actual syscall error class; then reopen and confirm a delayed stale failure cannot retire the new generation. - During probe saturation, distinguish bounded priority outcomes:
wanbond_path_probe_priority_coalesced_totalmay rise for skipped ordinary cadences,wanbond_path_pmtu_admission_canceled_totalonly when generation close cancels a wait forPbefore probe generation (not anErrClosedfrom generation or writer after reservation), andwanbond_path_echo_priority_overflow_totalonly for non-blocking reactive echo drops. None should coincide with receive-loop blocking. - Cycle the edge daemon Down/Up once with pacing enabled. Confirm no send remains blocked, the tunnel reconnects, and the journal contains no post-close socket-write errors from the prior generation. Repeat several cycles when validating a lifecycle change; goroutine count should return to its pre-cycle baseline.
For interpreting a transient, let P0 denote generated-priority debt at the
start of a send call. With one coincident post-call Pburst=2*Lmax, sustained
generated priority no greater than Rp=Pburst/200ms, and configured byte rate
R>Rp, admission is bounded by
Dp=(P0+Pburst)/(R-Rp) (not P0/R). Once admitted, local egress adds at most
Q/R+Lmax/R. For an exclusive single-path FEC recovery cut, additionally use
A=I=10ms; it must remain below 250 ms. The prefix B+C+P runs before any
successor DATA can arm a receiver gap and therefore does not enter A.
Therefore call-to-receiver delivery is bounded by
Dp+Q/R+Lmax/R plus the active resequencer hold for a missing lower outer
sequence. Record any observed inner-control call and check it against that
complete bound. These bounds cover the built-in PROBE/echo producer, including
PMTU probes that occupy ordinary local cadence slots. Priority arrivals in the
half-open interval
[call, call+Dp) update the registered waiter under the shaper lock even when
its goroutine does not observe the change until the former deadline; once it
matures, an exact-boundary debit changes future reservations but cannot revoke
the current call's eligibility, so admission needs no extra timer or packet
step. Do not rely on these bounds if a future authenticated outer CONTROL
producer sustains traffic beyond the declared Rp/Pburst model; that condition
constitutes explicit overload.
Canonical record notation: D=250ms; G=10ms; F=1200ms;
B/C/P/Fgroup/Lio/Mtotal; R/Rp/I; Sdevice;
H=clamp(max(SRTT+4*RTTVAR),10ms,D) over qualified fresh DATA carriers only;
W=min(D,A+H); Ecompletion. Record
SessionID restart versus same-process ContractID rotation separately and
verify OuterSeq continuity across rotation. Require the exact authenticated
ACK and A+H<D for fast recovery; otherwise require installed W=D and
record the labelled fallback, including saturated. Validate
geometry from config/contract state—there is no zero-parity inference.
-
With pacing enabled on the edge and disabled on a single-peer concentrator, confirm both directions publish and ACK
A=10ms, reportfast_eligible=1whenA+H<D, and installW<250ms. A shared multi-peer concentrator socket must continue to publish disabled. -
Under deterministic or recorded sparse loss, confirm several gaps whose successors arrived within one
Wrelease at their observation-based deadlines rather than accumulating one freshWper gap. A successor first observed later must retain its remaining recovery interval. -
During the run, capture FEC staged/decision/deadline series, recovery contract status/events/reasons/freshness for both bounded
sender/receiverdirections, receiver RTT age/H/W, shaper outer-priority outcomes/cut/high-water series, and resequencer arm/deadline/wake/fill series. Confirm the monitor reports the same current values. -
Confirm
group_decisions_totalreconciles with resolved staged groups, each active cut's datagram membership reconciles with its socket-call delta, and aggregate accepted/emitted/error bytes remain the cumulative authority while DATA/control queue gauges report current pressure.
Record every cycle, including invalid raw brackets; do not silently discard an
interval. For each leg retain the raw before/after values, raw-valid verdict,
final-20-second outer rate, receiver goodput/emitted-DATA ratio, loaded RTT p95,
retransmits/GiB, queue maxima, error/cancellation deltas, and DATA/parity/
priority reconciliation. Summarize how many raw-valid pairs remain at each cap
and the medians used for the 5Mbit versus 8Mbit retransmit comparison.
The field gate passes only when at least two raw-valid pairs per cap meet every threshold above. A below-cap or unstable raw bracket is inconclusive, not a product failure and not a passing sample.
- Date: _____________
- Starting edge/o3 commits, binary hashes, config hashes, service states: _____________
- Idle RTT per uplink (from Step 1 measurement): _____________
- Measured throughput per uplink (from Step 1 measurement): _____________
- Cycle 1 (
off -> 8 -> 5) report/artifact path: _____________ - Cycle 2 (
8 -> 5 -> off) report/artifact path: _____________ - Cycle 3 (
5 -> off -> 8) report/artifact path: _____________ - Valid raw pairs:
8Mbit_____ /5Mbit_____ (each must be >=2). - Threshold table and exact-byte reconciliation: PASS / FAIL / INCONCLUSIVE
- Restore original binaries/configs/service states on edge and o3; record final hashes and confirm they equal the starting hashes: _____________
- Go/no-go decision:
- T304 exact field gate: PASS / FAIL / INCONCLUSIVE
- Configuration/envelope valid (
B>=Lmax,C=Lmax,Q=B+C,Rp<R, exactPburst/P0/Dp): YES / NO - Proceeding with pacing enabled for field deployment? YES / NO
- Notes: _____________