判断某个变更需要什么证据时,先读这个页面。
OpenSurge for Mac 会修改 host networking,所以验证结论必须严格限定范围。 单元测试、静态检查和 virtual LAN lab 证明的是不同层级的事情。
运行:
make test这个命令运行 go test ./...,是当前 CI 级别检查。对于不宣称真实 host-network
行为的小型代码、parser、renderer 和单元级变更,通常足够。
如果沙箱阻止 Go cache 写入,把 cache 放到 /private/tmp 下。
运行:
make lab-test宣称 DHCP、DNS、mihomo 进程或配置生成、pf/NAT 规则、IPv4 forwarding、 rollback、网关生命周期清理、lab 拓扑或 runtime traffic defaults 被真实验证前,应运行这个门槛。
lab 会把 gateway 保持在 macOS 上,并用 socket_vmnet 网络中的 Lima 客户端
测试它。它验证 DHCP、DNS、ICMP/NAT、直连 HTTPS、通过 mihomo mixed-port
的显式代理 HTTPS,以及清理行为。
root-required Lab 目标应在同一个 TTY 里用 sudo -v && make <lab-target> 启动。
macOS sudo 缓存既会过期,也可能因 TTY/执行上下文不同而无法被脚本中的 sudo -n
复用;长时间连续跑多个门禁时,每个目标前都重新验证。除非运行环境明确需要无人值守,
不要把临时凭据问题扩大成宽泛的免密 sudo;仓库提供的可选规则也只限 root-owned
network helper 的三个固定子命令,不能代替网关测试所需的 sudo 缓存。
冷 lab-up 可能比 sudo ticket 活得更久,因此它完成后必须再次验证再启动测试;长门禁
结束后的 lab-down 同理。VM 停止但 helper stop 报 sudo: a password is required 时,
应把它视为不完整清理并重新执行带 sudo -v 的 lab-down。
Apple Silicon 上的 Codex/agent 终端可能经 Rosetta 运行,使 uname -m 显示
x86_64 并触发安装器架构拒绝。确认 sysctl.proc_translated=1 和
hw.optional.arm64=1 后,用
/usr/bin/arch -arm64 /bin/bash ./tests/lab/install-host-deps.sh 运行安装器;Intel Mac
不能使用这条绕行命令。
第一次 lab-up 包含固定镜像下载和 guest 依赖安装,不能和持久化 VM 的后续启动耗时
直接比较。正常清理使用 lab-down 保留磁盘,只有损坏或有意重建时使用 lab-destroy。
guest 的数据面 DNS 在一次测试后会指向 192.168.50.1;而下一次 lab-up 时被测网关
尚未运行,所以 provisioning 必须先恢复 Lima 控制面 DNS,并在依赖已齐全时跳过 apt,
否则会表现为 UDP/53 connection refused 与很慢的 boot scripts。
冷重建保持串行 provisioning,稳定复用的 VM 则并行启动;这样既不让两个 apt 任务争抢
上游带宽,又避免日常启动累加两次独立 guest boot 时间。
修改 tests/lab/lima/client.yaml 会使 Lima 按精确配置比较删除并重建对应
VM,这是有意的冷启动。VZ 冷启动可能在约两分钟内没有新输出,然后从
vsock SSH 回退到 usernet forwarder 并进入 READY;不要只因为这段静默就杀掉进程。
先看 runtime/tools/lima/bin/limactl list 和 ~/.lima/<client>/ha.stderr.log。
Lab 环境问题必须与数据面失败分开记录:
- 启动和清理会把 guest
/etc/resolv.conf恢复到 Lima 控制网关,并保证本机 hostname 可解析。如果 provisioning 报sudo: unable to resolve host或仍向已停止的192.168.50.1查询,先运行 guest helper 的restore-control,不要把它算作 IPv6 数据面结果。 omg0由/etc/systemd/network/05-open-mihomo-gateway-lab.network单一接管。networkctl status omg0应显示该文件;重复 IPv4/IPv6 默认路由通常意味着 netplan 和手工 DHCP/RA 同时在管理接口。IPv6 READY 信号还必须排除tentative/dadfailed地址,并要求正的preferred_lft。- 自动 RA 模式不保证
/etc/resolv.conf直接出现 IPv6 nameserver。IPv6 client probe 应在没有显式 IPv6 nameserver 时使用omg0IPv6 默认路由的 link-local next hop,并附加接口 scope;空的 HTTP/3 client evidence 通常先检查这一点。 - quic-go 在最小 guest 中可能报告无法把 UDP receive buffer 增大到建议值。若随后有
CLIENT_IPV6_HTTP3_OK,这是吞吐告警而不是握手失败;这个功能门槛不证明 QUIC 性能。 runtime/lab/proxy.env中不可达的旧代理和专用/private/tmpGo module cache 残缺都是 patched Mihomo 的构建前故障。脚本会对 Go mirror 绕过旧代理, 并在日志确认是该专用 cache 的缺文件后清理并重试一次。先查runtime/lab/logs/mihomo-build.log,不要进入数据面调试。- agent 沙箱中的
sysctl kern.bootsessionuuid/kern.boottime或sysctl.proc_translated: operation not permitted是执行环境权限信号。需要 host-network 结论时,在已批准的同一 PTY 重跑对应门槛。 - Lima 停止 VZ 时可能在红色日志中打印
use of closed network connection。 如果后续同时出现has shut down和lab network stopped,这是 hostagent 关闭 listener 后的收尾噪声,不是清理失败。
不要仅凭启动耗时把默认 1 CPU / 512 MiB 判定为不足。先采集 guest 的 available
memory、load、CPU idle/iowait 和 OOM 记录;如果 CPU 主要 idle、内存仍可用且没有 OOM,
应优先排查 DNS、下载和重复 provisioning,而不是增加 VM 常驻资源。
运行:
make policy-control-test这个门槛启动真实 mihomo 二进制和 OpenSurge CLI,但不启动 dnsmasq、pf、TUN,
也不需要 sudo。它用 imported profile fixture 验证 policies、policy-select、
connections、providers、provider-update 和聚合 snapshot 能通过 live
external-controller API 工作,并会重启 mihomo 证明 profile.store-selected 可以
恢复选中的策略。它还验证本机/私网 mixed-port 目标保持 DIRECT、专用
local-routing 控制器协调三种模式、HTTP-only Global 的 UDP fail-closed,以及普通
policy 接口不泄露内部组。它适合策略组控制、file/HTTP provider 状态读取和刷新、
机器可读 CLI、mihomo API wrapper 和 profile.store-selected 相关改动;不要用它
宣称 DHCP、DNS 下发、TUN 透明代理、same-LAN、真实设备路径或真实远端代理出口已验证。
运行:
make lab-test-ipv6-userspace
make lab-test-ipv6-same-wifi
make lab-test-ipv6-same-lan前两条分别覆盖独立下游 LAN 与同 LAN DHCP 全屋接管:两台客户端必须获得
fdfe:dcba:9878::/64 SLAAC 地址、Medium 优先级 OpenSurge IPv6 默认路由和 RDNSS。
其中 same-WiFi 门槛把第一台客户端配置为 IPv4 主路由绕行,要求设备状态报告
ipv6_blocked、不生成 selector,并让 IPv6 TCP 命中最前置的 packet-listener
TUN + InUser REJECT;第二台
客户端仍须继续完成 IPv6 TCP、UDP 与 QUIC carrier 的 DIRECT 路径。
第三条覆盖选择性旁路由:客户端手工使用不同 ULA,并把 Mac link-local 地址同时作为默认网关和 DNS,
dnsmasq 配置中不得出现 RA。随后三条门槛都要求无显式代理的 IPv6 TCP、受控 UDP
request/response、1200-byte QUIC Initial-shaped UDP carrier 和真实 HTTP/3-only
request/response 通过本机 fixture 出现在 patched Mihomo 的 opensurge-packet 路径,
并按两台客户端各自的 MAC/InUser 命中不同设备策略。HTTP/3 client 没有
TCP/HTTP/2 fallback;它分别要求 DIRECT 和受控 SOCKS5 UDP 出口完成 QUIC TLS 与
HTTP/3 GET,并要求 HTTP-only 出口记录 UDP REJECT、origin 与 CONNECT proxy 均无
请求。TCP origin 必须收到实际 HTTP request,UDP fixture 必须返回固定答案,HTTP/3
origin 必须记录 HTTP/3.0,不依赖公网上游作为唯一捕获证据。最后必须停止网关,验证
自动模式的 RA/default route 或旁路由手工配置,以及 gateway alias、broker、
socket/ready file 和 runtime state 被撤销。RFC 4862 允许客户端暂时保留
deprecated/等待过期的 SLAAC 地址,因此门槛不要求地址瞬间消失。
QUIC-shaped 项只证明 UDP carrier 和策略命中;HTTP/3-only fixture 证明的是三个本机 受控出口场景,不等于所有 QUIC/HTTP3 版本、0-RTT、连接迁移、公网节点或代理组合。单元测试或直接 Unix packet injection 可以证明 gVisor 与设备身份,但不能替代 macOS BPF、真实 RA 和 停止撤销的 host-network 证据。
真实订阅和公网 IPv6 出口使用独立的补充门槛:
sudo -v && \
OMG_LAB_IPV6_REAL_PROFILE=/absolute/path/to/profile.yaml \
make lab-test-ipv6-imported-egress它不能取代 lab-test-ipv6-userspace 的受控 origin/UDP fixture。补充门槛把订阅复制到
mode 0600 的 Lab runtime,使用 tun_ipv6: auto 并要求
native_ipv6_available: true / ipv6_reason: native_ipv6_available;先证明一台 VM 的
MAC/InUser 域名 REJECT,再让它完成 IPv6-only HTTPS、IPv6 UDP DNS 回包和 QUIC
形态 UDP 的 DIRECT 命中,并要求 Mac 基线和 VM HTTPS 回显分别属于当前上游接口的
GUA;不同 socket 允许选择不同的 IPv6 隐私地址。之后选择一个不泄露名称、且已通过
SOCKS5 UDP ASSOCIATE 公网 IPv6 DNS 回包的真实叶子节点,让另一台 VM 完成
fake-AAAA HTTPS、真实 IPv6 字面地址 HTTPS、IPv6 UDP DNS 回包和 QUIC 形态 UDP 的
GLOBAL 命中。VM 的 ULA 是真实下游 IPv6 包,但不是运营商委派的 GUA;DIRECT 是
Mihomo/gVisor 从 Mac 发起新 socket,不是 ULA 原样转发。
这个门槛只能输出脱敏 artifact。订阅副本、生成的 Mihomo 配置、原始 Mihomo 日志、
selector/API 输出和 cache 都不得进入 artifact;保留文件必须再接受订阅 server、凭据和
较长节点名 marker 扫描。只有确认网关停止成功后才删除 runtime secret;停止失败时保留
mode 0600 的恢复材料并把门槛判为失败。
虚拟 LAN lab 通过后,真实设备 smoke 的推荐入口是:
make real-device-start-off
make real-device-client-check
make real-device-stop该入口由 tests/real-device/smoke.sh 提供,会生成本地
runtime/real-device 配置、构建当前 CLI、绑定下游接口、运行 root doctor、
启动 gateway,并做基础 DNS/API/listener 探测。它仍然需要用户在终端里输入一次
sudo,但把 root-required 步骤收敛到一个 sudo 会话;不要把它描述为免密 root
helper。
真实设备 smoke 能证明物理客户端接入下游 LAN 后,能从 OpenSurge 获得
DHCP/DNS 并通过 Mac 网关出站。它不替代 make lab-test / make lab-test-tun
作为代码改动的可复现 lab 门槛。
当前进度快照来自 docs/agent-wiki/sources/validation/real-device-smoke.md。
截至 2026-07-06 CST,本轮已经验证 explicit/off runner、TUN runner 和最小
proxy egress runner 可以在物理下游 LAN 启动,真实 Pixel 手机可以获得
192.168.50.100-200 范围租约且 router/DNS 为 192.168.50.1。手机侧无代理
直连 HTTPS/NAT、显式 192.168.50.1:17890 HTTP proxy HTTPS、TUN 模式下无
显式代理 HTTPS,以及本机受控 upstream proxy 命中 open-surge-egress 均已完成
一次 smoke;Mac 侧能对应看到租约、DNS 查询、fake-ip 查询、mihomo.log 中的
客户端目标连接,以及受控代理日志中的 CONNECT example.com:443。stop 清理
范围包括释放下游接口上的 192.168.50.1 测试 LAN IP,避免后续 virtual LAN lab
把 192.168.48.0/22 的重叠回程路由选到真实设备接口。
explicit 模式的关键验收信号是:
make real-device-status显示Gateway: running、DHCP: running、mihomo: running、pf anchor: loaded和IP forwarding: enabled;leases中出现真实物理客户端,而不只是备用 AP 或小路由器;dig @192.168.50.1 example.com A能从 Mac 或下游客户端获得响应;curl --proxy http://192.168.50.1:17890 https://example.com/能通过 mihomomixed-port访问 HTTPS。
只有运行 make real-device-start-tun 并在客户端无显式代理时观察到成功出站、
fake-ip DNS 响应和 mihomo.log 中的真实客户端目标连接,才可以宣称真实设备
TUN smoke 被验证。
本轮已通过的真实手机检查是:
- 无代理直连 HTTPS/NAT:手机 Wi-Fi HTTP proxy 关闭,HTTPS 页面成功加载, Mac 侧能看到该手机租约和 DNS 查询。
- 显式 HTTP 代理 HTTPS:手机 Wi-Fi HTTP proxy 设为
192.168.50.1:17890, HTTPS 页面成功加载;如果日志级别允许,mihomo.log能看到来自手机 IP 的 HTTPS 连接。 - TUN 透明模式:
make real-device-start-tun启动后,手机保持无显式代理, HTTPS 页面成功加载,并且mihomo.log中出现来自手机 IP 的目标连接。 - 最小 proxy egress:
make real-device-start-tun-proxy启动后,手机保持无 显式代理,https://example.com/成功加载;dnsmasq.log中出现该手机的example.comfake-ip 查询,mihomo.log中出现example.com:443 match Domain(example.com) using open-surge-egress[real-device-egress],受控本机 HTTP CONNECT proxy 日志中 出现CONNECT example.com:443。
默认生成的 mihomo 配置仍然是 MATCH,DIRECT。已验证的 proxy egress smoke 只证明
最小受控 upstream_proxy 切片能把指定域名交给 open-surge-egress 和本机受控
代理;它不证明订阅规则、远端代理节点、策略组切换或出口 IP 变化。只有换成具有
独立出口的实际代理,并同时看到 mihomo.log 使用 open-surge-egress、受控代理
日志有对应请求、出口 IP 发生预期变化,才可以宣称远端代理出口路径被验证。
同 LAN 默认网关 smoke 的入口是:
make same-lan-start-tun
make same-lan-start-tun-proxy
make same-lan-start-tun-imported-egress
make same-lan-adb-check
make same-lan-adb-check-imported-egress
make same-lan-stop这个 gate 覆盖 Mac 和一台 Android 测试设备处于同一家庭或办公室 LAN 的场景。
第一版只允许 gateway.mode: "same_lan"、dhcp.enabled: false 和
transparent.mode: "tun"。OpenSurge 不在主 LAN 上发 DHCP;测试手机需要手动或由
外部 DHCP 配置把默认网关和 DNS 指向 Mac 的 LAN IP。
如果进入“同一个 Wi-Fi 关闭主路由 DHCP、由 OpenSurge 接管 DHCP/DNS”的测试切片,
先阅读 tests/same-lan/WIFI-DHCP-RECOVERY.zh-CN.md。恢复不是附属步骤,而是验收
的一部分:需要证明路由器 DHCP 已恢复、Mac Wi-Fi 回到 DHCP、至少一台客户端能重新
自动获取地址并上网。
这个高风险切片使用独立的 gateway.mode: "same_wifi_dhcp",而不是放宽
same_lan 的 dhcp.enabled: false 不变量。入口是:
OMG_SAME_WIFI_DHCP_ROUTER_DHCP_DISABLED=confirmed \
OMG_SAME_WIFI_DHCP_PROTECTED_IPS=<static-ip-list> \
make same-wifi-dhcp-start-imported-egress
make same-wifi-dhcp-adb-check-imported-egress
make same-wifi-dhcp-stoprunner 不会改变路由器设置;confirmed 只是要求操作者在手动关闭路由器 DHCP 后明确
确认。它拒绝把 Mac gateway 或 OMG_SAME_WIFI_DHCP_PROTECTED_IPS 中的静态地址放进
租约池。ADB gate 要求 Android 的地址在池内且出现在 omg leases 与 DHCPACK 日志中,
并继续要求 DNS、无显式代理 TUN、provider/update、TunEgress 的 DIRECT 与
egress-proxy 切换,以及受控 CONNECT 证据。stop 检查 OpenSurge runtime/PF/listener/
helper 清理和 IPv4 forwarding 恢复;随后仍必须由操作者恢复路由器 DHCP 和客户端自动
获取。完整 runbook 在 tests/same-lan/WIFI-DHCP-RUNNER.zh-CN.md。
Web GUI 允许操作者跳过客户端验收,或在网关停止后保留 Mac 静态 IPv4 并结束状态机;
这些是交互流程的明确 waiver,不是 Lab / same-WiFi gate 的通过证据。凡是宣称 DHCP、DNS、
TUN 客户端路径或停止后自动获取恢复已验收,仍必须收集本节要求的真实证据,不能用
client_validation_skipped / complete_static 代替。
make same-lan-adb-check 是客户端证据入口。它应通过 ADB 采集 Android 默认路由、
DNS 查询、无显式代理探针结果,并回看 Mac 侧 dnsmasq.log 和 mihomo.log。只有
同时看到 Android 默认路由包含 via <mac-lan-ip>、DNS 查询走 Mac、客户端触发目标
连接、mihomo.log 中出现 TUN 路径目标连接,才可以宣称 same-LAN TUN smoke 通过。
基础 same-lan-start-tun gate 不证明主路由 DHCP 全局下发、所有设备兼容、IPv6、
DoH/Private Relay、UDP/QUIC、imported profile 或策略组切换。Android 镜像如果缺少
curl、wget、nc、nslookup 或 dig,应把结果记录为 ADB 客户端探针能力不足,
而不是把人工浏览器页面成功当成完整自动化证据。
same-LAN 的真实代理出口可以先用最小 upstream_proxy 切片验证,不必先导入完整
订阅。2026-07-09 已用 api.ipify.org、Pixel 测试手机和 LAN HTTP 代理完成这一
层:Android 默认路由经 Mac、Android 显式代理为空、dnsmasq.log 看到 Android 源
IP 查询 api.ipify.org、mihomo.log 显示
Domain(api.ipify.org) using open-surge-egress[same-lan-http-egress],Android
浏览器显示的出口 IP 与 Mac 直接使用该 LAN 代理访问 api.ipify.org 的结果一致。
策略组切换需要单独验证。当前自动生成的 open-surge-egress 只有一个代理成员,只能
证明命中代理;要证明切换,应让 group 至少包含 LAN 代理和 DIRECT 两个候选,通过
omg policy-select --config <path> --group <name> --policy <member> 切换选中项后
重复出口 IP 探针。
same-LAN imported provider 策略切换的入口是
make same-lan-start-tun-imported-egress 和
make same-lan-adb-check-imported-egress。前者渲染 imported profile fixture,并启动本地
HTTP provider/受控 HTTP CONNECT proxy;后者要求 Android 无显式代理路径先命中
TunEgress[DIRECT],再通过 omg policy-select --group TunEgress --policy egress-proxy 切换后命中 TunEgress[egress-proxy],同时受控 proxy 日志出现
CONNECT <host>:443。这个 smoke 证明 same-LAN TUN 下的 imported provider-backed
策略切换可以命中受控本地代理;它不证明完整订阅兼容性或真实远端出口 IP。
2026-07-10 已用一台人工操作的 Android(未使用 ADB)完成这个 gate:手机手动把
网关和 DNS 指向 Mac、保持无显式代理;同一运行期内,example.com:443 先记录为
TunEgress[DIRECT] 且受控 proxy 日志为空,切换后记录为
TunEgress[egress-proxy] 且出现 CONNECT example.com:443。两次浏览器访问均成功,
随后 make same-lan-stop 恢复了 PF、IPv4 forwarding、监听端口和 runtime state。
生成的 imported profile 路径相对 runtime/same-lan/config-tun.yaml 解析,必须写为
./mihomo-profile.imported-tun-egress.yaml。同时,helper 必须同时监听本地 HTTP
provider 与受控 CONNECT proxy 两个端口才可开始客户端探针。
运行:
make lab-test-tun宣称透明代理路径被验证前,应运行这个门槛。它启用
transparent.mode: "tun",保持客户端没有显式代理配置,并要求 HTTPS 请求
出现在 mihomo TUN 路径中。
运行前确认:
sudo -v和 lab target 在同一个终端/TTY 里连续执行。sudo ticket 不是跨 agent exec 会话可靠共享的状态。192.168.50.1只配置在当前 lab bridge 上;virtual LAN 使用/22,真实设备 smoke 默认仍使用/24。如果en7等接口残留192.168.50.1/24,macOS 可能把 重叠范围内的 lab client 回程路由到 错误接口,表现为 TUN DNS timeout。先运行make real-device-stop或删除重复 地址。
当前重要验收信号:
- 客户端不依赖显式代理配置;
- 客户端 helper 运行 transparent 测试路径;
mihomo.log中出现透明 HTTPS 目标,例如--> example.com:443;- 成功时输出
transparent TUN log observed for example.com:443; - gateway 被停止,
runtime/lab/state.json被移除; - artifacts 写入
artifacts/lab。
make lab-test-tun 的标准配置保持 local_system_proxy.enabled: false,因此它能证明
TUN 主路径没有回归,但不能证明系统代理协同已应用,也不能证明它解决某个真实 Network
Extension 冲突。该兼容层需要额外的真实 Mac 验收:记录原 HTTP/HTTPS/PAC/自动发现状态,
让冲突扩展保持启用,证明 TUN-only 失败、协同开关成功恢复目标应用访问,再停止网关并
确认原状态恢复。只完成 mock networksetup 单元测试时必须明确写为未运行真实兼容验收。
修改 mihomo profile 导入或 OpenSurge gateway overlay 行为时,优先使用:
make lab-test-tun-imported-profile这个门禁使用 tests/lab/mihomo-profile.imported-tun.yaml 启动 imported profile
配置,并保持规则为 MATCH,DIRECT。它证明 imported profile overlay 可以进入 TUN
lab 路径;它不证明外部代理出口或远端节点可用。
验证 imported provider 和策略组切换会影响透明 TUN 出口时,使用:
make lab-test-tun-imported-egress这个门禁使用 tests/lab/mihomo-profile.imported-tun-egress.yaml,在 lab host 上
启动本地 HTTP provider 和受控 HTTP CONNECT proxy。脚本先让无显式代理客户端通过
TUN 访问测试 HTTPS host,并要求 mihomo.log 显示 using TunEgress[DIRECT] 且受控
proxy 未被使用;随后执行 omg policy-select --group TunEgress --policy egress-proxy
并重复透明请求,要求 mihomo.log 显示 using TunEgress[egress-proxy],同时受控
proxy 日志出现 CONNECT <host>:443。它证明 imported provider-backed 策略选择能
改变透明 TUN egress path;它不证明真实订阅节点、真实远端出口 IP、same-LAN 或真实
设备兼容性。
Lab 中的受控 CONNECT proxy 必须把上游 DNS 查询和 TCP socket 都绑定到真实 upstream interface。否则 proxy 自己的连接会再次进入正在测试的 TUN,或者把 mihomo fake-IP 错误地发到物理接口,产生递归或 TLS timeout,而不是有效的出口切换证据。
运行:
make lab-test-tun-local-routing当改动 open-surge/mac-* selector、本机 TUN/显式代理身份、规则 / 全局 / 直连语义,
或本机与下游隔离时使用。门槛使用 imported TUN egress fixture 和受控 CONNECT proxy:
- Rule 模式下,本机继续进入
TunEgress网关规则; - Global 模式下,本机 TCP 使用受控代理,而下游客户端仍按
TunEgress[DIRECT]; - Direct 模式下,本机保持
DIRECT,而下游客户端仍可按TunEgress[egress-proxy]使用受控代理; - HTTP-only Global 出口令本机 UDP 状态明确为
reject; - 普通
policies输出不暴露open-surge/mac-*内部组。
该门槛同时要求 mihomo.log 中本机 TUN source 为 198.18.0.1,下游仍保留自己的
LAN IPv4。make test、make web-test 或 make policy-control-test 都不能替代这条
真实 host-network/TUN 隔离证据。
运行:
make lab-test-tun-device-policy当改动 MAC 绑定 DHCP reservation、设备路由模式、每设备 selector 或设备规则覆盖的
数据路径时,使用此门槛。它让 bridge 与 DHCP 客户端实际使用 /22,并验证两个 Lima
VM 跨第三段获得 192.168.50.101/192.168.51.102 固定 IPv4;先证明 dedicated
设备的 default selector 位于全局 MATCH 之前,再证明
inherit_global 设备没有 default selector 且走全局 MATCH;随后通过 reload 把后者改成
独立模式,验证两台设备可独立选择不同 TUN egress,并验证设备专属 IP REJECT。它还断言 applied policy snapshot/state digest、omg devices 的
policy_identity_ready/lease_match 对真实租约成立、desired 文件修改后的 drift,
调用真实 omg reload 后网关回到 running 且 desired/applied digest 收敛,并再次检查
selector 隔离;同时要求设备默认 selector 指向 HTTP-only outbound 时 UDP/443 命中
REJECT fallback 而非 fall through 到全局 MATCH,DIRECT。它证明设备身份、跟随与
独立模式、默认出口、安全 reload、UDP fail-closed 和覆盖规则的真实 LAN/TUN 数据路径。
同一 fixture 还保留一条旧 LAN 带 MAC 登记,要求完整 desired 仍在,而 compiled/applied
设备列表、dnsmasq、Mihomo IPv4 规则/selector 和 IPv6 MAC 身份均排除它。
门槛还会让两台客户端各建立一条持久连接;切换第一台设备 selector 后,通过真实 Control
API 刷新该设备连接。第一台设备的旧连接必须消失,第二台设备连接必须保留,第一台设备
随后建立的新连接必须使用当前 selector。
大型 rule-provider、模板与 domain/IP/protocol/port 组合只改变配置编译时,
make test 提供相应覆盖;不需要为每条操作者定义的规则运行 Lab。系统 TUN 的设备
身份边界是 MAC 绑定 IPv4 DHCP reservation 加 IPv4 SRC-IP-CIDR;下游 IPv6 则由
packet path 把观察到的 source MAC 映射为 patched Mihomo 的 IN-USER(device:<id>)。
后者是路由归属,不是防 MAC spoofing 认证;upstream_router 设备只用它命中最高优先级
IPv6 REJECT。
2026-07-11 已在 P1-1..P1-5 修复(commit 7b14586)后运行此门槛并通过:两个 VM
拿到 .101/.102 固定租约且 omg devices identity 就绪,UDP
192.168.50.101 -> 1.1.1.1:443 命中设备 REJECT fallback,两设备 selector 独立
切换,设备级域名 REJECT 生效,stop 后 state.json 清除。artifacts 在
artifacts/lab/20260711-194621。ARP/ICMP reservation 冲突探测只在 same_wifi_dhcp
模式激活,此 lab(same_lan/tun)未在运行时覆盖该路径,由单元测试覆盖。
真实 same-WiFi 双设备门槛是:
make same-wifi-dhcp-start-device-policy
make same-wifi-dhcp-adb-check-device-policy
make same-wifi-dhcp-stop
make same-wifi-dhcp-verify-device-policy-recovery它要求两台真实客户端的 SSID Wi-Fi MAC、固定 reservation 和 ADB serial,启动前主动
证明不存在其他 DHCP OFFER;数据面验证两台设备 identity、独立 default selector、
设备规则 selector、准确源 IP 和 UDP REJECT。恢复门槛在操作者重新打开路由器 DHCP 后
要求主动 OFFER、Mac DHCP server_identifier、恢复的默认路由和两台客户端 HTTPS。
截至本实现落地时该 gate 尚未在本轮真机运行;因此 same-WiFi per-device 只能标记为
Experimental / cooperative IPv4,不能借用 virtual lab 的通过记录宣称已验收。
Mac 与下游客户端共用同一个 Wi-Fi 接口时,普通连通性 smoke 不足以证明上游断链恢复。 需要在完整恢复手册可用的前提下,单独记录以下证据:
- 断链前,连通性探针分别证明一个 DIRECT 国内目标和一个真实/受控代理目标可用,并记录 Mihomo 的 rule、chain 与日志;
- 人工让 Wi-Fi 断开并重新关联,确认接口、静态 IPv4、router 和 DNS 已恢复;
- 若 Mihomo 路径持续失败,保存 macOS Wi-Fi 时间线和当前
mihomo.log,执行sudo omg restart-mihomo --config <path>; - 证明 dnsmasq PID、PF anchor、IPv4 forwarding、Mac 静态网络和 DHCP 接管恢复阶段在 动作前后没有变化,只有 Mihomo PID 改变;
- 重复 DIRECT 与代理出口探针,要求均恢复,并确认旧日志已归档;
- 最后完成 same-WiFi stop 与路由器 DHCP、Mac DHCP、客户端自动获取恢复门槛。
make test 只证明独立恢复动作、单次自动触发和生命周期互斥的状态事务与接口边界。
普通 make lab-test-tun 使用虚拟 LAN,不能代替物理 Wi-Fi 断开/重关联证据;在上述真实
门槛未运行前,只能说明自动恢复机制已经实现,不能宣称物理链路恢复或根因已经彻底验收。
单元测试必须覆盖默认关闭且不持久化、Helper lease 丢失、外部 SleepDisabled 拒绝接管、
ownership marker 引用计数和上次异常运行的 marker reconciliation。pkg 静态检查必须确认
升级与卸载只在 marker 存在时恢复睡眠。它们不证明特定 Mac 型号和 macOS 版本在电池/电源
下的真实合盖行为;正式宣称前还需在安装包环境分别验证开关启用时合盖保持运行、关闭时
恢复合盖睡眠、Control Service kill、Helper kill 与系统重启后的恢复,并记录 pmset -g
中的 SleepDisabled 证据。测试机必须保持通风,不能把合盖运行中的机器放入包内。
最终报告必须明确说出实际运行了哪些命令。如果只运行了 make test,不要暗示
root-required lab 行为或 transparent routing 已经被验证。