1.15.0
- 插件改名为「奶昔防火墙」（NaixiFW）。原来的名字只是内嵌引擎的名字，产品不该叫这个。
  可执行文件 naixifwd、数据目录 /etc/log/naixifw、环境变量前缀 NAIXIFW_、包名 naixifw-<版本>-<架构>.ipkg
  一并改过来。引擎（DPI 核心）也独立出去了：它是本团队自己维护的私有 module
  github.com/nyarime/dpi-engine，不再以源码形式放在本仓库里 —— 它有自己的仓库、版本号、
  更新记录与授权归属，本插件只声明需要哪个版本。引擎里的授权与来源记录归它自己管
  （见该仓库的 PROVENANCE.md）。
- DPI 引擎抽成独立项目（github.com/nyarime/dpi-engine，私有仓库）。协议识别是引擎的职责，
  自研的 VLESS / IPSec / L2TP / Tailscale / ZeroTier 分析器随之归位到引擎的 analyzer 包，
  本仓库不再持有它们的源码。插件通过 module 依赖使用引擎，发布构建关掉工作区、只按
  go.mod 里声明的版本构建 —— 这样"打进包里的引擎是哪一份"是可复现、可追溯的，
  不会混入本地未提交的改动。
- 去掉全部向后兼容代码。本插件尚未对外发布，配置文件直接落在 /etc/log/naixifw/，
  不需要从旧路径迁移；旧版内核规则标记也不再被认作自己的规则。
- 清理引擎的对外品牌与来源物料：上游那份 README（描述的是它自己的 CLI 产品，并带有与
  本产品无关的政治化文案）、docs/、.github/ 不随迁；授权所需的 LICENSE 与来源记录保留。
- 新增 VLESS 协议识别。VLESS 的请求头是明文的，所以除了认出协议，还能直接读出它要访问的
  目标域名和端口 —— 面板「网站记录 / 应用分析」的"目标"一列就来自这里。判定链每一环都很窄
  （版本必须 0、附加长度必须 0、命令 1..3、地址类型 1..3），叠加起来随机数据全中的概率约
  1.8e-7。这是一条启发式规则、不是取证结论，改掉版本字节即可绕过；要用它做拦截请配合目标
  地址等条件。
- 新增 TLS 套娃检测（实验性）：识别"外层 TLS 里再套一层 TLS 握手"的形态（Trojan、
  v2ray 的 TLS 传输、各类自建隧道）—— 规则可写 nestedtls != nil && nestedtls.yes。
  内层握手的载荷是密文，链路上看不见；中间人唯一能看到的是外层 TLS 记录的**类型与长度**
  （记录头是明文，长度在加密后保留），所以这个判据本质是启发式，只看三个条件：客户端握手
  后的第一条载荷记录在 200-1600 字节（内层 ClientHello 的尺寸区间）、第二条小于 100 字节
  （内层握手的小记录）、第三条存在。只查第一条载荷记录，不做全文扫描 —— 位置本身就是判据
  的一部分。可预期误报面：首条载荷记录恰好在区间内、紧跟一条小记录、之后还有数据的正常
  客户端形态（普通 HTTP/2 因为序言只有 54 字节左右不会被命中）。
  **这一条建议先只记录、看日志里的统计，确认自己网络里的误报情况后再考虑拦截** ——
  它是在 24 条真实抓包上标定的（套娃 12 条、普通 HTTPS 12 条，全部判对），但还没有用
  真实代理流量验证过，属性里也带着 experimental=true 以便一眼看出。
- SSL VPN 厂商表从 12 家扩到 26 家：补上 Citrix/NetScaler、F5、Sophos、WatchGuard、
  Barracuda、Zscaler、Aruba、OpenVPN、SoftEther、DrayTek，以及山石网科、绿盟、启明星辰、
  奇安信、联软、锐捷，深信服的 aTrust 与 Ivanti（Pulse 后继）也纳入。这张表针对的是
  设备**出厂/默认证书**（多数管理员不会更换内部 VPN 网关的证书）；证书被换成不含厂商
  字样的仍会给出证书原文，可以按自己环境里的实际证书写规则。
- 新增 SSL VPN 产品识别：Cisco AnyConnect、深信服 EasyConnect、Fortinet、Palo Alto
  GlobalProtect、Juniper/Pulse、SonicWall、Check Point、Array、ocserv、华为、H3C、天融信。
  规则可写 sslvpn.vendor == "easyconnect"，也可以按证书原文自己写（sslvpn.cn contains "sangfor"）。
  这类协议跑在 TLS 里、握手之后的信令全是密文，网关侧读不到它的登录地址或协议载荷 ——
  唯一明文可见的判据是**服务端证书**里的厂商字样。所以判出来的是"产品身份"，不是协议内部
  结构的取证。两条边界必须知道：① TLS 1.3 下证书在 ServerHello 之后就被加密，此时只报
  sslvpn.tls13=true，判不出产品；② 管理员换成不含厂商字样的证书（自签通用域名证书常见）
  就认不出，此时仍会给出证书原文 sslvpn.cn / sslvpn.org 供自定义规则。
  实现上还要注意：证书往往跨 TCP 分段、跨 TLS 记录到达，所以入队窗口如果只看"首包"，
  要放宽到能覆盖证书（一般 <4KB），否则这个分析器看不到任何东西。
- 新增 VPN / 组网协议识别：WireGuard（已有）、OpenVPN（已有）、IKEv1/IKEv2（IPSec）、
  L2TPv2、Tailscale、ZeroTier。之前这些流量在内核和本插件眼里同样是"一段高熵字节"，
  只能靠"全加密流量"（fet）笼统归类，规则没法精确到具体协议。
- 每个协议各自一个名字，精度按可判性如实分级：
  · ipsec 最硬：版本低位必须为 0 + 交换类型合法 + 头部长度字段必须等于报文长度，
    三者同时成立才认，误报率可忽略，因此不依赖端口。
  · tailscale 靠 disco 报文里的 6 字节魔法值（"TS" + 😀 的 UTF-8 编码）确定 ——
    这个魔法值只属于 Tailscale。原生 WireGuard 握手与 Tailscale 的逐字节同构，
    光看握手分不开，所以另外补了两档：同一条五元组上先见过 disco（确定），
    以及 WireGuard 形状出现在 41641 端口上（标为 port_hint，低置信度，会有误报）。
  · l2tp 限 1701 端口，再加版本号 + 控制位 + 长度字段三重校验。
  · zerotier 只有端口 + 类型字节，代码里如实标成低置信度，不建议用于自动拦截。
- 关于"禁 Shadowsocks 会不会误伤 Tailscale"：不会。面板上的 shadowsocks/vmess 走的是全加密
  流量分析器 fet，而它是纯 TCP 分析器；Tailscale/WireGuard/IPSec/L2TP/ZeroTier 全是 UDP，
  两个分析器集合不相交。这条用测试钉住了（谁把 fet 改成也吃 UDP，测试立刻失败）。
  但要如实说清：**裸连接的 Shadowsocks 与 VMess 在流量层无法区分**（都没有可辨认的握手），
  面板上那两个开关的条件本来就是同一个。能区分的只有带握手的那些：
  VLESS（请求头明文）、Trojan（伪装成真 TLS）、以及上面这些 VPN 协议。
- 去掉所有向后兼容代码：本插件尚未对外发布，配置文件直接落在 /etc/log/naixifw/，
  不需要从旧路径迁移（未发布的版本不可能有旧路径的生产部署）。同时去掉旧版内核规则标记的
  兼容识别 —— 旧版内核规则标记不再被认作自己的规则。
  测试环境如果装过旧版，升级前先卸载旧包（旧包的 stop.sh 会清掉自己的内核规则）。
- 注意：改名会改变应用身份，应用市场里这是新应用，老版本的安装不会显示为"可更新"，
  需要卸载重装。

1.14.0
- 新增「受控设备」：可以把插件要看的流量限定在选定设备上，其余设备的流量根本不进插件。
  面板首页新增一张卡片（设备范围：全部设备 / 仅选定设备；设备清单支持 IP、网段、MAC，
  一行一个或用逗号分隔）。这是上游面板里 device_routing 那一档，本插件补上它，
  因为在此之前"只想盯住某一台设备"只能全量接管，代价是整个网络的每个包都要过用户态。
  实现：给选定设备的连接打一个 connmark（0x400，与引擎自己的 1001/1002 及爱快
  自身使用的连接标记都不冲突），入队规则再加一条 connmark 匹配。标记打在连接上
  而不是报文上，所以两个方向、后续每个报文都跟着走 —— 回程流量不需要再按
  "目的地址是内网设备"去猜。规则插在引擎入队跳转之前，与绕过名单同一位置。
- MAC 与 IP 都支持：MAC 扛得住 DHCP 换 IP（盯住"那台电脑"不会因为续租就断掉），
  IP/网段适合按用户组划范围。
- 选「仅选定设备」却留空设备清单会被拒绝，面板直接说明后果：这样一条流量都不会
  进入插件。本机模式下也拒绝这一档（本机模式处理的是设备自身的流量，没有"设备"可选）。
- 面板会提示口径差别：「仅选定设备」＋「辅助增强」下，内核已识别的流本来就不进插件，
  按设备也看不到它们的识别结果；要看全量流量要把 ikuai.scope 改成 full。
- 状态接口新增设备范围、设备清单、连接标记值与内核里实际装了几条标记规则
  （device_rules），能分清"设置存下来了"和"内核真的装上了"。
- config.yaml 的 ikuai 段新增 deviceScope / deviceList，手改文件同样生效。
- 绕过规则的计数不再把设备标记规则算进去（两者在面板上是两个数字，含义不同）。

1.13.4
- 修一个"开着但什么都没监测到"的静默失效：「仅透视」一条规则都不生成。
  面板三个协议按钮（全加密流 / TLS 伪装 / SSH）发的是 audit，后端只认 block，
  于是设成「仅透视」后 rules.yaml 里没有对应规则 —— 而引擎只加载规则里用到
  的分析器，所以这类流量压根不进分析流程：面板上「异常流量」卡片恒为 0，
  「实时流量透视看板」永远空白，操作者看到的是"没有异常流量"。
  更糟的是面板自己在教操作者用这个模式：「把协议设为仅透视即可在此实时查看」。
  现在「仅透视」生成一条只记录不拦截的规则（log: true，不写 action），
  命中即记录、流量原样放行；面板上三个协议卡片与看板因此才有数据。
  不认识模式值（拼错、上游新值）只会"少拦"，绝不会变成拦截。
- 「流量透视看板」不再拿日志当数据源。原来它读日志环再按文本猜协议，
  而日志环按等级与每秒预算裁剪：日志等级一调到 warn，或者一秒超过 500 条，
  看板就整页空白 —— 这跟"确实没有异常流量"长得一模一样，是测量功能最坏的错。
  现在引擎在日志门禁之前写入独立的透视缓冲（500 条，记录双向 IP 与端口、动作、
  命中规则、单调递增 id），记录不再受日志等级影响；新增的 SSE 事件让看板实时更新；
  「清空记录」只清看板，不再顺手动日志。「总分析包」等统计磁贴的计数行为不变。
- 辅助增强下的「仅透视」规则同样静默无效（内核分类过的流不再进插件，插件只看得到
  握手、看不到载荷），面板警告与启动日志现在把这类规则一并列出，而不是只报拦截类。
- 验证方式补强：新增端到端测试，用内存合成报文（裸全加密流 / SSH / 正常 TLS）
  跑真实的内嵌引擎，走「面板保存 → 生成 rules.yaml → 引擎编译 → 实际识别」全链路，
  断言「仅透视」下识别出全加密流量与 SSH、正常 TLS 不误报、且不切断任何流量。
  修好前该测试失败并直接指出"仅透视没有生成任何规则"。
- 面板模型新增透传保留上游的 device_routing 段：本版前端没有它的界面，
  但保存时会整体回写模型，之前被丢弃的字段会在操作者动任何一次设置后永久丢失。

1.13.3
- 修 1.13.2 没生效的 worker 上限：配置文件加载阶段把 workers.count 钳在 1-64
  （沿用旧值 + 记一条说明），而校验函数里的新上限 1-256 永远看不到这个值 ——
  实测在真机上把 count 改成 128 保存，接口回"已保存并重新加载"，但实际仍是 2。
  现在上下限抽成 spec.WorkersMin / WorkersMax 一处定义，加载与校验共用；
  新增测试同时钉住"128 原样保留、999 沿用旧值且有说明、加载结果能过校验"。

1.13.2
- 工作线程数上限从 64 放宽到 256：插件也部署在 128 核的机器上，64 线程把引擎
  卡在硬件之下（仍是合理性边界，不是推荐值 —— 家用路由远在到达上限前就先 CPU 瓶颈了）。
- readme 增加「全灭」模式的推荐配置与实测开销（约 17000 包/秒·核），并写明该模式
  能拦什么、拦不住什么：非 TLS 的全加密流量与 SSH 能拦；TLS 伪装的 Trojan / VMess
  按设计豁免（否则会误杀全部 HTTPS）；UDP 不处理（fet 只处理 TCP）。

1.13.1
- 识别范围警告补上「更省 CPU」的改法：不必把全部流量都拉进插件，可以 ikuai.scope=full
  配 ikuai.queueOnly=custom + ikuai.queuePorts（如 "53,80,443" 或某协议的服务端口），
  只让这些端口的流量进插件 —— 内核不再替这些端口做判断，于是插件能看到流的开头，
  载荷类规则就能生效。1.13.0 的警告只给了"全量接管"一条路。
  已在真机确认该组合生成的队列规则只含端口条件（不含 appmark/ikverdict 限制）。

1.13.0
- 修一个"开了但不会生效"的静默失效：识别范围为「辅助增强」时，依赖报文载荷的拦截
  规则根本不会命中，而界面上完全看不出来。现在会明确提示：
    * 面板顶部显示黄色警告条，列出受影响的规则名，并说明改法（改成「全量接管」）。
    * 启动日志里同样打印一条带 ⚠️ 的说明。
    * /api/rules 新增 warnings 字段。
  原因：辅助增强只把「内核未分类」的报文交给插件，而内核是在一条流开始带载荷之后
  才给它分类的 —— 于是这类流只有握手报文会到插件。域名 / SNI / Host / 协议屏蔽都
  要靠载荷才能判断，因此永远匹配不上。
  只对「拦截」类规则报警：只记录不拦截的规则不报警（默认安装不会挂常驻警告），
  IP / 端口这类不依赖载荷的规则也不报警。

1.12.0
- 配置文件位置改到持久分区：/etc/log/naixifw/config.yaml 与 /etc/log/naixifw/rules.yaml。
  原来在 /etc/naixifw，那属于固件分区（实测只有 129MB），固件升级或恢复出厂会把配置抹掉。
  升级时自动迁移：老位置的文件会被复制到新位置并逐字节校验，确认无误后才删除原文件；
  新位置已有文件时不覆盖。迁移会在启动日志里说明。
  另外处理了一个升级陷阱：应用设置里的路径是安装时一次性写入的，升级不会更新，
  所以老安装启动时仍被指向 /etc/naixifw；该路径现在会在读取时自动重定向到新位置
  （只针对恰好等于老默认值的路径，自己改过的路径不动）。
- 「原生配置预览」新增「重置为出厂设置」：把该文件恢复出厂内容。
  重置 rules.yaml 会同时清空面板上配置的规则（否则面板会显示一批不在文件里的规则）；
  重置 config.yaml 会把拦截恢复为关闭。两项都会先确认再执行。
- 发布闸门新增：默认路径必须在 /etc/log 下、迁移与重置逻辑必须存在、重置端点必须注册。

1.11.0
- 「原生配置预览」现在可以直接在面板里编辑并保存（原来只能看）。
  两个文件（config.yaml / rules.yaml）各自有「编辑 / 保存 / 取消」；保存先过校验：
  配置文件要能解析成有效设置（例如 log.level 只能是 debug/info/warn/error），
  规则要能被引擎编译。不通过就不写盘，并把原文件还原、把引擎的错误原文回给你，
  所以贴坏内容不会把插件弄成起不来的状态。保存成功即时生效（热加载）。
  上游那份面板的这块本来是只读的，编辑功能是本插件加的（独立脚本，上游前端保持原样）。

1.10.0
- 管理面板换成上游面板的前端，本插件自研面板退役。
  为什么：自研面板是与上游并行的第二套界面与第二套参数定义；上游那套是现成的、功能更全的
  中文界面（域名/IP/端口规则、屏蔽、流量透视、实时日志、环境诊断、规则仿真测试）。
  做法：把它的前端资产（index.html / css / js / i18n）原样内置进本插件，并按它前端实际调用的
  21 个 /api/* 端点实现后端 —— 底层是**本插件自己的引擎**，不是上游的 dashboard 二进制。
  为什么不直接跑上游的 dashboard 二进制：它在启动引擎前检查 /sys/module/nft_queue，
  而 iKuai 把 NFQUEUE 编进内核（不是模块），该前置条件在本固件上永远不成立（实测被拒）；
  它还会用 nftables 自己写防火墙规则，而 iKuai 没有 nftables。
- 调度方式沿用本插件的「内核优先」：内核 DPI 先判，只有内核未能识别的流量才进入本插件队列
  （爱快自身的 appmark/ikverdict 语义），与云防火墙那类内核调度一致，不是把全部转发流量
  都搬到用户态。
- 面板的规则模型（域名/协议开关、IP、端口、屏蔽）由本插件翻译成引擎的表达式规则：
  域名按 DNS 名称 / TLS SNI / HTTP Host 分别生成，子域用 hasSuffix（DNS 名称带末尾点、
  SNI 与 Host 不带，写错就会静默不命中）；生成的规则经引擎编译器校验通过才落盘。
- 面板生成的规则写在 rules.yaml 的标记行之后，标记之前的操作者规则**逐字保留**（含注释与
  格式），面板只重写自己那一段。
- 面板里的「DNS 污染」「反诈 HTTP 跳转」以及屏蔽的「仅记录」模式，本引擎没有对应实现：
  设置会被保存但**在面板上明确说明不生效**，不会假装生效。
- 「安装内核模块」按钮在本固件上如实回答「NFQUEUE 已内建」——上游那一版正是卡在这里。
- 环境诊断给出 iKuai 实际相关的检查项（NFQUEUE 内核接口、iptables 可读性、挂载一致性、
  当前调度方式），只诊断不改内核。
- 原始配置编辑（config.yaml / rules.yaml）保存前先校验，内容非法则拒绝并**还原原文件**。
- 本插件自己的 /api/v1/* 保持不变：发布闸门与验收脚本用它，且它带有面板放不下的字段。
- 面板头部的版本徽标改为显示**本插件**的版本（原来显示上游 dashboard 自己的 v1.1.2-14）。
- 面板总览的统计磁贴接上真实数据，不再恒为 0：
    * 「总分析包」= 内核交给本插件的报文数（读 iKuai iptables 规则自带的 -c 计数，
      读不到时不显示 0 而是保持 0 并在 status 里以 queue_counters_ok 标明"未测到"）；
    * 「已拦截流量包」= 引擎的拦截判定数（本引擎按流判定，用户态拿不到逐包拦截数，
      因此这里给的是判定次数，不是编造出来的包数）；
    * 「全加密代理 / Trojan / SSH」等卡片 = 引擎识别并命中的次数。
  计数在日志等级门禁**之前**累加：否则把日志调到 warn 就会让面板数字变成 0，
  看起来像"没有流量"，这是最坏的一种错。
  说明：fet（全加密流量）无法区分裸 VMess 与 Shadowsocks，两者合并计入
  「全加密代理 (SS / 裸 VMess)」这一张卡，不编造拆分。

1.9.0
