Repository navigation
Releases
v1.22.1
v1.22.1 — 快照与回滚 + 持久变更审计
优先级 3 安全网:所有配置变更可退,变更历史重启不丢。
快照与回滚(netsteer-snapshot)
- 档位(mode)/ 总开关与设备加速(qos)/ 向导(wizard)/ 不抢网设备列表(bulk)四类写操作变更前自动快照
- 快照内容:netsteer + sqm 全量 uci、firewall offloading 字段、当时档位;存
/etc/netsteer/snapshots/,保留 20 份,同标签 5 分钟去重(防连点/自愈刷屏) restore <id|latest>:uci 恢复 → sqm restart → fw4 reload → netsteer-apply 重对齐运行态 → 重放 nft 规则层(P2P 直通按恢复档位重建、游戏通道 qos reapply)——规则不在 uci 里,纯 uci 恢复会漏这一层(真机实测发现并补上)- 回滚前自动另存 pre-restore 快照:回滚本身可回滚;
/etc/config/*.pre-restore兜底防 import 失败 - 不整包恢复 firewall(只还原 offloading 单字段)——整包恢复防火墙风险过高
持久变更审计
/etc/netsteer/history.log:save/auto/restore 全记录 + check 的状态变化双写;重启不丢(v1.22.1 前 /tmp 版重启即失是盲区);2000 行滚动留 800- 仅 state 变化时写 /etc,不伤 flash
UI
- 专家调教页新「快照与回滚」卡:快照计数 + 最近一份 + 最近 6 条变更时间线 + 带确认弹窗的回滚按钮
真机验证(R5C)
- mode 切换触发自动快照(快照内 mode 字段=变更前状态)✅
- restore latest:standard→p2p 档位恢复、cake 在位 ✅
- 连续回滚:pre-restore 链(回滚可回滚)✅
- 指定恢复 p2p 态快照:nft 直通规则 3 条重放重建 ✅;standard 态 0 条(不误建)✅
- snapshot 单测 27 项(CI 新增步骤)+ e2e 19 项全绿 + doctor VERDICT OK
Full Changelog: v1.22.0...v1.22.1
v1.22.1 — Snapshots & rollback + persistent change audit
Priority-3 safety net: every config change is reversible, and the change timeline survives reboots.
Snapshots & rollback (netsteer-snapshot)
- Auto-snapshot before write operations across four channels: mode switches, master toggle / device boost (qos), wizard apply, bulk device list
- Snapshot contents: full netsteer + sqm uci, firewall offloading field, mode at the time; stored at
/etc/netsteer/snapshots/, 20 kept, 5-min same-label dedup (anti double-tap / self-heal spam) restore <id|latest>: uci restore → sqm restart → fw4 reload → netsteer-apply runtime re-align → replays the nft rules layer (p2p passthrough rebuilt per restored mode, game lane via qos reapply) — rules don't live in uci, a uci-only restore would miss them (found in device testing and fixed)- Auto pre-restore snapshot before every rollback: rollbacks themselves are rollback-able;
/etc/config/*.pre-restorefallback guards against import failure - firewall is never wholesale restored (only the offloading field) — restoring the whole firewall package is too risky
Persistent change audit
/etc/netsteer/history.log: all save/auto/restore events + check's state-change lines; survives reboots (before v1.22.1 the /tmp copy vanished on reboot); rolls at 2000 lines keeping 800- /etc is written only on actual state changes — no flash wear
UI
- New "Snapshots & rollback" card on the expert page: snapshot count, latest id, last 6 timeline entries, and a rollback button behind a confirmation modal
Device verification (R5C)
- Mode switch triggers auto-snapshot (snapshot's mode field = pre-change state) ✅
- restore latest: standard→p2p mode restored, cake in place ✅
- Chained rollback: pre-restore chain (rollback the rollback) ✅
- Restoring an explicit p2p snapshot: 3 nft passthrough rules rebuilt ✅; standard state stays at 0 (no false rebuild) ✅
- 27 snapshot unit tests (new CI step) + 19 e2e checks green + doctor VERDICT OK
Full Changelog: v1.22.0...v1.22.1
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.22.0
v1.22.0 — 负载延迟探测 + 过热保护
亮点
负载延迟探测(缓冲膨胀,诚实边界版)
总览页新增「缓冲膨胀」卡:一键测"空闲延迟 vs 负载下延迟",8 流 CDN 下载产生真实下行负载,同窗 ping 采样,差值 + 负载规模(load_mbps)一起落盘。
- 负载不足(<50Mbps)时判
weak并明示"结果仅供参考"——直接来自实测教训:负载没拉起时 0ms 差值是假阴性 - 判定分级:稳定(<10ms) / 轻度(<30) / 中度(<100) / 重度(≥100)
- 诚实边界:CDN 单流被恒限 ~24Mbps,本机 8 流负载上限 ~200Mbps——千兆线路上本探测验证的是整形健康度与容量下界,不是满载 bufferbloat(满载需 flent 式外部负载源)。README 与脚本头均如实写明
过热保护(thermal guard)
netsteer-check(每分钟 cron)采样最热温区:
- 数值落
/tmp/netsteer/health.json(内存盘,每分钟写不伤 flash;跨阈值事件才写 uci/log) - ≥85°C:日志告警,10 分钟防刷屏
- ≥95°C:自动降档 steering(过热保护优先于吞吐),事故日志落
/etc/netsteer/incidents/thermal-*.log;温度回落不自动升档(宁漏勿误),UI 提示手动恢复 - 总览页新增「设备温度」卡:正常绿 / 偏热(≥85°C)无色 / 过热(≥95°C)红
其他
netsteer-audit新增health(温度)、mtu_fix(MSS 钳制检出)、bloat(探测结果透传)字段- rpcd ACL 白名单新增
netsteer-bloat - 修复:
for z in firewall.@zone[*]被 shell glob 吞掉导致 mtu_fix 恒 0(改 uci show 解析)
真机验证(R5C aarch64)
- 正常态:8 流负载 159Mbps,idle 5ms → loaded 6ms,bloat 1ms =
ok(cake 整形工作正确的判定) - SQM 关闭态:116Mbps 负载无整形,无排队 →
ok(不满载不误报,宁漏勿误) - 温度监测:health.json 实时更新(46°C),UI 温度格渲染正确
- 单测 431 全绿(含 bloatMeta/tempMeta 18 项新断言)/ e2e 18 项全绿 / doctor VERDICT OK
Full Changelog: v1.21.0...v1.22.0
v1.22.0 — Loaded-latency probe + thermal guard
Highlights
Loaded-latency probe (bufferbloat, honest-boundary edition)
New "Bufferbloat" card on the dashboard: one tap measures idle latency vs loaded latency. An 8-stream CDN download generates real downstream load, ping samples the same window, and both the delta and the measured load (load_mbps) are persisted.
- When load fails to ramp (<50 Mbps) the verdict is
weakwith an explicit "reference only" note — a direct lesson from field testing: a 0 ms delta with no load is a false negative - Verdict tiers: stable(<10ms) / light(<30) / mid(<100) / severe(≥100)
- Honest boundary: CDNs rate-limit each stream to ~24 Mbps; on-device 8-stream load tops out around 200 Mbps — on gigabit links this validates shaping health and a capacity lower bound, not full-saturation bufferbloat (which needs flent-style external load). Stated plainly in both READMEs and the script header.
Thermal guard
netsteer-check (per-minute cron) samples the hottest thermal zone:
- Values go to
/tmp/netsteer/health.json(tmpfs — per-minute writes never touch flash; uci/log writes only on threshold crossings) - ≥85°C: logged warning with 10-minute anti-spam
- ≥95°C: auto-downgrade to the steering profile (thermal protection beats throughput), incident log at
/etc/netsteer/incidents/thermal-*.log; no auto-restore on cooldown (better-silent-than-wrong) — the UI tells you when to re-enable - New "Device temperature" card on the dashboard: green normal / plain ≥85°C / red ≥95°C
Other
netsteer-auditgainshealth(temperature),mtu_fix(MSS-clamp detection),bloat(probe passthrough)- rpcd ACL whitelist adds
netsteer-bloat - Fixed:
for z in firewall.@zone[*]swallowed by shell globbing made mtu_fix always 0 (now parsed via uci show)
Device verification (R5C aarch64)
- Normal: 8-stream load at 159 Mbps, idle 5 ms → loaded 6 ms, bloat 1 ms =
ok(correct reading for healthy cake shaping) - SQM off: 116 Mbps load unshaped, no queueing →
ok(no false alarm on an unloaded link) - Thermal: health.json live updates (46°C), UI card renders correctly
- 431 unit tests green (18 new bloatMeta/tempMeta assertions) / 18 e2e checks green / doctor VERDICT OK
Full Changelog: v1.21.0...v1.22.0
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.21.0
v1.21.0 — 场景注入 e2e + lint 门禁
版本特色
场景判定第一次有了自动化验证。以前改一次 sense.awk 的判定逻辑,只能靠人工在真机上制造流量再肉眼判断——现在有独立的注入测试:真实流量 → 观测面 → 引擎判定,端到端跑。
同时把 JS lint 接进 CI,存量未使用声明清零并设为门禁。
新增
tests/e2e/inject_test.py— 场景注入 e2e(独立脚本,需双 pod 环境,无 pod 自动跳过)- streaming 场景:真实下载
www.youtube.com(--resolve固定真实 IP 绕开 fake-ip,SNI 保留域名),轮询采样直到引擎产出ev_stream_dom - download / gaming 场景:UDP 打 6881(BT 下载端口)/ 27015(Steam),切 conntrack 观测面后验证端口证据进引擎
- 观测面改动后自动恢复
auto - 已挂进
e2e.py --inject(默认不跑,避免 CI 无 pod 环境误报)
- streaming 场景:真实下载
.oxlintrc.json— oxlint 配置:_前缀视为有意忽略;no-underscore-dangle关闭- CI lint 门禁:
no-unused-vars/no-unused-expressions必须归零,否则 CI 失败
修复
- JS 存量未使用声明清零(48 → 0)
- 27 处
catch (e)→catch (_e) - 16 处未使用参数/变量加
_前缀 - 4 个从未被调用的死代码函数删除:
leaseSuggestions/netdevParse/speedTier/tierFrom - 1 处三元表达式副作用改写为 if/else
- 27 处
- 注入测试踩坑记录(已写入脚本注释)
netsteer-qos dnslog输出的是 JSON,判定引擎吃的是 TSV 七字段——必须走dnslog | qos_json2tab | exclude.awk | sense.awk(netsteer-qos sense是封装)- 注入流量必须真正经过路由器透明代理,否则 mihomo 连接表里没有该来源,域名证据拿不到
- 连接关闭即出表,采样必须在注入进行中轮询
- LAN→LAN 流量不过防火墙 conntrack,端口类证据必须打公网
验证
- 单测 413 项全绿
- e2e 18 项全绿(含真机 JS 页面渲染,版本 v1.21.0)
- 注入 e2e 4 项全绿(streaming
ev_stream_dom产出、downloadev_download产出、gamingev_gaming产出、观测面恢复) - 候选包真机升级成功,doctor VERDICT OK
v1.21.0 — Scene-injection e2e + lint gate
Highlights
Scene detection finally has automated verification. Previously, changing the detection logic in sense.awk meant manually generating traffic on a live router and eyeballing the result — now there is a dedicated injection test: real traffic → observation plane → engine verdict, end to end.
JS lint is now wired into CI, with the legacy unused-declaration backlog cleared to zero and gated.
Added
tests/e2e/inject_test.py— scene-injection e2e (standalone script; requires two compute pods, auto-skips without them)- Streaming scene: real download of
www.youtube.com(--resolvepins the real IP to bypass fake-ip while keeping the SNI domain), polling until the engine producesev_stream_dom - Download / gaming scenes: UDP floods to 6881 (BT) / 27015 (Steam); after switching the observation plane to conntrack, port evidence is verified through the engine
- Observation plane is restored to
autoafterwards - Wired into
e2e.py --inject(off by default so CI without pods never false-fails)
- Streaming scene: real download of
.oxlintrc.json— oxlint config:_-prefixed names are treated as intentionally ignored; four functions covered by unit tests are exempted fromno-unused-vars(they are invoked via eval-injected extraction incore.test.js, not dead code)- CI lint gate:
no-unused-vars/no-unused-expressionsmust stay at zero or CI fails
Fixed
- JS unused-declaration backlog cleared (48 → 0)
- 27×
catch (e)→catch (_e) - 16× unused params/vars prefixed with
_ - 1× ternary-with-side-effects rewritten as if/else
- 27×
- Injection-test pitfalls (documented in the script)
netsteer-qos dnslogemits JSON; the engine consumes 7-field TSV — the production pipelinednslog | qos_json2tab | exclude.awk | sense.awk(netsteer-qos sense) must be used- Injected traffic must actually traverse the router's transparent proxy, otherwise the mihomo connection table has no row for the source and domain evidence never appears
- Connections leave the table when closed — sampling must poll while injection is in flight
- LAN→LAN traffic bypasses conntrack; port-class evidence must target public addresses
Verification
- Unit tests: 413 green
- e2e: 18 green (incl. real-device JS page render, version v1.21.0)
- Injection e2e: 4 green (streaming
ev_stream_dom, downloadev_download, gamingev_gaming, observation-plane restore) - Candidate package upgraded on the live router;
netsteer-doctorVERDICT OK
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.20.0
v1.20.0 — 反馈闭环:一键诊断 + 泄漏修复
版本特色
把"报障"从聊天记录变成一条命令。netsteer-doctor 十段输出 + VERDICT 判定,配合 issue 模板构成完整反馈闭环——这是项目从单人内部产品走向可被陌生人使用的关键一步。同时修复了 7×24 soak 监控首日抓到的 sense-daemon 泄漏。
新增
netsteer-doctor一键诊断命令:SSH 到路由器执行即可收集定位问题所需的全部信息——包版本/环境/依赖组件/SQM 整形/硬件分载/观测面/场景引擎/加速通道/转向状态/配置与日志尾十段,末行VERDICT判定(OK/WARN/FAIL)。已知边界自动标注:缺依赖、flow offload 导致计数冻结、daemon 泄漏、DNS 观测降级、SQM 未启用、游戏通道缺 IP、state 未初始化- GitHub issue 模板:bug report(doctor 输出为必填项)与 feature request,中英双语
- README「排障与反馈」章节(中英双语)
修复
- sense-daemon 进程泄漏(7×24 soak 监控首日实锤:单机累积 3 个孤儿进程),四层修复:
- 函数级防重:daemon 启动前校验 pid 文件 +
/proc/<pid>/cmdline双证据,直调秒退(此前任何绕过 init.d guard 的调用路径都会再起一个进程并覆盖 pid 文件) - init.d 补
stop():旧版restart()=start()导致升级后旧 daemon 永生(一直跑旧代码) - apk postinst 由
start改restart:升级时真正换新 daemon - trap 退出清理 pid 文件前校验属主:修复升级窗口内旧 daemon 退出误删新 daemon pid 文件的竞态(busybox sh 在 sleep 结束后才处理信号,窗口最长一个轮询周期)
- 函数级防重:daemon 启动前校验 pid 文件 +
实测
- 真机(NanoPi R5C / OpenWrt 25.12.6):
restart 后 daemon 数量=1;直调netsteer-qos sense-daemon退出码 0 且进程数恒 1;doctor 输出VERDICT: OK,数值字段(SQM 带宽/分载标志)与 audit 原始值一致 - e2e 新增 2 项(doctor 十段断言 + 直调防重断言),16→18 项全绿;单测 413 项全绿
已知边界
- doctor 的 daemon 泄漏检测只报告不回收(
/etc/init.d/netsteer restart可手动回收);升级到本版本后存量泄漏进程需要一次 restart - 观测面结论依赖 netsteer-qos env 缓存,代理内核刚启停时 doctor 显示的通道可能滞后一个探测周期
v1.20.0 — Feedback loop: one-command diagnostics + leak fix
Highlights
Turns bug reporting from chat logs into a single command. netsteer-doctor prints ten sections plus a VERDICT line, and pairs with the new issue templates to close the feedback loop — the step that takes this from a single-user internal tool to something strangers can actually run. Also fixes the sense-daemon leak caught on day one of 7×24 soak monitoring.
Added
netsteer-doctorone-command diagnostics: run it over SSH to collect everything needed to debug — package version, environment, dependencies, SQM state, offload flags, observation channel, scene engine, acceleration tunnels, steering state, config and log tail — ending with aVERDICTline (OK/WARN/FAIL). Known boundaries are flagged automatically: missing deps, flow-offload counter freeze, daemon leaks, degraded DNS observation, SQM disabled, game tunnel without IP, uninitialized state- GitHub issue templates: bug report (doctor output required) and feature request, bilingual
- "Troubleshooting & feedback" section in both READMEs
Fixed
- sense-daemon process leak (caught on day one of 7×24 soak: 3 orphaned daemons on one box), fixed in four layers:
- Function-level guard: daemon startup verifies pid file +
/proc/<pid>/cmdlineas dual evidence; direct invocation exits immediately (previously any path bypassing the init.d guard spawned another process and overwrote the pid file) - init.d gains a real
stop(): the oldrestart()=start()meant a daemon survived every upgrade, forever running old code - apk postinst now runs
restartinstead ofstartso upgrades actually swap the daemon - The TERM trap now checks pid-file ownership before deleting: fixes a race where an old daemon's exit removed the new daemon's pid file during the upgrade window (busybox sh handles signals only after
sleepreturns, so the window lasts up to one poll interval)
- Function-level guard: daemon startup verifies pid file +
Tested
- On-device (NanoPi R5C / OpenWrt 25.12.6): daemon count = 1 after restart; direct
netsteer-qos sense-daemoninvocation returns exit code 0 with process count unchanged; doctor reportsVERDICT: OKwith numeric fields (SQM bandwidth, offload flags) matching raw audit output - e2e extended by 2 checks (doctor ten-section assertions + direct-invocation guard), 16→18 all green; 413 unit assertions all green
Known limits
- Doctor reports daemon leaks but does not recycle them (
/etc/init.d/netsteer restartdoes); pre-existing leaked processes need one restart after upgrading to this version - Observation-channel findings rely on the netsteer-qos env cache; right after a proxy core starts or stops, the channel shown by doctor may lag by one probe cycle
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.19.3
v1.19.3 — 判定引擎修复:L2 识别链、带宽腐蚀与 DSA 适配
本版核心修复来自外部贡献者 PI-Cursor DS 的全库代码审查(PR #1),经维护者行为级独立审计后合并。
修复
P0 · 场景识别 IP 段/端口链全失效:sense.awk 两处变量链断裂(分类写入局部变量、调用方读从未赋值的全局名),导致游戏 IP 段与端口规则两条识别链 100% 落空——裸 IP 直连流量(游戏/P2P 的核心形态,无域名、无 SNI)此前永远判不出场景。修复后裸 IP 直连正确命中游戏段,非游戏 IP 不误判。同一处还有一个跨记录累乘缺陷:网段基准值逐条翻倍溢出,即使分类修好也匹配不上,已一并修复。
P1 · 带宽反复切换被腐蚀:进入延迟优化档时带宽 ×0.9 写回但不留基准,反复切换档位会让 SQM 带宽一路缩水(实测 4 轮 -27%)。现首次降速前把真实带宽存档,此后降速与恢复都以存档为基准,标准档恢复原始带宽。
P1 · 「vs 基线」增益层从未渲染:吞吐图增益层引用了错误的变量名,计算结果为 NaN 被图形层静默吞掉。修复后 A/B 对比的增益线正常绘制。
P1 · netsteer-apply 参数被静默吞掉:任何调用一律全量应用,传参无效。现支持 apply / clear / preset {all|balanced|off} / auto,未知参数报错退出。
P2 · DSA 命名设备适配:吞吐采样与分核应用此前硬编码 eth0/eth1,在 lan1/wan 命名的现代 OpenWrt 设备(DSA 交换芯片主流形态)上采样为零、配置失效。现通过 sysfs 物理口探测与 WAN 角色判定(ubus → uci → 默认路由逐级回退)动态适配,pppoe-wan 等虚拟口正确归入承载层。
P2 · 状态文件原子写:场景状态落盘改为临时文件 + 原子替换,前端轮询不会再读到半截内容。
CI · 版本守卫加固:PR 事件的 N/merge 引用名此前可穿过版本守卫使构建崩溃;现仅接受完整 N.N.N 形态,构建脚本对版本串做字符白名单校验。
验证
单测 413 全绿(场景识别 21→26,含裸 IP 直连、非首条 IP 规则、端口链协议不匹配等新增断言);真机 e2e 16/16;核心修复均在真实路由器上复现验证(修复前裸 IP 判 normal → 修复后判 gaming)。本版无新功能,调度逻辑不变,升级即用。
v1.19.3 — Detection engine fixes: L2 classification chain, bandwidth corrosion, DSA adaptation
The core fixes in this release come from a full-repo code review by external contributor PI-Cursor DS (PR #1), merged after an independent, behavior-level audit by the maintainer.
Fixes
P0 · Scene classification: IP-range and port chains never fired. sense.awk had two broken variable chains (the category was written to a function-local variable while the caller read a never-assigned global name), which silenced the entire IP-range and port-rule classification paths — bare-IP direct traffic (the core pattern of gaming and P2P: no domain name, no SNI) could never be classified. After the fix, bare-IP direct connections correctly match game ranges, and non-game IPs are not misclassified. A companion defect in the same code path — the range base value multiplying across records until it overflowed — is fixed as well; even with the category fixed, no range would have matched.
P1 · Repeated mode switching corrupted bandwidth. Entering the latency tier wrote bandwidth × 0.9 back without keeping a baseline, so repeated switching kept shrinking the SQM bandwidth (measured −27% over four cycles). The true bandwidth is now archived before the first downshift; both the downshift and the standard-tier restore work from the archive.
P1 · The "vs baseline" gain layer never rendered. The throughput chart's gain layer referenced a wrong variable name, producing NaN that the SVG layer silently swallowed. The A/B gain line now draws.
P1 · netsteer-apply silently ignored arguments. Every invocation ran a full apply regardless of arguments. It now supports apply / clear / preset {all|balanced|off} / auto and rejects unknown arguments with a usage message and exit code 1.
P2 · DSA-named device adaptation. Throughput sampling and RPS/XPS application hardcoded eth0/eth1, which zeroes sampling and breaks configuration on modern OpenWrt devices with lan1/wan naming (DSA switch chips). Physical interfaces are now detected via sysfs and the WAN role is resolved through ubus → uci → default-route fallback; virtual carriers such as pppoe-wan are classified correctly.
P2 · Atomic state writes. The scene state file is now written via temp-file + atomic rename, so the polling front end can no longer read a half-written file.
CI · Hardened version guard. The N/merge ref from pull-request events previously slipped through the version guard and crashed the build. Only full N.N.N forms are accepted now, and the build script validates the version string against a character whitelist.
Verification
413 unit tests green (scene classification 21→26, with new assertions for bare-IP direct hits, non-first IP rules, and protocol-mismatched port chains); 16/16 on-device e2e; core fixes were reproduced and verified on real hardware (bare IP classified normal before, gaming after). No new features; steering logic is unchanged. Upgrade and go.
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.19.2
v1.19.2 — 界面精致化:徽章/控件/图标系统重设计
界面
徽章系统重设计:右侧状态徽章改为封闭语义集——只承载「状态 + 规模」,措辞统一为 未开启 / 已开启(可带 · N 台),不再混装 IP 尾段与宣传语;新增标签变体(无圆点的能力标签,如「AI 智能识别」)与图标标签变体(场景图标即标记)。
控件语言统一:智能模式页按钮收敛为两态——绿描边 = 肯定操作(开启/添加/更新),灰静音 = 关停操作(关闭加速/关闭);设备标签统一为一种胶囊(建议带 +、已选带 ×,弱化尾段符号);建议区(疑似游戏设备 / 疑似后台占用 / 识别到)统一同款内嵌条容器;全部输入框统一 placeholder 式排版,三卡左缘对齐。
场景识别图标:文字符号(◈☂▶⇩)换为手绘 24px 线性 SVG 图标(游戏手柄 / 摄像机 / 圆圈播放 / 箭头入托盘 / 地球),描边跟随文字颜色、深浅主题自动适配;几何原创、无第三方版权,包内自包含离线可用。总览「当前场景」格、场景大标签、证据 chip 三处同步。
修复:吞吐量图表 X 轴时间刻度被非等比拉伸导致文字压扁——刻度移出 SVG 改为 HTML 行,永不变形;顺带根治一个潜伏缺陷:LuCI 构造 SVG 标签时不带命名空间,「实时出口延迟」曲线此前从未真正渲染过,现已修复。
无功能变更:调度逻辑、判定引擎与配置项与本版无关,升级即用。
v1.19.2 — UI refinement: badge / control / icon system redesign
UI
Badge system redesigned: card badges now follow a closed vocabulary — they carry state and scale only (未开启 / 已开启, optionally · N 台), never IP fragments or slogans. New badge variants: a neutral tag style (capability labels such as "AI 智能识别", no dot) and an icon-tag style (the scene icon itself is the marker).
Control language unified: buttons on the smart tab collapse into two states — green outline for affirmative actions (enable / add / update), muted gray for stopping actions (disable / close). Device chips become one capsule design (suggestions carry a weakened +, selected entries a weakened ×). All three suggestion areas (suspected gaming devices / suspected background usage / detected traffic) share the same inset band container. Every input follows the placeholder-only pattern; the three cards' input rows now align on the left edge.
Scene icons: the text glyphs (◈☂▶⇩) are replaced with hand-drawn 24px line SVG icons (gamepad / video camera / play-in-circle / arrow-to-tray / globe). Strokes follow the text color and adapt to light and dark themes automatically; the geometry is original artwork, free of third-party licensing, and fully self-contained in the package for offline use. Applied to the overview scene tile, the scene headline chip, and the evidence chips.
Fixes: the throughput chart's X-axis time labels were visually squashed by the non-uniform SVG stretch — labels now live in an HTML row below the chart and can no longer distort. Also fixes a latent defect: LuCI creates SVG tags without a namespace, so the real-time latency curve had never actually rendered; it now does.
No functional changes: scheduling logic, detection engine and configuration options are untouched by this release. Upgrade and go.
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.19.1
v1.19.1 — 交互一致性修复 + 配置写入通道重建
修复
「自动切换」报错根除:此前页面报「ubus uci.apply 失败:[5]」——深入查证后确认这不是系统故障:错误码 5 是 uci.apply 在「没有待应用变更」时的正常返回(官方 LuCI 页面同样如此),根因是前端把这种正常语义当致命错误弹窗,且开关状态与配置值不同步时更易触发。本版把插件全部配置写入改走 uci 命令行通道(新增 netsteer-cfg 脚本,写入即落盘),链路更简单直接,同类误报一次根治。
「自动切换」开关并入智能模式:独立开关概念冗余——都叫智能模式了,不该再问"要不要自动"。现在智能模式开启即自动联动:检测到看视频/打游戏自动切到更合适的模式,闲时恢复均衡,您手动选的档位始终优先。场景卡里的说明文字即全部,无需理解第二个开关。
顺带修复:总开关关闭时联动不再拉起优化(此前"关闭"状态下场景切换可能把优化重新打开);智能模式页不再出现 "null" 文字(控件渲染缺陷)。
变更明细
- 新增
netsteer-cfg:netsteer 配置统一写入脚本(uci set + commit 即时落盘,绕开异常的 ubus apply) - 前端 6 处配置写入全部切换到新通道(自动切换/识别排除表/设备加速/P2P 直通/自动调优标记/批量管理)
- 场景联动默认开启(
sense_linked缺省 0→1),且总开关 off 时不接管 - UI:删除「自动切换」独立卡片,智能场景卡新增一行联动说明
质量门
单测 440 项全绿(core 302 / cfg 18 / bulk 33 / sense 21 / tune 15 / qos-awk 9 / feed 11),e2e 16 项真机全绿,R5C 真机终装 + md5 对账通过。
Full Changelog: v1.19.0...v1.19.1
v1.19.1 — Interaction consistency fixes + config write path rebuild
Fixes
Root-cause fix for the "auto-switch" error: the page used to show "ubus uci.apply failed: [5]" — deep-dive verification confirmed this is NOT a system fault: code 5 is simply what uci.apply returns when there is nothing to apply (stock LuCI behaves the same). The real defect was our frontend treating that normal reply as a fatal error, made more likely by UI state drifting from the stored value. This release moves ALL plugin config writes to the uci command-line channel (new netsteer-cfg helper — write and commit immediately), a simpler and more direct path, eliminating this class of errors for good.
The "auto-switch" toggle is now part of Smart Mode: a separate toggle was conceptually redundant — Smart Mode should not ask whether to be smart. With Smart Mode on, scene linking is always active: when someone watches video or plays games the router switches to a better-suited profile automatically, and falls back when idle. Whatever profile you picked manually always wins. One line of explanation in the scene card is all there is — no second switch to understand.
Also fixed: scene linking no longer re-enables optimization while the master switch is off (previously a scene change could turn it back on); the Smart Mode page no longer renders a stray "null" text (a widget rendering defect).
Change details
- New
netsteer-cfg: unified config-write helper for netsteer (uci set + commit, effective immediately, bypasses the broken ubus apply) - All 6 frontend config writes migrated to the new channel (scene linking / detection exclude list / device boost / P2P passthrough / autotune flag / bulk devices)
- Scene linking is now on by default (
sense_linkeddefault 0→1) and never takes over while the master switch is off - UI: removed the standalone "auto-switch" card; the Smart Scene card gained one line explaining the linking behavior
Quality gates
440 unit checks green (core 302 / cfg 18 / bulk 33 / sense 21 / tune 15 / qos-awk 9 / feed 11), 16 e2e checks green on the live router, R5C on-device install + md5 reconciliation passed.
Full Changelog: v1.19.0...v1.19.1
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.19.0
v1.19.0 — 不抢网设备 (设备级带宽仲裁)
新能力
不抢网设备 (D22):把指定设备的流量标记为低优先级 (DSCP CS1),网络繁忙时自动让路——家人看视频、远程访问优先;空闲时它仍可用满带宽。适合下载机、共享盒子 (如网心云)、NAS 同步等后台设备。
与 v1.13 游戏通道对称:那是把设备升优先级 (CS4),这是降优先级 (CS1),共用同一条 nft 标记管线。
组成
netsteer-bulk:多设备列表管理 (add/remove/list/status/off/reapply) + 存量手工规则迁移检测 (migrate/migrate --apply,兼容dscp set cs1与数值dscp set 8两种写法)- 智能模式页新卡:设备 chips 点击移除、IP 添加、徽标 (已启用 · N 台让路中)、besteffort 模式提示
- 存量迁移提示:检测到 /etc/nftables.d/ 手工 CS1 规则时,卡内显示"一键迁入"
- 判定建议 (qos suggest):持续吃上行的设备自动出现在"发现疑似后台占用"条,点击即加入
- 游戏通道互斥:同一设备不可同时升/降优先级,脚本层硬校验 (ERR: game-conflict)
- boot/hotplug 兜底:重拨/重启后规则自动恢复;规则带 counter,status 可见标记包量
原理
上行 nft forward 钩子 (priority -140) 标记源地址 → WAN cake bulk tin (thresh≈带宽/16);下行 netdev ingress 钩子 (-440) 标记目的地址 → ifb cake。不切 SQM 脚本,对现有整形配置零侵入;besteffort 模式无 tin (标记保存,切回 diffserv 模式后生效,UI 有提示)。
实测 (网心云 .175 持续吃满 48M 上行场景):CS1 标记后 Best Effort 队列积压 158KB→0、延迟 21.7ms→393μs,网心云自动让路至 bulk tin 3Mbit。
测试
- 单测 6 套 422 项全绿:core 302 / bulk 33 (新增) / sense 21 / tune 15 / qos-awk 9 / feed 11
- e2e 16 项真机全绿 (含不抢网卡在位/徽标语义/互斥脚注)
- 真机链路:migrate 检测→迁入→nft 表生成→标记计数增长→reapply 兜底恢复→互斥拒绝
v1.19.0 — No-fight Devices (Per-device Bandwidth Arbitration)
New capability
No-fight devices (D22): mark a device's traffic as low priority (DSCP CS1) — it automatically yields when the network is busy: family video and remote access go first; when idle it can still use the full bandwidth. Fit for download boxes, shared-bandwidth boxes (e.g. passive-income miners), NAS sync, and other background devices.
Symmetric to the v1.13 game channel: that one raises a device's priority (CS4), this one lowers it (CS1) — both share the same nft marking pipeline.
Composition
netsteer-bulk: multi-device list management (add/remove/list/status/off/reapply) + legacy hand-written rule migration detection (migrate/migrate --apply; understands bothdscp set cs1and numericdscp set 8spellings)- New card on the Smart page: device chips (tap to remove), IP add, badge ("on · N devices yielding"), besteffort-mode hint
- Legacy migration banner: when hand-written CS1 rules are found under /etc/nftables.d/, the card offers one-click adoption
- Suggestion engine (qos suggest): devices continuously saturating the uplink surface in the "possible background hog" strip — one tap to add
- Game-channel mutual exclusion: one device cannot be both raised and lowered; hard-checked at script level (ERR: game-conflict)
- boot/hotplug self-healing: rules auto-restore after redial/reboot; rules carry counters, status shows marked packet volume
How it works
Uplink: nft forward hook (priority -140) marks the source address → WAN cake bulk tin (thresh≈bandwidth/16). Downlink: netdev ingress hook (-440) marks the destination → ifb cake. No SQM script switching — zero intrusion into existing shaping; besteffort mode has no tins (markings saved, effective once back on a diffserv mode; the UI says so).
Measured (miner box .175 continuously saturating a 48M uplink): after CS1 marking, Best Effort backlog 158KB→0, latency 21.7ms→393μs; the box automatically yields into the 3Mbit bulk tin.
Tests
- 6 suites, 422 assertions all green: core 302 / bulk 33 (new) / sense 21 / tune 15 / qos-awk 9 / feed 11
- E2E 16 all green on a real router (card present / badge semantics / exclusion footnote)
- On-device chain: migrate detect → adopt → nft table created → marking counters climbing → reapply self-heal → exclusion rejection
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.18.2
v1.18.2 — UI 一致性/响应式审计修复
发布日期: 2026-11-10
定位: 全维度 UI 审查(3 tab × 3 视口 DOM 断言 + vision 走查)后的统一性修复。
修复清单
色彩统一
- 清除 LuCI cbi 按钮色入侵:页内按钮(自动切换「开启」/「更新识别库」/「添加」)统一为自有
.ns-btn-act(描边 + --ok 语义绿),页面绿色收敛为单一 --ok (#0b7f6c);
弹层内按钮保留 LuCI modal 语言(全站弹层一致性优先) - #d35400(warn 橙) 等 4 处确认语义色,保留
字号收敛
- .netsteer-mask 的 .92em 浮点脏字号 (11.04px ×9) → 固定 var(--fs-sm,12px)
- 全页字号收敛为 13/12/14/26 四档
移动端触控 (max-width:640px)
- minibtn/ns-btn-act 触控热区 ≥36px(审计实测 <32px 违规 6→0)
- 徽章 max-width 防溢出
布局防挤
- 卡头 flex-wrap:P2P 直通标题不再断词("Fullcone 等/效")
- P2P 徽章 IP 尾段缩写(已开启 · …11.175),徽章不再折两行
- 卡片间距统一 14px→16px
状态呈现
- 「识别到:」空灰条不再在"正在观察"态渲染(有识别结果才显示,杜绝空壳)
验证
- 单测 core 302 (+p2p 短 IP 断言) / sense 21 / qos-awk 9 / feed 11 / tune 15
- e2e 15 ALL GREEN
- 三视口 DOM 复测:溢出 0、触摸违规 0、色彩/字号收敛达标
v1.18.2 — UI Consistency / Responsive Audit Fixes
Positioning: uniformity fixes after a full-dimension UI review (3 tabs × 3 viewports DOM assertions + vision walkthrough).
Fix list
Color unification
- Cleared LuCI cbi button-color intrusion: in-page buttons (auto-switch "on" / "update library" / "add") unified to the in-house
.ns-btn-act(outline + --ok semantic green); page greens converge to a single --ok (#0b7f6c); modal buttons keep the LuCI modal language (site-wide modal consistency first) - #d35400 (warn orange) + 3 others confirmed semantic, kept
Font-size convergence
- .netsteer-mask's .92em dirty fractional size (11.04px ×9) → fixed var(--fs-sm,12px)
- Whole page converges to four sizes: 13/12/14/26
Mobile touch targets (max-width:640px)
- minibtn/ns-btn-act touch area ≥36px (audit-measured violations 6→0)
- Badge max-width overflow guard
Layout anti-squeeze
- Card heads flex-wrap: P2P title no longer breaks mid-word
- P2P badge shortens the IP tail (on · …11.175), no longer wraps to two lines
- Card gaps unified 14px→16px
State presentation
- The "recognized:" empty gray bar no longer renders in the "observing" state (only with real results — no empty shells)
Verification
- Unit: core 302 (+p2p short-IP assertion) / sense 21 / qos-awk 9 / feed 11 / tune 15
- E2E 15 ALL GREEN
- Three-viewport DOM re-check: 0 overflows, 0 touch violations, color/size targets met
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.
v1.18.1 — 参数自适应 + 联动/排除 UI
v1.18.1 — D20 参数自适应 + 联动/排除 UI
发布日期: 2026-11-10
定位: v1.18 范围收口 — Task 4 参数自适应落地 + Task 3 联动/设备排除的用户面补齐。
新增
netsteer-tune (D20 参数自适应)
- 新增
netsteer-tune <tier|show>: 按档写三组参数并持久化- nf_conntrack_max 按内存自适应(根治满表断网):
clamp(mem_mb×512, 16384, 262144)
— 128MB→65536 / 256MB→131072 / ≥512MB→262144 顶格;buckets=max/4;
运行时 sysctl -w +/etc/sysctl.d/10-netsteer.conf开机重放 - rfs_sock_flow_entries: T0 16384 / T1 32768 / T2 65536
- sense-daemon 采样间隔: T0 60s / T1 30s / T2 20s(kill 后等退干净再 restart,防幂等跳过)
- nf_conntrack_max 按内存自适应(根治满表断网):
- 向导 apply 自动调用(分级完即按档调参);
show子命令输出当前参数 JSON
智能模式页两块新卡 (D14 用户面)
- 自动切换卡: 联动开关 + 一句状态行("正在检测: 看视频 → 将自动切到「低延迟优先」");
用户语言呈现,不暴露档位字母;手动选的 P2P/极速档明示不改动 - 排除设备卡: IP 列表(点击移除)+ 输入添加(IPv4 校验);
保存后 daemon 下一轮采样自动生效(每轮重读 sense_exclude,无需 reload)
修复
- netsteer-tune 首版 daemon 换活竞态:kill 后 init.d 幂等 restart 看到未退净进程会跳过拉起
→ kill 后最多等 3s + 强制 9 号兜底 + 删 pid 再 restart
测试基线
- core 301 / sense 21 / tune 15(新增,公式三档抽验)/ qos-awk 9 / feed 11
- e2e 15 ALL GREEN;真机验证: tune T1 端到端(sysctl 落地+uci 三项+daemon 换活 pid 变化)、
UI 两卡渲染与文案、联动状态行用户语言
v1.18.1 — D20 Parameter Auto-tuning + Linkage/Exclusion UI
Positioning: v1.18 scope close-out — Task 4 parameter auto-tuning + Task 3 user-facing linkage/exclusion surfaces.
Added
netsteer-tune (D20 parameter auto-tuning)
- New
netsteer-tune <tier|show>: writes three parameter groups per tier, persisted- nf_conntrack_max scaled to memory (root fix for table-full outages):
clamp(mem_mb×512, 16384, 262144)— 128MB→65536 / 256MB→131072 / ≥512MB→262144 cap; buckets=max/4; runtimesysctl -w+/etc/sysctl.d/10-netsteer.confreplay at boot - rfs_sock_flow_entries: T0 16384 / T1 32768 / T2 65536
- sense-daemon sampling interval: T0 60s / T1 30s / T2 20s (after kill, wait for full exit before restart to avoid idempotent skip)
- nf_conntrack_max scaled to memory (root fix for table-full outages):
- Wizard apply calls it automatically (tuned right after tiering);
showprints current parameters as JSON
Two new cards on the Smart page (D14 user surface)
- Auto-switch card: linkage toggle + one status line ("detecting: video → will switch to low-latency"); user language only, no tier letters; manually chosen P2P/turbo modes explicitly never overridden
- Excluded devices card: IP list (tap to remove) + input to add (IPv4 validated); saved entries take effect on the daemon's next sampling round (sense_exclude re-read each round, no reload needed)
Fixed
- netsteer-tune first-version daemon restart race: init.d idempotent restart saw the not-yet-exited process and skipped relaunch → now waits up to 3s after kill + SIGKILL fallback + pid removal before restart
Test baseline
- core 301 / sense 21 / tune 15 (new; formula spot-checks across tiers) / qos-awk 9 / feed 11
- E2E 15 ALL GREEN; on-device: tune T1 end-to-end (sysctl landed + three uci values + daemon pid change), both cards' rendering and copy, linkage status line in user language
Download the .apk matching your device architecture (check /etc/apk/arch on the router).
Install (OpenWrt 24.10+/25.12):
apk add --allow-untrusted /tmp/luci-app-netsteer-*.apk
Built automatically by CI from this tag — unit tests + 4-arch build matrix.
Every package is verified with apk verify before it is attached.