1.54.0
- 【留存压缩·你点名要的】轮转出去的段现在用 gzip 压成 .gz（最高档），最新的活动文件保持明文
  —— 排查时 tail/grep 是刚需。压缩在后台 goroutine 里做，不占写入路径；轮转本身仍然只是
  一次改名。
- 【留更久】默认保留段数从 5 提到 12：8MB × 12 段未压缩约 96MB，压完约 10MB，加上活动文件
  8MB 约 18MB —— 比原来"5 段不压缩"的 40MB 更省，而历史是 2.4 倍长。压缩省下的余量花在
  "留更久"上，这比花在"少占盘"上更符合经得起长期堆积。

  实测（2000 条真实形状记录）：普通 gzip 约 9~12 倍；重复度极高的合成样本能到 75 倍。
  面板的"留存条数"会把压缩段解开一起数，所以显示的数字仍然是真实条数。
- 【改对了一个会丢段的缺陷·测试抓到的】原来的段名是"逐级后移"（.1 → .2 → .3 …）。后台压缩器
  正在压 .1 时，一次新轮转会把 .1 改名成 .2，压缩器压完却把结果写成 .1.gz —— 内容是 .2 的。
  于是同一段在盘上出现两次，而"丢掉最老一个"执行时删掉的可能是真段，**记录就这么少了**。
  测试抓到过相邻段之间少几条。改成不可变段名（traffic.jsonl.<时间戳>）后改名不再发生，
  这个竞态从根上消失；排序靠固定宽度的 UTC 时间戳，字典序即时间序。
  顺带修掉：清空日志原来按编号删，段名不可变后会漏删 .gz —— 那会让"清空"之后面板还读得到旧记录。
- 【运行时垃圾统一 /tmp/naixifw·你定的】缓存、pid 文件、临时文件、以及本进程自己的日志
  （run.log / engine.log）全部移到 /tmp/naixifw/ 下。原来它们放在安装目录，而升级会整目录替换
  .pkg/app —— 一次升级就把正在用的 pid 文件删掉了，日志也会被清（"该留的证据被升级清掉"比丢
  缓存更糟）。放 /tmp 还多一层：重启即清空，小存储设备上"靠重启回收"比任何清理逻辑都可靠。
  注意生命周期：这些是"本次运行的诊断"，需要长期留存的是流量记录（traffic.jsonl，在持久目录
  /etc/log/naixifw 下，另有轮转与压缩）。
- 【引擎测试搬回源码旁·你选的 2】test/ 目录取消，4 个测试回到各自包的目录里。
- 【未做】io 写路径的 recover：我在测试里踩到过规则引用未加载分析器时的空指针，但那是我的测试
  写错了（没给 Logger）。收包路径要不要兜住"规则写错导致崩溃"是**产品决策**不是缺陷修复，
  没有你的话我不动它。

1.53.0
- 【判据收敛·你定的口径】「端口与协议不符」改称 **协议不一致**，两档范围按你的原话重划：
  · 宽松档**容忍**「把 25 端口拿来开 web」「把 3389 拿来开 SSH」—— 这两种在宽松档下不再判定。
  · 严格档才判，命中即拦，日志里写「协议不一致」。
  实质：原先这一整类（在别人的端口上跑服务）被放在"绝无可能"那一档，而宽松档只跑那一档，所以
  一开宽松就会把自己家网络上"奇怪但正常"的服务误拦 —— 这正是会让操作员永久关掉一个功能的那类
  误报。现在宽松档只保留真正跑不通的组合。
- 【宽松档剩下什么】只剩一条：**443 端口上的明文HTTP**。它不属于"有人故意这么配"，而是
  "这么配根本连不上" —— 全世界的浏览器都当 443 是加密端口，出现明文说明中间有人拆掉了 TLS。
  这一档在任何模式下都敢拦，因为它不依赖任何阈值。
- 【补一个漏掉的端口】5900（VNC）加入远程管理端口组，与 3389（RDP）、445（SMB）同组。
- 【引擎结构按你的要求固定】引擎不再在根目录长新文件夹，IANA 端口表与 Cloudflare 地址段移入
  analyzer/ 之下；根目录固定为 analyzer / ruleset / engine / io / modifier / docs / test。
  插件侧有闸门盯着这件事，日后再加会构建失败。
- 【引擎测试搬进 test/】只用导出接口的验收测试移入引擎的 test/ 目录；需要访问包内私有符号的
  测试搬不了 —— Go 规定测试文件必须与源码同目录才能访问私有符号，这是语言限制不是偏好。

1.52.0
- 【新增·「端口与协议不符」类目】判"矛盾"而不是"可疑"：某个端口上出现了绝不属于它的东西。
  判据数据来自引擎内置的 **IANA 端口注册表**（714 条：0-1023 全部公认端口 + 一组有实际流量的
  注册端口，由 IANA 的 CSV 生成而非手抄），分组出"这个端口不可能承载什么"。
  例：SMTP 的 25 端口上跑Web明文流量 —— 这正是你举的例子。它比"非标端口"强在：非标端口只是
  事实（可能完全正常），而矛盾是两个独立事实互相打架，误报率因此有界。
- 【宽松/严格两档】判据分两级，面板上可切换：
  · 宽松（默认）：只用"绝无可能"一级 —— 邮件端口跑Web、共享/远程管理端口跑Web、数据库端口跑Web、
    这些端口上出现SSH。敢拦，因为它要求两个事实矛盾。
  · 严格：加上"很反常"一级 —— 协议在该端口没有TLS形态（445上出现TLS）、端口承诺加密却出现明文
    （443上跑明文HTTP）。这一级是线索不是判决，默认关闭。
  刻意排除的误报（有测试钉住）：邮件端口的TLS（STARTTLS是正常的邮件提交方式）、3389的TLS
  （RDP的现代默认）、21的TLS（显式FTPS）、1433的TLS（SQL Server加密）、389的TLS（StartTLS）。
  这些一旦标出来，每个正常在这种端口上工作的人都会上板子，而一次误伤就足以让操作员不再看板子。
- 【新增·高危端口名单】25/135/139/444/445/5800/5900（运营商与云厂商默认封堵，来源：阿里云ECS
  常见端口说明）。这是**信息**不是判决：端口高危不代表流量可疑，它代表**服务**放在这里很反常 ——
  而这正是隧道会躲的地方。
- 【新增·端口表接口】/api/v1/ports/reference 返回判据用到的端口、它们的IANA名字、以及每个端口
  上不容许什么（含中文说明与命中的两级）。判据要可解释：只给一个开关的拦截功能，操作员在第一次
  误伤之后就会永久关掉它。
- 【记录·一条被权威表纠正的记忆】IANA 现在把 465 注册为 urd，不是多数资料仍在写的 smtps。
  端口表以注册表为准，并有测试钉住这一点，免得日后有人"修回"成记忆里的名字。

1.51.0
- 【新增·「Cloudflare CDN」作为一种类型】上一版把CF豁免写成了规则里的 cloudflare_cdn(...) 调用；
  按你的意见改成**一个类型**：协议库里多一条「Cloudflare CDN」，判据是"目的地属于CF地址段 **且**
  端口是CF公开端口"。非标端口类目的豁免因此变成引用它：portstd != nil && cloudflarecdn == nil
  —— 豁免所豁免的东西从此在协议库里有名字、看得见、改得动，而不是表达式里的一段条件。
  这个类型默认关，而且面板上写明：**拦它几乎总是错的**（CF上是一大块正常网站）。它的意义是
  ①让豁免有名字 ②操作员能基于它写自己的规则。
- 【保留·cloudflare_cdn 内置函数】表达式里仍可用 cloudflare_cdn(ip, 端口)，供自定义规则直接判。
  它与类型读的是引擎里同一份 cloudflare 清单，不存在两份数据。
- 【记录·面板为什么不用它命名流量】这个类型**不参与**流量记录的命名（analyzerOf 里没有它）：
  CF 上是全世界一大块普通 HTTPS，用它命名会把实时看板刷满"Cloudflare CDN"，而操作员要看的是
  "这条是不是隧道"。要判断某条流量是不是CF CDN，看协议库与规则即可。


（本包内只保留最近的版本记录，完整历史见项目仓库的 changelog。
原因：插件市场的安装器把整条记录作为一个命令行参数交给 sqlite3，而系统对
单个参数有硬上限（131072 字节）——包内文本过大会导致注册失败。）
