OpenWrt package bundle for a Rust implementation of cake-autorate with a
LuCI UI, UCI configuration, managed SQM lifecycle, and optional calibration.
This project is a Rust/OpenWrt adaptation of
cake-autorate, which was
created by lynxthecat in 2021 and subsequently
developed by its community. The fast load-and-delay controller, terminology,
configuration model, and many defaults here deliberately follow that original
work. Development of this port started from the maintained
woffko/cake-autorate fork.
Our sincere thanks go to lynxthecat for founding cake-autorate, to all of its
contributors for refining the algorithm in real networks, and to the CAKE,
OpenWrt, and sqm-scripts developers whose work provides the queueing and
shaping foundation. The port preserves the original fast load-and-delay
controller and builds an OpenWrt-native Rust runtime, managed SQM lifecycle,
LuCI/UCI workflow, structured Multi-WAN routing, Full Auto-Tune, transport
quality ratings, bounded ceiling discovery, RAM-only graphs, and optional
outbound DSCP profiles around it. The detailed boundary between inherited and
port-specific work is stated below; this project does not claim authorship of
the original cake-autorate concept.
Substantial parts of this port were written and reviewed with OpenAI Codex and Google Gemini working as paired development assistants. Anthropic Claude Opus was also used for additional deep bug analysis and independent review. The human project author defined the requirements and product logic, made the design and safety decisions, controlled access to test equipment, reviewed the results, and retained final authority over every accepted change. The assistants provided implementation, analysis, testing, and independent review; project ownership and responsibility remain with the human author.
- Quick setup guide covers a clean installation, the first instance, Full Auto-Tune, conservative retry, Status/Quality and Multi-WAN.
- Controller mathematics describes rate measurement, delay baselines, bufferbloat detection, the fast rate controller, and the bounded adaptive-ceiling state machine with formulas and examples.
- Testing and observed results starts with the current r313/r120 acceptance contract, then retains older RC sections as an explicitly historical engineering chronology.
- Release history lists superseded source tags and their milestones. Only the current r313/r120 Release remains a supported download.
- Bounded probe ceiling is the concise state-machine and safety-invariant reference for the optional outer controller.
- Full Auto-Tune documents the native calibration job, proposal formulas, phase-background accounting, three separate throughput ratios, bounded profile optimizer, and fail-closed validation contract.
- Transport-aware quality control documents HTTP/TCP latency fusion, the strict control signal, LibreQoS-like detected ratings, the throughput floor, bounded natural-load search, and scheduled Full Auto-Tune.
- Multi-WAN routing and lifecycle describes the structured
main/mwan3route model, per-uplink state isolation, failover/recovery, route identity checks, SQM ownership, and operational diagnostics. - Profile traffic priorities documents the native per-profile DSCP rule editor, its strict ownership boundary, outbound-only classification, rule order, runtime attestation, and Multi-WAN isolation.
The release targets OpenWrt 25.12 and publishes the daemon for the same 12
package ABIs as the nftables mwan3 v3.6.11-1 release:
x86_64, four AArch64 variants, five ARMv7 variants, MIPS 24Kc and
little-endian MIPS 24Kc. Run
apk --print-arch on the router and select the identically suffixed daemon
APK. Native route-bound WebSocket/TCP/HTTP probing uses statically linked Rust
TLS and socket libraries; ordinary OpenWrt runtime dependencies remain
explicit below.
Status keeps the operational state in one place: uplink lifecycle, Autorate/SQM/classifier health, active profiles, current collection state and the last complete connection rating. The anonymized example below shows two independently routed uplinks and the honest WAITING FOR DATA state. After a complete passive or guided capture, LAST KNOWN preserves that DL/UL grade; an incomplete or contaminated attempt never replaces it.
Get rating offers an automatic router-side test and a guided client capture. The dialog first attests the current operation for that exact instance; a second tab reconnects to an active Rating instead of starting a competing job. Starting Guided after a completed Automatic run creates a new job and worker identity, while closing during an in-flight Start receipt still cancels the exact admitted job. Raw lease/debug identities are never shown to the user.
Graphs use an opt-in, bounded RAM-only history. Latency, transport delta, effective delay, CPU and synchronized download/upload traffic share the same timeline; the oldest samples are discarded automatically and nothing is written to flash. The live Multi-WAN capture also shows an adaptive backoff event on the shared time axis.
Settings manages each uplink independently and exposes traffic priorities, Full Auto-Tune, categorized editing and deletion directly from its instance row. A clean package installation creates no instance until the user chooses Create instance.
The Edit dialog groups routing, rate limits, adaptive ceiling, probes, quality, controller, SQM, testing and monitoring controls instead of presenting one long form. See the current categorized Autorate setup.
Full Auto-Tune offers Gaming, Best overall, Variable link and Fair calibration profiles, then measures the selected uplink and presents diagnostics before anything is written to UCI. Multi-WAN calibration keeps the route and evidence separate for each selected uplink. Gaming additionally has a one-run Extreme A+ search for wide links: it accepts only measured A+ minima, disables Auto-Apply below 70% retained capacity, and warns that such a throughput sacrifice is intended for short latency-critical sessions rather than continuous household use.
Variable link opens a small access/capacity wizard instead of guessing the provider medium from an Ethernet or PPPoE handoff. QMI/MBIM/NCM and modem-like devices can be identified with an explicit confidence value; cellular, LEO/GEO satellite, fixed wireless/WISP, shared wired and unknown access can always be selected manually. The choice sets only the bounded exploration floor and probe cadence. It never invents a runtime limit: only an exact tested CAKE point may become the minimum or safe ceiling. Runtime learning is a separate choice between Validated ceiling only, Bounded learning from real traffic, Bounded + scheduled active calibration, and Explicit service hard caps.
During a run, the dialog reports an evidence-backed percentage and the current operation, such as idle-latency measurement, raw capacity, download/upload search, candidate confirmation, directional comparison, restoration, or proposal preparation. Progress is monotonic but is never advanced by an elapsed-time animation; 100% is reserved for a published Review after the previous runtime has been restored.
Review can present several independently measured choices. Every trade-off for the selected card is listed, followed by one aggregate I accept all listed trade-offs confirmation. For explicitly selected cellular, satellite, and fixed-wireless access, Full raw capacity also attempts a download-unshaped / upload-shaped result. If that control cannot satisfy the hard evidence gates, the card remains visible but disabled with its exact reason.
The following controlled OpenWrt cellular-link sample runs are anonymized benchmark evidence and are not shipped behavior or guarantees.
- Raw/unshaped path: 324–342 / 40–43 Mbps observed with C/C.
- Existing CAKE 114.5/15.8 produced 102–104 / 14.2 with DL C and UL A.
- Upload-only shaping showed about 342 / 13.9 with DL B and UL A.
- Simultaneous load comparison:
- no CAKE: 202 / 44.9, D, 205 ms
- 114.5/15.8: 107 / 11.7, A, 17 ms
- 250/15.8: 219 / 10.3, B, 34.8 ms
- 200/15.8: 180 / 11.7, B, 31.6 ms
- 175/15.8: 154 / 11.2, A, 18.8 ms
ICMP samples in this set stayed around 10–13 ms while TCP/WebSocket samples were 72–190 ms. This is a measurable risk signal that some providers/networks may prioritize or specially treat ICMP, so ICMP-only grading can understate user-traffic latency.
RC27 implements the following controller and LuCI model:
- Keep raw capacity, current rate, measured runtime minimum, exploration minimum, safe ceiling, failed bound, confidence and route epoch independently for download and upload. A route/source/member change expires old evidence.
- Offer an explicit raw-capacity calibration mode which transactionally removes only the managed download ingress CAKE/IFB path, measures the selected uplink, and restores the exact prior runtime through a watchdog even if the worker or browser disappears. Upload shaping may remain active when the selected test requires it.
- Run shaped candidates through the same transport-aware path, then confirm the selected DL/UL pair under simultaneous load. The worse corroborated ICMP or native transport delta is authoritative.
- Build one ranked Review set from as many as four independently measured runtime topologies: both directions shaped, upload-only shaping, download-only shaping, and no SQM. Every card names its exact tested rates and evidence; Auto-Tune never derives or invents an untested rate merely to make a proposal available.
- Keep profile class, retained-capacity objectives, and relative utility versus another safe topology as policy judgements rather than technical failures. A proposal that misses one of them remains selectable only after Review shows the deviation and the user explicitly acknowledges that proposal's warning.
- Keep measurement integrity, route identity, raw-bypass proof, complete background accounting and contamination limits, loss, the manual-review latency ceiling, and a proven 50–110% CAKE realization safety envelope for every shaped direction as non-overridable hard gates. Historical-throughput trust and the ordinary 80% realization objective remain explicit Review warnings inside that envelope. Acknowledging a profile trade-off cannot weaken the hard checks.
- If shaped frontier search cannot produce a safe result but the independent raw control is complete and passes every applicable hard gate, carry an exact no-SQM fallback into Review instead of discarding the whole run. This is a manual proposal backed by the measured raw topology, not permission to infer missing shaped evidence.
- The same running SQM topologies can be selected manually in Edit → SQM
setup → CAKE directions. One-sided mode removes CAKE from the unselected
direction; it is not the same as retaining CAKE at a fixed rate by disabling
Adjust DL or Adjust UL. Logical capacity values remain independent of the
managed SQM runtime
0marker used for an absent direction, including across the standard LuCI Save & Apply cycle. - Treat a flat latency curve as directional evidence. If one Variable-link direction meets its quality target but lower tested CAKE rates provide no repeatable latency improvement, hold that direction at its highest safe, target-meeting tested point while the peer direction finishes its search. Such a result is always manual-review only, requires a safe simultaneous DL+UL confirmation, and never invents an untested runtime minimum.
- When Variable-link reaches its medium-specific 35%, 40%, or 50% exploration boundary without proving a knee, or bounded repeats remain nonmonotonic, keep the result useful without overstating it: select the best exact-tested safe point at or above the 50% trust boundary, use that same point as the runtime minimum, require a safe simultaneous DL+UL confirmation, and expose it only for manual review.
- Each close manual proposal lists every missed advisory/profile criterion in Review. One aggregate checkbox accepts the complete displayed set for that exact topology; the individual codes remain bound to Apply and cannot be hidden or changed after confirmation. A final simultaneous latency miss is reviewable only within the adjacent quality class: Gaming/Extreme A+ to A (30 ms), Best overall A to B (60 ms), Variable link B to C (200 ms), and Fair C to D (400 ms). Final simultaneous realization between 50% and the ordinary 80% proof threshold is also an explicit per-direction acknowledgement.
- Grow passively only under proven saturation, clean transport evidence and a measurable throughput gain. The selected policy may instead freeze the exact validated ceiling, add budgeted scheduled calibration, or enforce explicit service caps. Variable Link never treats either policy choice as evidence of capacity above its measured raw control.
- Apply causal backoff: two reductions without meaningful latency improvement
restore the last useful point and enter
HOLD_NO_EFFECTinstead of destroying throughput for radio/operator delay outside CAKE's control. - Keep three user choices separate: calibration strategy (Shaped only, Full raw capacity, or Reuse trusted bounds), runtime learning (Validated ceiling only, Bounded learning from real traffic, Bounded + scheduled active calibration, or Explicit service hard caps), and operating profile (Gaming, Best overall, Variable link, or Fair). Reuse is enabled only after that instance has saved positive DL and UL P50 references; an uncalibrated or stale reuse choice is explained and safely normalized to Shaped only.
- Keep scheduled traffic injection opt-in. The scheduler reserves and settles per-instance daily/monthly byte allowances in a crash-safe ledger, shows the next due run and remaining allowance, and stops before exceeding a hard budget.
- Full raw capacity first measures comparable bidirectional, download-only and upload-only controls by bypassing only the direction under test. For explicitly selected cellular, satellite, and fixed-wireless access it then additionally attempts a terminal upload-only-shaped experiment with download ingress bypassed. A verified result becomes a separate manual option; unavailable or unsafe evidence remains an explained disabled card, never a silent runtime topology change.
- Keep fail-closed behavior unchanged: an integrity, identity, contamination, or restoration failure preserves the last safe state and publishes either a typed diagnostic or an exact evidence-backed manual option; it never invents a lower-confidence rate.
- Bind every manually reviewable deviation to the exact option as a stable acknowledgement code. Auto-Apply is possible only when that option requires no acknowledgements; there is no global confidence label which can weaken a hard gate.
One live high-capacity cellular development run intentionally used a 2 GiB hard
limit. It completed raw measurements near 403/46 Mbps and a first shaped
point near 220/30 Mbps, then stopped with typed
traffic-budget-exhausted, restored the original SQM runtime and wrote no UCI.
A complete Variable-link frontier at that capacity can consume roughly
4.5–6 GiB, so periodic active testing must be enabled only with an
appropriate data allowance. CPU saturation is reported as advisory evidence;
it does not by itself reject an otherwise safe candidate.
The RC27 release builds the OpenWrt 25.12 daemon APK for this ABI matrix:
| APK suffix | Representative OpenWrt target |
|---|---|
x86_64 |
x86/64 |
aarch64_cortex-a53 |
bcm27xx/bcm2710 |
aarch64_cortex-a72 |
bcm27xx/bcm2711 |
aarch64_cortex-a76 |
bcm27xx/bcm2712 |
aarch64_generic |
armsr/armv8, rockchip/armv8 |
arm_cortex-a7 |
mediatek/mt7629 |
arm_cortex-a7_neon-vfpv4 |
bcm27xx/bcm2709 |
arm_cortex-a9 |
bcm53xx/generic |
arm_cortex-a9_vfpv3-d16 |
mvebu/cortexa9 |
arm_cortex-a15_neon-vfpv4 |
armsr/armv7 |
mips_24kc |
ath79/generic |
mipsel_24kc |
little-endian 24Kc targets |
The target is an APK ABI rather than one specific board. The authoritative
choice is the value returned by apk --print-arch. Every full daemon asset
follows the name
cake-autorate-rs-1.0_rc27-r313_openwrt-25.12_<arch>.apk; the shared
luci-app-cake-autorate-rs-1.0_rc27-r120.apk contains the
architecture-independent full LuCI interface and SQM integration.
The same release also contains a separately compiled Lite pair for every
ABI: cake-autorate-rs-lite-1.0_rc27-r313_...apk and
luci-app-cake-autorate-rs-lite-1.0_rc27-r4.apk. Lite keeps the manual
controller, routing, latency probes, directional SQM and bounded adaptive
ceiling, but deliberately omits Get rating, speed-test calibration, Full
Auto-Tune and scheduled calibration. Full and Lite are mutually exclusive;
install both packages from one pair, never mix a Full daemon with Lite LuCI or
the other way around.
The accepted release is
v1.0-rc27-r313-r120:
24 architecture-specific daemon APKs, the Full and Lite noarch LuCI APKs,
SHA256SUMS, and machine-readable matrix/release manifests. The published
Lite daemon APK is about 79% smaller than Full on average across the matrix;
Lite LuCI is about 94% smaller than Full LuCI.
RC27 keeps forwarded traffic separate from the isolated speed-test result and binds background, retention, latency, topology and measurement deviations to the exact Review option which observed them. An option with no acknowledgement codes may satisfy unattended Apply; every safe exception is manual-only and must be accepted through one aggregate confirmation which still preserves the complete code list. A strict busy-link stop can be retried or continued once with conservative safeguards. A stalled speed-test phase is retried with bounded cooldowns; an automatically chosen server may be replaced only by restarting the complete raw-control series, while an explicitly pinned server is never changed. An exhausted timeout is reported as retryable and inconclusive, preserves the verified diagnostics in RAM, and never exposes an Apply action. CPU saturation is visible as a warning rather than a false quality failure. The release retains the explicit Automatic/Gaming/Best overall/Fair/Custom traffic-profile model and sequential per-member Multi-WAN calibration. Direct APK assets are provided for all 12 daemon ABIs plus the Full and Lite architecture-independent LuCI APKs. Each ABI has a separately compiled Full and Lite daemon. Dependencies resolve through the router's configured OpenWrt package feeds; no offline bundle is attached.
The original lynxthecat/cake-autorate solves the central variable-link problem: CAKE needs a bandwidth setting, while LTE, 5G, Starlink, cable, and other links may change capacity faster than a static setting can follow. It observes traffic load and reflector delay, then adjusts download and upload rates independently between configured minimum, baseline, and maximum values.
This port preserves that control model rather than replacing it with a generic speed-test loop:
- per-direction minimum, baseline, and maximum CAKE rates;
- traffic-load detection from interface counters;
- ICMP RTT or timestamp-based one-way-delay evidence from multiple reflectors;
- fast rate increases under clean load, immediate reduction on confirmed bufferbloat, low-load return toward baseline, and a refractory interval;
- idle/stall handling, reflector health/replacement, stale-sample rejection, and wire-size/CAKE-overhead compensation;
- a hard configured maximum by default. The Rust-only adaptive ceiling remains a separate, explicit opt-in.
The port then adds an OpenWrt-native management and measurement layer around that controller:
| Area | Original project | Added by this Rust/OpenWrt port |
|---|---|---|
| Runtime | Concurrent Bash processes and external pingers | One memory-safe Rust controller per UCI/procd instance, bounded parsers and native route-bound transport probes |
| Platform | OpenWrt and Asuswrt-Merlin | OpenWrt 25.12 package feed/SDK integration for the published x86_64, AArch64, ARMv7 and MIPS ABI matrix; Asuswrt-Merlin is not supported |
| Configuration | Shell configuration files | UCI source of truth, procd lifecycle, rpcd ACLs, and an integrated LuCI interface |
| SQM ownership | Works with an existing CAKE/SQM setup | Creates, synchronizes, verifies, repairs, and uniquely owns each managed SQM/CAKE/IFB/redirect path while leaving unrelated queues alone |
| Multiple links | Multiple script instances are possible | Structured main-table or nftables mwan3 member routing, one isolated instance/state/queue per uplink, route identity checks, failover states, and cross-WAN ownership guards |
| Initial tuning | User chooses min/base/max from observed link behavior | Manual wizard, backend-aware speed test, and Full Auto-Tune with separate Gaming, Best overall, Variable link, and Fair throughput/latency objectives |
| Auto-Tune safety | Not an upstream feature | RAM-only jobs, background-traffic accounting, ICMP plus native transport evidence, bounded per-direction frontier search, typed validation, exact proposal review, crash recovery, and guarded UCI apply |
| Quality | Delay drives the controller | LibreQoS-style complete DL/UL detected grades, passive client-traffic episodes, guided Get rating, CURRENT/LAST KNOWN semantics, and optional transport-aware ceiling control |
| Maximum discovery | Configured maximum is fixed | Optional bounded adaptive ceiling starts from exact shaped evidence and learns only below measured-raw and optional service caps, without rewriting UCI |
| Observability | Detailed logs and external analysis tools | Live JSON status, component-level Services health, CPU/softirq and CAKE diagnostics, redacted export, and opt-in RAM-only synchronized latency/CPU/traffic graphs |
| Traffic policy | Relies on the surrounding CAKE/SQM configuration | Optional outbound-only nftables DSCP profiles for Gaming, Best overall, Fair, and editable Custom rules, with runtime checksum attestation and no second qdisc owner |
| Automation | Primarily controller runtime | Scheduled quiet-window Auto-Tune, per-instance speed-test server caching, package/backend checks, and safe review-only versus validated auto-apply modes |
| Integrations | Upstream logging/analysis ecosystem | Optional MQTT/Home Assistant publisher and a LuCI replacement surface for the managed SQM settings |
The additions are intentionally bounded. This is not a drop-in rewrite of every upstream script, log-analysis utility, or platform integration. In particular, the upstream Asuswrt-Merlin path is absent, upstream configuration files are not accepted verbatim, and adaptive ceiling, Full Auto-Tune, transport grades, Multi-WAN routing, graphs, and the native DSCP classifier are port-specific. The controller mathematics and inherited terminology are documented in Controller mathematics; port-specific safety boundaries are documented in the linked feature references above.
Traffic policy is one exclusive per-instance choice: Automatic, Gaming, Best overall, Fair, or Custom. Automatic follows the Auto-Tune profile; pinned policies do not change on later calibration. Previewed rules come from the same catalog as the nftables renderer, Customize this preset stages an editable UCI copy without auto-commit, and the independent classifier master remains off unless the user enables it. Status names both the Auto-Tune and traffic-priority profiles.
The legacy migration is one-time and idempotent. It adds a resolved profile and migration marker but never enables traffic rules, Autorate, or SQM. Desktop, touch, keyboard, and narrow mobile layouts are covered by deterministic tests and authenticated Playwright checks.
Mobile preset view · staged Custom copy
The screenshots use anonymized instance, interface, host and address labels. They combine a completed rating capture with the current RC27 Multi-WAN, graphs, Auto-Tune and traffic-priority interface; rates and diagnostics are representative examples rather than guarantees.
This release is RC27 r313/r120: daemon package r313 and Full LuCI package r120, with the parallel manual-only Lite pair r313/r4. It retains the complete two-direction Rating authority, truthful staged Auto-Tune progress, a ranked four-option Review including the measured mobile download-bypass topology, one aggregate trade-off confirmation, and an Apply flow which verifies the runtime and immediately reloads authoritative UCI without another button or tab switch.
r311 hardens the native Apply transaction itself. Before mutating either UCI
package it durably records the exact original and candidate cake-autorate and
sqm bytes, modes, digests, request/job/worker identity, and selected option
manifest. Recovery classifies each live package as original, candidate, or
foreign, safely resolves every original/candidate mixed pair, and refuses an
unrecognized overwrite. Stale restore temporaries are cleaned only while the
same config-pair lock is held; unsafe links fail closed. A legacy recovery
record without candidate bytes remains rollback-only.
Apply, recovery, ordinary service start/reload, package replacement, and an empty controller plan now share one state-driven readiness boundary. Success is not published until the runtime lock is released and the exact expected controller set is ready; a failed readiness proof becomes a failed terminal, never a false Applied result. Package-upgrade deferral has its own typed receipt so it cannot be confused with a genuine empty plan. No fixed retry timer or second post-install confirmation is used. The focused crash-boundary suite, live VM/router upgrades, browser audit, and Full/Lite 12-ABI verification are recorded in Testing. The README intentionally describes current behavior instead of retaining a cumulative RC diary. Superseded milestones remain in Release history and git tags, while GitHub Releases contains only the current downloadable build.
r313 additionally fixes fresh Full installation and Lite-to-Full replacement. After OpenWrt's default package hook returns, the Full package now re-attests the main controller, stops and settles any calibration instance that the default hook already started, then enables and starts exactly one coordinator. The in-place upgrade branch remains separate and performs no duplicate readiness confirmation. Exact Full → Lite → manual stop/save/start → Full testing now returns package status zero and restores the original UCI, services and both CAKE qdiscs byte-for-byte.
This repository is organized as an OpenWrt package feed/SDK overlay. Each package directory follows the OpenWrt package documentation layout:
package/<package-name>/Makefile
package/<package-name>/files/
package/<package-name>/src/
files/ contains installed default config, init scripts, LuCI menu/ACL files, and LuCI views. src/ contains bundled application source; OpenWrt explicitly supports bundled source code inside a package directory, commonly under src/.
package/cake-autorate-rs- Rust daemon package.package/luci-app-cake-autorate-rs- Full LuCI app.package/luci-app-cake-autorate-rs-lite- minimal manual-only LuCI app./etc/config/cake-autorate- UCI config installed by the daemon package./etc/init.d/cake-autorate- procd service wrapper./usr/sbin/cake-autorated- daemon binary.
This is the current Rust/OpenWrt release-candidate implementation, not a drop-in replacement for every upstream Bash utility or supported platform.
Implemented:
- UCI-based config loading.
- Multiple enabled UCI sections via procd instances.
- Structured
route_mode=auto|main|mwan3andmwan3_memberrouting. Native nftables mwan3 state is validated before a member is used; each instance publishes its resolved member, L3 device, source address, external address, fwmark and routing table. Policy failover produces independentACTIVE/STANDBY/OFFLINE/LEARNINGlifecycle transitions without sharing learned state between uplinks. fpingRTT reflector probing,fping-tsandtspingICMP timestamp OWD probing, explicit-serverirttOWD probing, plus a basic per-reflectorpinger_method=pingfallback.- Active reflector health tracking and replacement for running
fping,fping-ts,tsping,irtt, andpingprobes: response-deadline offences, baseline/EWMA comparison, periodic replacement, optional reflector stats logging, and pinger restart with the next spare candidate. - Runtime status JSON and LuCI status page expose active, spare, and bad reflector sets plus per-reflector samples, offence counters, and last RTT.
- sysfs RX/TX byte counter sampling.
- CPU usage sampling from
/proc/statis always exposed in runtime status;output_cpu_statsandoutput_cpu_raw_statscontrol log records only. The Status value is whole-router utilization. Run/usr/sbin/cake-autorated --cpu-profile 30to measure the daemon, persistent pingers and scheduler separately, including short-lived child work waited by each daemon. - adaptive rate calculations using delay/load windows.
- Optional Rust-only bounded-probe ceiling extension. Validated ceiling only is the conservative default when link classification is inconclusive; bounded passive/scheduled learning is an explicit policy. Each direction starts from its exact tested-safe point, independently qualifies clean high load, briefly tests a higher ceiling, promotes only a clean target with a measurable throughput gain, and remembers the lowest target that caused confirmed bufferbloat. Later probes use the midpoint between safe and failed bounds. Short load/delay-classification fluctuations are tolerated, while sustained loss or a global probe-response gap rolls back without poisoning the safe/failed bounds; a stall resets runtime learning. Measured-raw DL/UL caps and any tighter service caps remain hard safety limits, and runtime learning never rewrites UCI. Status exposes the phase, safe ceiling, failed bound, probe target, and last transition reason. See ADAPTIVE_CEILING.md for the state machine and acceptance tests.
- Optional native transport RTT measurement, disabled by default. Persistent WebSocket, TCP-connect, and persistent HTTP resolve DNS outside the timer and bind sockets to the selected device/source/fwmark. Measurement supplies the observational LibreQoS-compatible detected rating. A separate, default-off controller toggle may use only trusted, route-verified, CPU-clean evidence to block unsafe ceiling growth or run a bounded natural-traffic search above a protected per-direction floor. See TRANSPORT_QUALITY.md.
- Passive detected-rating load classification is independent of controller
high/low/idle state. A bounded rolling peak for entry, average for exit,
enter/exit hysteresis,
direction latch, and dropout grace turn real forwarded traffic into stable
DL,UL, orBIDIRECTIONALrating phases without double-counting byte counters. OptionalGet ratingautomatic/client capture uses the same detector and supplies a bounded per-direction trigger; it never bypasses shaping. Automatic capture first enforces a quiet window and runs separate download-only and upload-only load phases. The top-level grade is published only from a fresh finalized capture containing trusted ICMP and transport evidence for both directions. Partial, stale, compatibility, or one-sided evidence remains diagnostic and cannot replace the last complete grade. - The controller, rating detector, transport scheduler, and RAM graph history reuse one atomic per-interval RX/TX counter sample. This prevents either direction from disappearing because another consumer already advanced the counter baseline.
tc qdisc change ... cake bandwidth ...shaper updates.- Upstream-style idle/stall handling: sustained idle can stop pingers, activity restarts them, and optional minimum-rate enforcement applies on sustained idle or global no-response timeout.
- daemon log rotation by age/size with best-effort gzip compression.
- JSON status file under
/var/run/cake-autorate/<instance>/status.json. - Optional per-instance LuCI
Graphshistory for RTT, transport/effective latency, total CPU, download/upload traffic, DL/UL safety floors, and detected grade events. It is disabled by default and enabled directly on each active instance card. A per-instance dropdown selects 1, 2, 5, 10, 15, 30, or 60 second sampling. Each uplink is a separate vertical card; both charts share a horizontally scrollable timeline, auto-follow new samples until the user scrolls back, and expose exact values on hover. Samples stay only in/var/runtmpfs. A configurable globalautoor 256 KiB–100 MiB budget is divided across enabled instances, dynamically capped fromMemAvailable, and compacted in a streaming pass. Older rows are read in bounded pages, critical memory pressure pauses history, and no sample is written to router flash. - LuCI Status can export a diagnostic text bundle containing redacted cake-autorate config, SQM config, runtime status, daemon logs, package versions, and recent syslog lines.
- LuCI Status shows the exact installed daemon and LuCI package versions at the top of the page.
- LuCI settings page with compact instance rows and modal tabs for detailed settings.
- LuCI cross-field validation for manual min/base/max rates, explicit
download/upload interface conflicts,
pingfallback pinger count, and duplicate managed SQM section ownership. - LuCI and init guard against enabling an automatic IFB download interface without an enabled SQM backing queue for that instance. A stray IFB created by another SQM section does not satisfy the guard.
- Managed SQM owns its target interface exclusively: the init script disables
conflicting unmanaged SQM queues on the same device. On systems running the
OpenWrt
bridgeraccelerator, managed SQM devices are added to its blacklist and an empty conflictingclsactis removed before SQM starts. Autorate now also requires a real ingress redirect to its IFB, so a failed download shaper cannot silently report all visible traffic in the upload direction. - While running, each managed instance checks the actual CAKE/IFB/ingress state. If it disappears, probing and rating stop, Status reports the concrete runtime error, and the Rust service-lifecycle path performs a targeted, ownership-checked SQM restart through the narrow init bridge. Attempts are serialized, deferred during a speed test, and rate-limited to avoid a recovery loop.
- The mandatory Status Services column independently reconciles configured
intent with daemon processes, managed SQM ownership, both CAKE qdiscs and
rates, IFB/redirect topology, native traffic-rule attestation, current heavy
operation, and guarded apply state. It exposes
HEALTHY,DISABLED,DEGRADED,ORPHANED, orBLOCKEDplus the exact component-level reason. - Optional native profile traffic rules classify only outbound packets in the
private
inet cake_autorate_dscptable. Gaming, Best overall, and Fair have separate built-in defaults and editable ordered custom rules. No qosify, eBPF, external qdisc owner, or free-form shell rule is used. The loaded ruleset is SHA-256-attested against its instance, resolved interface, and profile; Status reports missing, ineffective, drifted, and orphaned rules. - LuCI setup wizard for creating instances, importing SQM rates, running a
router-side speed test, and writing derived limits. Its normal speed-test step
shows only rates and the test action; backend/package/headroom controls and
reflector scanning are available behind
Advanced test options. A visual three-step navigator (Interface,Speed test,Review) also supports direct validated navigation by clicking any numbered step. Full Auto-Tunecreation and re-run mode alongside the manual wizard. It performs interface/route/backend preflight, reflector selection, idle ICMP and native persistent-transport baselines, and one bidirectional plus two download-only and two upload-only unshaped controls on a reused validated server. A pure Rust calculator derives explicit DL/UL min/base/max, activity and delay thresholds, link-layer overhead, and bounded adaptive-ceiling limits. LuCI shows the raw evidence and complete proposal before creating the instance; job state stays under/tmp, cancellation terminates the process group, and UCI is not written before confirmation. The shaped job records ICMP p95-to-p95 growth, native transport p95-to-p95 growth, loss, aggregate/busiest-core/softirq CPU, CAKE counters, three distinct throughput ratios, and forwarded client background before restoring the previous qdisc/SQM state. Typed gates and a bounded Rust per-direction optimizer search the measured quality/throughput boundary, repeat unreliable observations, raise a candidate until its hard floor is reachable, and confirm the exact selected pair. The public Review contract carries immutable option IDs, manifest/review digests, exact tested topology and rates, and the option's complete acknowledgement list. Missing or structurally invalid evidence remains a hard stop; safe background, retention, latency or topology exceptions are explicit-review only. No acknowledgement can weaken route, SQM ownership, loss/latency, measurement, or runtime-restoration gates.- Optional scheduled Full Auto-Tune, disabled by default, adds a quiet-time gate, maintenance window, interval, RAM-only daily byte budget, and explicit review-only versus validated auto-apply mode. Unattended apply requires a preferred option whose Auto-Apply evidence contract passes with no required acknowledgements, met profile objectives and complete restored runtime; every acknowledged option remains explicit-review only.
- LuCI instance editing keeps advanced speed test backend controls and pinger/reflector planning behind the advanced settings toggle. The automatic interface preset, speed-test headroom, and manual min/base/max escape hatches are also hidden from basic setup. Basic speed test actions still use the current unsaved interface and backend selections when those controls are available.
- When
Manual rate limitsis enabled, editing the SQM download/upload rates does not overwrite the explicit autorate min/base/max values. Automatic mode continues to derive base/max from SQM rates and minimums at half-rate. - In the LuCI edit modal, enabling the basic
Enable SQMtoggle also enablesManage SQMfor that instance so the setup page can recover disabled external/imported SQM queues without visiting advanced settings. Manage SQMdefaults on to match the init-script default, and detailed SQM queue/link-layer fields are hidden when the instance is not managing SQM.- Required LuCI value/list fields use packaged defaults when older/incomplete sections lack a key, while optional fields remain optional and empty.
- LuCI Reflectors tab can check pinger backend availability and scan configured
reflectors plus the upstream default anycast reflector pool, including RTT and
ICMP timestamp capability, without adding hard dependencies. It shows RTT/OWD
backend mode, install/manual-action hints, and can run
apk add fpingorapk add irttfor supported optional backends if they are missing. - LuCI can apply the pinger planner recommendation into pending changes for an existing instance, and the create wizard writes pinger method, active pinger count, and reflector list for new instances.
- LuCI setup tab keeps the normal path to target interface, SQM enable, download/upload rates, and one-click speed testing. Explicit upstream min/base/max controls remain available in advanced manual-rate mode.
- Basic setup uses one
Enable autoratecontrol for both autorate and its managed SQM queue. Advanced users can disableManage SQMonly when they maintain a separate enabled SQM queue themselves. - Native router-side Speed Test with two user choices:
Autoandspeedtest-go. Both resolve to the same route-bound Rust operation and the Full daemon package depends onspeedtest-go; retired librespeed, iperf3 and built-in HTTP execution paths are no longer selectable. The backend tries nearby servers, rejects an implausibly asymmetric automatic result, and caches the first validated server per instance; entering a server ID pins the test to that Ookla server. Jobs have durable identities and are polled or reattached by LuCI instead of living inside an rpcd request. - Disabled instances are shown as
DISABLEDin LuCI and do not display stale runtime counters; the init script removes stale status samples after a service stop. - Integrated SQM backend sync: each
cake-autorateUCI section can own a matchingsqmqueue section. - Optional native MQTT publisher service: per-instance MQTT export reads bounded SUMMARY/CPU log records, speaks MQTT 3.1.1 directly without exposing credentials in a child-process argument list, and registers retained Home Assistant discovery and availability records when enabled.
- Automatic interface preset: selecting the target interface fills
sqm_interface,ul_if,dl_if=ifb4<target>, and empty/generatedping_extra_args=-I <target>for non-IRTT pingers so reflector probes are bound to the selected uplink by default. - Automatic SQM rate import from an existing
/etc/config/sqmqueue for the selected interface when available. - Upstream-style max-wire packet compensation for OWD thresholds and achieved
rate monitor timing, using live interface MTU plus CAKE
atm/noatm overheadfromtc qdisc show. - Upstream-style stale reflector response guard: pinger samples processed more than 500 ms after their timestamp are logged and skipped.
- LuCI status page with start, restart, stop actions. An enabled instance that
has received no valid probe sample after ten seconds shows a compact
No probe replieswarning with pinger/multi-WAN routing guidance.
Known limits:
- Adaptive ceiling is intentionally not part of upstream
cake-autorateand is an explicit opt-in. Configure absolute caps deliberately; leaving it off preserves exact upstream hard-max semantics. The recommended initial tuning is 20 seconds qualification, a 3% open probe, 8 seconds observation, 30 seconds cooldown, and 900 seconds failed-bound memory. Runtime status/logs expose all phase transitions and effective-ceiling changes. pinger_method=pingstarts one basic ping process per active reflector, but it remains a fallback; usefping,fping-ts,tsping, or explicit-serverirttwhere those backends are available.pinger_method=irttrequires the optionalirttpackage and at least one explicitlist irtt_server ...entry. Generic DNS reflector pools are not used as IRTT servers. The router and IRTT servers also need synchronized clocks; upstream-compatible parsing ignores negative one-way delays from unsynchronized hosts.ping_prefix_stringremains available only for compatible legacy/main-route setups and is always tokenized without a shell. The init script migrates the exact legacy formmwan3 use <member> execto structuredroute_mode=mwan3; structured Multi-WAN never accepts a free-form shell prefix.- The LuCI wizard and interface preset fill
ping_extra_args=-I <target>when the field is empty or still contains a generated-I ...value. Manual multi-argument ping args andping_prefix_stringare preserved. fping-tsandtspingdepend on reflectors that answer ICMP timestamp probes; many public DNS anycast reflectors do not.tspingis runtime-detected and not a hard package dependency; install a compatibletspingbinary manually where available before selecting it.- reflector health/replacement is implemented as an MVP;
fping-tsuses separate DL/UL OWD samples while RTT backends still use RTT/2 estimates. - The LuCI planner can scan a broader upstream default candidate pool and apply the recommended pinger method, active count, and ordered reflector list.
- Use the external LibreQoS Internet Quality Test at https://test.libreqos.com/ as a manual browser-side validation tool after configuring autorate. It is intentionally documented only, not integrated into the wizard or router-side speed test backend.
- Pinger auto-install is intentionally limited: the GUI can install/repair the
supported
fpingpackage used byfping/fping-tsand the optionalirttpackage.tspingremains a manual binary install, andirttis only ready when explicit IRTT servers are configured and clocks are synchronized. - Multi-WAN policy definitions, tracking targets, weights, metrics, and the underlying network interfaces remain router/network configuration. Full per-uplink integration requires the native nftables mwan3 backend and its member-scoped status API. The application validates and consumes that state; it does not invent a missing uplink or repair an invalid mwan3 policy.
- MQTT is an optional native sidecar rather than controller authority. It
requires a configured plain-MQTT broker,
log_to_file=1, andoutput_summary_stats=1; CPU sensors additionally requireoutput_cpu_stats=1. Broker loss terminates the sidecar so procd owns retry policy, while retained LWT marks the instance offline.
SQM integration:
luci-app-cake-autorate-rsis intended to be the single LuCI UI for SQM setup plus autorate control.- Installing the Full pair pulls
sqm-scripts,uclient-fetch,nftables-jsonandspeedtest-go.sqm-scriptsprovides the normal OpenWrt CAKE/IFB stack;uclient-fetchremains available for reflector discovery and the explicitly untrusted legacy transport diagnostic, not as a speed-test fallback. Full Auto-Tune transport validation uses the Rust probe. - The LuCI package declares
PROVIDES:=luci-app-sqmandCONFLICTS:=luci-app-sqmas the build-time replacement intent. Final OpenWrt 25.12 APK v3 metadata verification confirms that the generator emits the provide but omits a runtime conflict field. Remove the standaloneluci-app-sqmbefore installing this replacement; do not rely on the live APK solver to reject both UIs. - The UI includes the required
luci-app-sqmsettings: enable flag, interface, download/upload rates, debug logging, verbosity, qdisc, queue setup script, DSCP/ECN options, queue limits, latency targets, raw qdisc options, link layer mode, overhead, and advanced link layer parameters. cake-autorateUCI sections are the user-facing source of truth; the init script synchronizes matchingsqmqueue sections before starting SQM and autorate.- Stopping
cake-autoratealso stops SQM runtime state for sections marked as managed by cake-autorate, leaving unrelated SQM queues alone. - Disabled sections and sections with
manage_sqm=0do not mirror into SQM; stale owned SQM sections are cleaned up instead. - Multiple interface/queue pairs are represented as multiple
cake_autoratesections and shown in one compact LuCI grid.
Daemon package dependencies:
ucifpinguclient-fetchsqm-scriptsnftables-jsonspeedtest-go
LuCI package dependencies:
cake-autorate-rsluci-basesqm-scriptsuclient-fetchjsonfilternftables-json
Lite keeps only the shared daemon dependencies: uci, fping,
uclient-fetch, and sqm-scripts (plus libc). It deliberately omits the
Full-only nftables-json and speedtest-go dependencies together with the
calibration feature. Lite LuCI depends only on cake-autorate-rs-lite,
luci-base, and sqm-scripts; it ships no ACL surface for Rating, Speed Test,
Auto-Tune, scheduling, graph history, traffic classification or native Apply.
Native WebSocket and persistent-HTTP probes, including Full Auto-Tune
transport validation, use statically linked rustls and webpki roots and add no
dynamic APK dependency. The diagnostic-only legacy-http transport backend
can use uclient-fetch; normal LuCI images already provide a libustream TLS
provider and CA certificates. RC27 ships only direct APK assets, so these
dependencies must resolve through compatible configured OpenWrt feeds.
sqm-scripts pulls the required tc, CAKE, IFB, iptables, and related shaping
packages on OpenWrt.
speedtest-go is a mandatory Full dependency. Auto is backend selection
policy, not a separate implementation: it resolves to the same native
speedtest-go path. jsonfilter is a mandatory Full LuCI dependency used for
typed JSON handling.
Optional pinger backend binaries:
fpingwith--icmp-timestampsupport forpinger_method=fping-tstspingirttforpinger_method=irtt, with explicitirtt_serverentries
The daemon accepts pinger_method=tsping when the binary is present in PATH and
pinger_method=irtt when irtt is installed and explicit IRTT servers are
configured. IRTT OWD samples require the router and IRTT server clocks to be
synchronized; negative one-way delays are ignored to match upstream behavior.
tsping remains optional because no supported OpenWrt package was available on
the current test router. The advanced LuCI Reflectors tab can check pinger
backend availability, show RTT/OWD and round-robin/individual mode, install
supported fping/irtt packages when needed, scan reflectors, and apply the
recommendation into pending changes. If tsping is manually installed, the
planner can use it as the timestamp probe path when fping --icmp-timestamp is
unavailable. The create wizard writes pinger defaults and can run the same scan
before creating a new instance.
The LuCI Logging tab validates the built-in native MQTT publisher. No external
MQTT client package is needed. After setting mqtt_enabled=1, mqtt_host, and
the required summary logging options, restart cake-autorate; its Full-only
MQTT sidecar creates Home Assistant discovery sensors and publishes instance
state under the configured base topic.
Use a clean OpenWrt 25.12 SDK whose package architecture matches the required APK suffix. The Rust feed builds a large host Rust/LLVM toolchain on first use, so cache the SDK or use a prepared build image for normal iteration.
Recommended feed workflow:
cd /path/to/openwrt-sdk
cp feeds.conf.default feeds.conf
cat /path/to/cake-autorate-rs/feeds.conf.example >> feeds.conf
./scripts/feeds update packages luci
./scripts/feeds update cake_autorate_rs
./scripts/feeds install rust fping luci-base
./scripts/feeds install cake-autorate-rs luci-app-cake-autorate-rs
make defconfig
make package/cake-autorate-rs/compile V=s -j1
make package/luci-app-cake-autorate-rs/compile V=s -j1For the manual-only variant, select/build cake-autorate-rs-lite and
luci-app-cake-autorate-rs-lite instead. The daemon is compiled with Rust
default features disabled, so this is a real smaller binary rather than only a
hidden menu:
./scripts/feeds install cake-autorate-rs-lite luci-app-cake-autorate-rs-lite
make package/cake-autorate-rs/compile V=s -j1
make package/luci-app-cake-autorate-rs-lite/compile V=s -j1Overlay workflow during local development:
cp -a package/cake-autorate-rs /path/to/openwrt-sdk/package/
cp -a package/luci-app-cake-autorate-rs /path/to/openwrt-sdk/package/
cp -a package/luci-app-cake-autorate-rs-lite /path/to/openwrt-sdk/package/Enable packages in .config when building as modules:
CONFIG_PACKAGE_cake-autorate-rs=m
CONFIG_PACKAGE_luci-app-cake-autorate-rs=m
CONFIG_PACKAGE_fping=m
CONFIG_PACKAGE_rust=m
For Lite select CONFIG_PACKAGE_cake-autorate-rs-lite=m and
CONFIG_PACKAGE_luci-app-cake-autorate-rs-lite=m instead of the two Full
package symbols.
Copy the matching daemon APK plus the noarch LuCI APK to the router and install them together. Determine the daemon suffix first:
apk --print-archIf the standalone SQM LuCI application is installed, remove only that UI
package first; keep sqm-scripts, which is a required runtime dependency:
apk info -e luci-app-sqm && apk del luci-app-sqmFor example, when it prints aarch64_generic:
apk add --allow-untrusted \
/root/cake-autorate-rs-1.0_rc27-r313_openwrt-25.12_aarch64_generic.apk \
/root/luci-app-cake-autorate-rs-1.0_rc27-r120.apkFor a small manual-only installation, use the matching Lite pair instead:
apk add --allow-untrusted \
/root/cake-autorate-rs-lite-1.0_rc27-r313_openwrt-25.12_aarch64_generic.apk \
/root/luci-app-cake-autorate-rs-lite-1.0_rc27-r4.apkChanging variants is a package replacement, not an in-place feature toggle.
Back up /etc/config/cake-autorate, simulate the exact transaction first, and
install one complete pair. Moving to Lite preserves manual instance settings
but intentionally removes the rating/Auto-Tune services and their LuCI pages.
fping and sqm-scripts are pulled automatically. Optional pinger backends:
# fping-ts uses the installed fping binary; no extra package is required
apk add irtt # also configure explicit IRTT servers and synchronized clocks
# tsping is a compatible binary installed manually; ping is supplied by the base systemThe release intentionally contains only these direct APKs. The router must
have working OpenWrt 25.12 package feeds so apk can resolve runtime
dependencies. Use apk add --simulate with the same two paths first when
checking a custom image or feed configuration.
The Full daemon already depends on speedtest-go; Auto and speedtest-go
therefore require no additional backend installation. Lite intentionally has
no Speed Test backend or calibration UI.
Fresh installs contain no autorate instance and do not create an SQM queue.
Create the first one in Network → CAKE Autorate SQM → Settings as described
in the quick setup guide. A native Full Auto-Tune Review is
applied by its own Apply selected option action and reloads authoritative
UCI automatically; a manual wizard result still uses ordinary Create then
Save & Apply. Existing package upgrades retain all configured instances.
To enable an already-created instance named wan_sqm from SSH:
uci set cake-autorate.wan_sqm.enabled='1'
uci commit cake-autorate
/etc/init.d/cake-autorate enable
/etc/init.d/cake-autorate restartGraph history is opt-in per instance. The same switch is available on the
LuCI Graphs page; from the shell it can be changed with:
uci set cake-autorate.wan_sqm.graph_history_enabled='1' # use '0' to disable
uci set cake-autorate.wan_sqm.graph_history_interval_s='10' # accepted: 1-60
uci set cake-autorate.globals.graph_history_ram_budget_kib='auto'
uci commit cake-autorate
/etc/init.d/cake-autorate restartWhen enabled, history.csv is sampled at the selected interval under
/var/run/cake-autorate/<instance>/. Each row contains timestamp, RTT,
transport/effective latency, total CPU, download/upload kbit/s, safety floors,
rating phase and directional sample counts, plus a detected-grade event when
one changes. The global budget accepts auto
or one of 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
65536, and 102400 KiB. The daemon caps that request according to available
RAM and divides the effective total across enabled histories. For example,
roughly 100 MiB available permits at most 1 MiB total, while 1 GiB permits at
most 100 MiB. At less than 16 MiB available, collection pauses and releases its
history. Files are removed on service stop/reboot and never stored in flash.
cake-autorated --instance wan_sqm --dump-config
cake-autorated --instance wan_sqm --once
cat /var/run/cake-autorate/wan_sqm/status.json
/usr/sbin/cake-autorated --calibrationctl summary
/usr/sbin/cake-autorated --calibrationctl speedtest-current wan_sqm
/usr/sbin/cake-autorated --calibrationctl rating-current wan_sqm
/usr/sbin/cake-autorated --mqtt-status wan_sqm status
/usr/sbin/cake-autorated --cpu-profile 30*-current reports either the exact current operation or state=idle.
Job-specific *-status, *-result, and *-cancel commands require the public
job ID returned by the corresponding Start operation. These calibration
commands are intentionally unavailable in Lite.
For a no-shaper smoke test, disable both shaper adjustment flags:
uci set cake-autorate.wan_sqm.adjust_dl_shaper_rate='0'
uci set cake-autorate.wan_sqm.adjust_ul_shaper_rate='0'
uci commit cake-autorate
cake-autorated --instance wan_sqm --onceFor ping fallback and CPU/log smoke tests, use a temporary disabled-rate
instance with explicit counter paths, adjust_dl_shaper_rate='0',
adjust_ul_shaper_rate='0', pinger_method='ping', output_cpu_stats='1',
and a temporary log_file_path_override.
Rust is a reasonable daemon language for this project because it provides one static-ish native binary, predictable memory safety, and better long-term maintainability than a large shell daemon. The main practical cost on OpenWrt is build complexity: the first SDK build of rust/host is heavy because it compiles Rust/LLVM tooling.
For faster iteration, keep a cached SDK or CI artifact with the Rust host toolchain already built.







