> Source: https://nexusatlas.dev/docs/concepts/qos — Nexus Atlas developer documentation (Concepts). Converted from the HTML page; the page is canonical.

# Transport classes & FEC opt-in · default off

On a wide link, fairness is free. On a 64 kbit/s bearer shared by a command channel and a video stream, *something* decides who starves — and if you didn't decide, the queue did. The transport-class path makes that decision explicit: classify traffic, give every class a policy, and let the narrow moments degrade the right things first. With `[qos] enabled = false` (the default) none of this machinery is even constructed; the data path is byte-identical to the legacy build.

## Six classes, one honest queue

Traffic is sorted into Control, Voice, Position, Telemetry, Video and Bulk — highest priority first — by a classifier that needs **no application changes**: DSCP marks win when present (CS6/CS7 → Control, EF → Voice, AF41 → Video, CS1 → Bulk), well-known ports fill the gaps (MAVLink's UDP 14550/14551 → Control, RTP ranges → Video), and everything else defaults sanely (TCP → Bulk). Class and reliability mode ride in the existing inner-header priority byte, so QoS-enabled and legacy nodes stay wire-compatible.

The queue is strict-priority with two safeties: per-class *reserved-rate floors* so even Bulk trickles rather than starving outright, and a byte-budget AQM (`backlog_bytes`, default 4 MB) that sheds from the lowest class when the backlog fills. A 4 Mbps video stream can never crowd out a 2 kbit/s command channel — that sentence is the feature.

## Deadline budgets: late data is a lie

Real-time classes carry a per-packet deadline derived from the bond itself — `deadline_k` (default 3.5) × the worst alive link's RTT, clamped between `deadline_floor_ms` (80) and `deadline_ceil_ms` (1000). A packet that exceeds its budget while queued is dropped at dequeue: a video frame that would arrive 2 seconds late is worthless, and transmitting it anyway would delay the frames that still matter. Each class's reliability mode says what lateness means for it — reliable-bounded, deadline-bounded, latest-value-only, best-effort, or store-carry-forward.

## Per-class multipath

Each class chooses how it uses the bond, overriding the global scheduler for its own traffic:

| Policy | Send plan | Meaning |
|--------|-----------|---------|
| SingleBest | Mirror(1) | The lowest-cost alive link, and only it. |
| Duplicate{n} | Mirror(n) | A copy on the n best links; the receiver dedups. Loss protection at n× cost. |
| Aggregate | Stripe | Each packet on exactly one link, weighted by effective goodput (capacity × (1−loss)) — the bond's capacities add. |
| Fec{overhead_pct} | Stripe (per shard) | Aggregate, plus parity shards that reconstruct losses with zero retransmission latency. |

Links that miss a class's latency budget are excluded from its stripe set before weights are computed; an emptied set falls back to the single best link, and an all-dead bond falls back to transmitting on everything configured. The default policy table ships sensible tactics — Control duplicated ×2 with an 80 ms budget and a reserved floor, Video striped with a 200 ms budget, Bulk stored when the peer is away — and everything is editable live.

## Forward error correction

FEC batches K data packets (`fec_block_packets`, default 8), computes M parity shards with a systematic Reed-Solomon code, and stripes all K+M across the aggregate link set. **Any K surviving shards reconstruct the block** — loss on any link, recovered at the receiver, with no retransmission round trip. An idle flush (`fec_flush_ms`, 60) bounds latency when traffic is sparse, and incomplete blocks are abandoned after `fec_block_timeout_ms` (200), delivering whatever data shards arrived. It is doubly gated: the global `fec_enabled` switch *and* a per-class `Fec` policy must both be set. Worked example: 1-in-8 packet loss becomes invisible to the application for 12.5% overhead.

**FEC economics — know before you buy:** the configured overhead is a floor, raised automatically so the block survives the death of the largest link share. On a two-link bond that arithmetic forces ~100% overhead — exactly the price of `Duplicate`, which is the simpler tool at that price. FEC's fractional-overhead benefit is real from three links up. The daemon computes, applies and reports this per class (`effective_overhead_pct`, `parity_floor_applied`) rather than letting a nominal 25% silently under-protect.

## Store-carry-forward

Bulk traffic addressed to a peer with no alive links is not dropped — it is stored (memory, or a file-backed write-ahead log via `bundle_backend = "file"`) and flushed priority-then-FIFO the moment any link to that peer returns. Bounded by count, bytes and lifetime (`bundle_max_count` 10 000, `bundle_max_bytes` 16 MB, `bundle_lifetime_ms` 10 minutes). A vehicle that drives out of coverage delivers its backlog when it drives back in.

## Adaptive per-class escalation

Optionally, individual classes escalate their own protection as conditions degrade — `Duplicate` adds copies, `Aggregate` steps into FEC and thickens it — with hysteresis and dwell on both directions, and two global guards that keep self-protection from becoming self-harm: an escalation bandwidth budget (default 30% of the peer's alive capacity) and a queue-depth gate so *congestion* loss can never trigger load amplification. Every decision is visible in the per-interface QoS stats document.

## Turning it on

```
[qos]
enabled = true              # master switch — off means none of this exists
fec_enabled = true          # second gate for FEC; classes still need a Fec policy
rx_release_buffer = true    # optional Video jitter/reorder buffer (default off)
bundle_backend = "file"     # durable store-carry-forward across restarts
bundle_path = "/var/lib/atlas/nexus0.bundles.wal"
```

Class policies, per-class escalation and the guards live in a JSON policy document beside the daemon — re-read every `reload_secs` (default 2) and editable through `GET/PUT /api/qos` with merge semantics — so policy changes land mid-session with no restart and no packet loss. The [configuration reference](https://nexusatlas.dev/docs/config#cfg-qos) documents every key and default.
