diff --git a/docs/android-client-design.html b/docs/android-client-design.html
new file mode 100644
index 0000000..ca236ca
--- /dev/null
+++ b/docs/android-client-design.html
@@ -0,0 +1,170 @@
+
+
+
+
+
+Pangolin Android 客户端设计方案
+
+
+
+
+
+
← 文档索引
+
+
Pangolin Android 客户端设计方案
+
目标终点:MVP 端到端连通 + 切节点 + KillSwitch(清掉 TODO 11G) · 分支 feature/android · 2026-06-22
+
+
+一句话定性:这不是从零开发,而是把已有的 M2 纸面 PoC 推到真机能连真实节点出网,并补齐切节点 + KillSwitch(清掉 TODO 11G)。Flutter UI 全平台共享、本次零改动。
+
+
+
1. 现状盘点
+
之前的任务(commit 83c23f9 "feat(android): 实现 M2 Android VPN 隧道")已写好相当完整的原生层,但从未真正编译运行过。
+
+
+
已具备 现成
+
+- Flutter UI 全平台共享(连接键三态 / 节点 / 统计 / 账户都现成,Android 直接复用)。
+- Channel 契约已冻结(
pangolin/vpn MethodChannel + pangolin/vpn/status、pangolin/vpn/stats EventChannel),见 client/lib/bridge/vpn_bridge.dart。
+MainActivity.kt —— 通道注册 + VPN 授权流程(VpnService.prepare)+ 电池优化豁免引导。
+PangolinVpnService.kt —— VpnService + libbox BoxService 集成、openTun、统计(CommandClient + TrafficStats 兜底)、前台通知。
+VpnEventBus.kt —— Service↔Activity 状态总线(主线程回调)。
+AndroidManifest.xml —— VPN 权限、前台服务 specialUse(适配 Android 14)。
+build-android.sh —— gomobile 编译 sing-box → libbox.aar(sing-box v1.13.12 / Go 1.24.3 / gomobile pin v0.0.0-20240604…,tag with_quic,with_utls,with_clash_api,with_gvisor)。
+poc_config.json —— REALITY 配置模板。
+
+
+
+
+
缺口(让它真正能跑要补的)待补
+
+libbox.aar 没构建过 —— app/kernel/dist/android/ 为空。需要 Android NDK + gomobile(硬前置)。
+- 原生 libbox API 名是「猜」的 —— 代码里多处注释「若编译失败请对照实际 libbox API 微调」(
Libbox.newBoxService、TunOptions.inet4Address() 等从没被真实 aar 校验过)。
+- Dart 侧没接线 ——
vpn_bridge_provider.dart 目前把 Android 落到 VpnBridgeMock(只处理了 macOS/桌面)。
+- 三个 stub ——
selectOutbound / getActiveOutbound / setKillSwitch 标了 TODO 11G。
+- 从没在真机/模拟器上端到端连过。
+
+
+
+
2. 整体策略:自底向上「先构建,再对着真实 API 修」
+
最大未知是 libbox.aar 能不能构建出来、原生代码里那些「猜」的 API 名对不对。所以第一步就把这个雷踩掉,而不是最后才发现编译不过。
+
正确性锚点:官方 SagerNet/sing-box-for-android(SFA)的 Kotlin 源 + 构建出的 aar 里 classes.jar 的真实方法签名。现有 PangolinVpnService.kt 结构是照 SFA 写的,只是 API 名没校验过——拿真 aar 一比对即可定。
+
+
3. 里程碑(垂直切片,每片真机可验)
+
+| 里程碑 | 目标 | 验收 |
+
+| A. 构建内核 | 跑通 build-android.sh 产出 libbox.aar(arm64/arm/amd64,tag 含 with_clash_api) | aar 存在、unzip -l 三 ABI 的 .so 齐 |
+| B. 编译链接 | Dart provider 接 Android→VpnNativeBridge;原生代码对着真 aar 修到 flutter build apk 通过 | APK 构建成功、flutter analyze 零警告 |
+| C. 端到端连通 | 真机一键连 RackNerd 节点,TUN 起、DNS 不劫持失败、能打开被墙站点 | 真机实测科学上网成功;UI 三态正确(连接键 off→connecting→on 严格由内核回调驱动,禁止乐观显示) |
+| D. 统计走字 | stats EventChannel 每秒推上/下行字节 + 速率 | 统计页数字跳动,与系统流量大致吻合 |
+| E. 切节点(11G) | selectOutbound(tag) 经 libbox CommandClient 做 group 选择(复刻桌面 selectProxy);getActiveOutbound 查询当前出口 | 节点页切换出口不断连、当前出口高亮正确 |
+| F. KillSwitch(11G) | setKillSwitch(on) 做 L1(strict_route)+ 引导系统 Always-on(L3) | 开关后内核停止即断网/恢复符合预期;UI 诚实标注「彻底防泄漏需到系统设置开启」 |
+
+
+
+
4. 关键技术决策(与桌面/契约对齐,不自创)
+
+- 配置来源不变:Dart 侧
connect_api.fetchConfig 从服务端 POST /v1/nodes/:id/connect 取完整 sing-box config,原样传 bridge.start(configJson)。客户端绝不拼配置。
+- TUN 由
PlatformInterface.openTun 建:libbox 回调里用 VpnService.Builder 配地址/MTU/路由/DNS → establish() 拿 fd。autoDetectInterfaceControl 走 VpnService.protect() 防环路。现有代码已写好,重点是校验 TunOptions 的真实 getter 名(已用 try-catch 兜底)。
+- DNS 劫持铁律:和 macOS 一样,服务端 config 的
route.rules 首条必须是 {"action":"hijack-dns","port":[53]}——否则隧道连上也打不开网站。这是服务端职责,Android 侧只需确认下发的 config 带这条。
+- 统计:优先 libbox
CommandClient(command=STATUS, 1s);连不上退回 TrafficStats(按 UID)。现有代码已实现双路。
+- 切节点 = Clash group 选择:复刻桌面
clashApiClient.selectProxy(group, tag) 的语义,Android 用 libbox CommandClient 的 group 选择 API。组名对着服务端渲染的 config 确认(config 含 auto urltest 组 + reality-out/hy2-out 出口)。
+- KillSwitch =
strict_route + 引导 Always-on:详见 §5 第 1 条与 KillSwitch 设计知识库。
+
+
+
5. 三处定调(用户已确认)
+
+
① KillSwitch(里程碑 F)
+
做「L1 + 引导 L3」的组合——setKillSwitch(on) 落 strict_route(与 Windows 桌面统一)+ 在 UI 诚实标注「彻底防泄漏需到系统设置开 Always-on」,并提供跳转引导。不假装 app 内能做到真 KillSwitch。 背景与分级见 KillSwitch 设计知识库。
+
+
+
② 工具链
+
默认降 Go 到 1.24.3、保 gomobile pin,贴 sing-box 官方 SFA 验证组合;不升 pin。(本机现为 Go 1.26.1,gomobile pin 是 2024-06 旧版,新 Go + 旧 gomobile 可能 gomobile bind 失败。)
+
+
+
③ 测试基线(里程碑 C)
+
先 x86_64 模拟器跑通(环境最干净、最贴官方验证、排除真机变量)→ Vivo X200(Android 16 / API 36,AOSP 系,主力真机) → 华为 HarmonyOS 4.2(兼容 Android 版,次要兼容性抽查)。华为后台保活激进、电池优化引导 Intent 需单独适配。
+
+
+
6. 已知风险 / 坑
+
+- Go 1.26 vs gomobile 旧 pin:若
gomobile bind 失败,按定调降 Go 1.24.3(保 pin)。
+- 当前没有设备连着(
adb devices 为空)——里程碑 C 起需插真机/开模拟器。模拟器为 x86_64,aar 必须含 amd64(脚本已含)。
+- libbox API 漂移:现有
.kt 方法名是猜的,里程碑 B 逐一对平(TunOptions getter 已 try-catch 兜底)。
+- Android 14/16 前台服务 specialUse:manifest 已声明;targetSdk 拉到 36 时复测 FGS 启动是否被限。国内分发免审;未来上 Play 需补用途说明。
+- 电池优化 / 厂商保活:现有代码会弹豁免引导;华为/Vivo 后台管理激进,需引导用户允许后台 + 自启动,且厂商设置页深链可能落不准。
+
+
+
7. 不在本次范围(YAGNI)
+
+- 国内分流
.srs 本地预取(现走远程 rule-set)。
+- 应用图标 / 启动页打磨。
+- 签名打包 / 应用市场上架 / 侧载分发流程。
+- per-app 分应用代理。
+- macOS 原生侧的 11G(
selectOutbound/getActiveOutbound/setKillSwitch)补齐——平行未完成,见 KillSwitch 知识库 与 todo #1,不在本次范围。
+
+
+
8. 参考位置索引
+
+| 主题 | 文件 |
+
+| 桥接契约 | client/lib/bridge/vpn_bridge.dart |
+| 平台分派(需加 Android→Native) | client/lib/bridge/vpn_bridge_provider.dart |
+| 连接状态机(调 bridge.start) | client/lib/state/connection_provider.dart |
+| 取 config | client/lib/services/connect_api.dart |
+| Android 通道注册 | client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/MainActivity.kt |
+| Android VPN 服务 | client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt |
+| 内核构建脚本 | app/kernel/build-android.sh |
+| 内核版本锚点 | app/kernel/VERSION |
+| 桌面参考实现(selectOutbound/killSwitch) | client/lib/bridge/desktop_vpn_bridge.dart |
+| 服务端 config 渲染 | server/internal/httpapi/clientconfig.go |
+| KillSwitch 设计知识库 | docs/killswitch-design.html |
+
+
+
+
+
+
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..9131ec1
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,121 @@
+
+
+
+
+
+Pangolin 文档索引
+
+
+
+
+
+
diff --git a/docs/killswitch-design.html b/docs/killswitch-design.html
new file mode 100644
index 0000000..79e8c3c
--- /dev/null
+++ b/docs/killswitch-design.html
@@ -0,0 +1,151 @@
+
+
+
+
+
+KillSwitch 设计与跨平台能力矩阵(知识库)
+
+
+
+
+
+
← 文档索引
+
+
KillSwitch 设计与跨平台能力矩阵(知识库)
+
Pangolin 客户端 · 断网保护设计依据与现状 · 2026-06-22
+
+
+KillSwitch 不是「一个开关」,而是分层能力——理想态、平台天花板、当前实现是三件不同的事。本文沉淀其设计依据与现状。
+
+
+
1. 本质:fail-closed
+
KillSwitch 的本质是 fail-closed:
+
隧道不处于活动状态时,禁止任何流量走明文/默认路径出去。
+
目标是堵住 VPN 掉线瞬间的 IP / DNS 泄漏。没有 KillSwitch 时,隧道一掉,流量静默回落到运营商默认路由,用户真实 IP 与 DNS 查询直接暴露——对科学上网场景是致命泄漏。
+
+
2. 理想设计的 5 条属性
+
+- 默认关闭(fail-closed):隧道一旦不可用,流量是「被丢弃」而非「放行」。
+- 扛得住进程死亡 ← 最难。app/内核进程崩溃或被系统杀掉后,封锁依然生效。这要求封锁由 OS 内核/框架强制,而不是靠 app 进程内的路由表。
+- 覆盖所有「缺口窗口」:开机后还没连上的窗口、掉线重连的窗口、进程崩溃的窗口——三个都要堵。
+- DNS 防泄漏:DNS 查询也强制走隧道或被一并阻断。
+- 可控范围 + 诚实 UI:可选放行 LAN(局域网打印机等)、可选分应用;并如实告诉用户当前到底保护到了哪一层,不夸大。
+
+
+
3. 能力分级(统一标尺)
+
+| Level | 机制 | 能堵什么 | 堵不住什么 |
+
+| L0 无 | 掉线即走明文 | — | 全泄漏 |
+| L1 内核路由绑定(软) | 路由绑死隧道接口(如 sing-box strict_route) | 进程活着、隧道未建/重连时不泄漏 | 进程一死、接口被拆 → 路由恢复 → 泄漏 |
+| L2 OS 防火墙强制(硬) | OS 级包过滤规则(NE 框架 / WFP / nftables)阻断非隧道流量 | 进程死了也照堵(规则在 OS 内核,不在 app) | 开机到规则生效前的窗口 |
+| L3 常开 + 开机持久 | L2 + OS 从开机起自动拉起 VPN 并强制阻断 | 开机窗口也堵,全程 fail-closed | (基本无死角) |
+
+
+
理想态 = L3。
+
+关键认知:L1 与 L2 之间有一条质变线——L1 是「app 进程内的约束」,L2/L3 是「OS 框架的约束」。只有跨过这条线才算「真 KillSwitch」。目前 Pangolin 各平台用的 strict_route 都还停在 L1。
+
+
+
4. 各平台能力天花板
+
+| 平台 | 机制 | 天花板 | 说明 |
+
+| macOS | NetworkExtension System Extension | L3 | includeAllNetworks=true 全量入隧道 + NEOnDemandRule 常开 + enforceRoutes。NE 守护进程(neagent)在系统级强制,扛 app 崩溃。Apple 平台原生支持最完整。 |
+| iOS | NetworkExtension | L3 | 同 NE 原语,甚至更干净;on-demand「按需常开」。⚠️ 目前无 iOS 客户端。 |
+| Android | VpnService | L3,但有条件 | app 内只能到 L1(strict_route);真正的 L2/L3 = 系统设置「始终开启 VPN + 无 VPN 时阻止连接」,OS 级强制、扛崩溃、开机生效。但 app 不能编程开启,只能深链引导用户手动开(或 Device Owner/MDM 下发)。 |
+| Windows | 子进程 sing-box + wintun | L2(需开发) | 现仅 strict_route(L1)。要到 L2 须让 app 装 WFP(Windows Filtering Platform)过滤器阻断非隧道流量;做成系统服务持久化才能扛崩溃。 |
+| Linux(非主目标) | 子进程 + tun | L2(需开发) | L1 现成;L2 靠 nftables/iptables killswitch 链。 |
+
+
+
一句话:Apple 两端原生能直达 L3;Android 能到 L3 但要用户手动配合;Windows/Linux 要自己写 OS 防火墙规则才能上 L2。
+
+
5. 各平台当前实际实现(代码事实)
+
+| 平台 | 当前 Level | 真实状态 | 代码位置 |
+
+| Windows | L1 | ✅ 改 strict_route + 子进程重载 + 退避重连。进程被硬杀仍泄漏。 | desktop_vpn_bridge.dart:305(applyKillSwitchToConfig)、:178 |
+| Linux | L1 | 同 Windows(共用 DesktopVpnBridge)。 | 同上 |
+| macOS | L0 | ❌ stub:setKillSwitch 直接 result(nil),includeAllNetworks()→false。天花板 L3,实际啥都没做。 | VpnChannel.swift:69、PacketTunnelProvider.swift:224 |
+| Android | L0 | ❌ stub:setKillSwitch 只打日志(TODO 11G)。 | MainActivity.kt(setKillSwitch 分支) |
+| iOS | — | 无客户端。 | — |
+
+
+
注:macOS 自 kUseNativeVpnMacOS=true(vpn_bridge_provider.dart:19)起走原生 System Extension,不再走 DesktopVpnBridge。所以 macOS 的 strict_route(Windows 路线)对它不生效,必须走 NE 原生路线。
+
+
6. Pangolin 现实判断与决策
+
+- 能力与实现严重不匹配:macOS 明明能 L3,现在却是 L0(最差);Windows 反而是唯一做了的(L1)。
+- Android 客户端方案的 KillSwitch 定位(MVP+ 阶段):app 内做到 L1(
strict_route,与 Windows 一致),再引导用户开系统 Always-on 拿到 L3,UI 上诚实标注「彻底防泄漏需在系统设置开启」。这是「app 能力上限 + OS 兜底」的合理组合,不夸大。
+- 跨端一致性缺口(按性价比排序):
+
+- macOS 原生 11G(
includeAllNetworks + on-demand)—— 天花板 L3、改动集中在 Swift,性价比最高。
+- Windows L1→L2(WFP 防火墙强制)—— 工作量较大(需原生过滤器 + 持久化服务)。
+- Android —— 受 OS 限制,编程上限就是 L1,剩下靠引导,无更高可做空间。
+
+
+
+
+
7. 关联待办
+
KillSwitch 相关待办见项目 todo/(/todo list),主要三条:
+
+- #1 mac macOS 原生 KillSwitch 补齐 L0→L3(
includeAllNetworks + on-demand)
+- #2 Android Android KillSwitch L1(
strict_route)+ 引导系统 Always-on(属 Android 客户端 11G)
+- #3 Windows Windows KillSwitch L1→L2(WFP 防火墙强制,backlog)
+
+
+
8. 参考实现位置索引
+
+| 主题 | 文件 |
+
+桥接契约(setKillSwitch 方法签名) | client/lib/bridge/vpn_bridge.dart |
+| 平台分派(哪个平台走哪个 bridge) | client/lib/bridge/vpn_bridge_provider.dart |
+| Windows/Linux 实现(strict_route) | client/lib/bridge/desktop_vpn_bridge.dart |
+| macOS 原生通道(stub) | client/macos/Runner/VpnChannel.swift |
+| macOS 隧道 Provider | client/macos/PacketTunnel/PacketTunnelProvider.swift |
+| Android 通道(stub) | client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/MainActivity.kt |
+| Android VPN 服务 | client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt |
+
+
+
+
+
+
diff --git a/docs/superpowers/plans/2026-06-22-android-client.md b/docs/superpowers/plans/2026-06-22-android-client.md
new file mode 100644
index 0000000..d7e5ad1
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-22-android-client.md
@@ -0,0 +1,438 @@
+# Pangolin Android 客户端 Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 把已有的 M2 Android PoC 推到真机能连真实节点出网,并补齐切节点 + KillSwitch(清 TODO 11G)。
+
+**Architecture:** Flutter UI 全平台共享、零改动。Android 走 `VpnService` + 内嵌 sing-box `libbox.aar`(gomobile 绑定),经冻结的 `pangolin/vpn` MethodChannel/EventChannel 与 Dart 通信。配置由服务端渲染、客户端原样下发。本质是「先构建 aar → 对着真实 libbox API 修原生代码到能编译运行 → 接线 Dart → 真机端到端 → 补 11G」。
+
+**Tech Stack:** Flutter 3.44 / Dart、Kotlin、Android `VpnService`、sing-box `v1.13.12` libbox(gomobile)、Go 1.24.3、Android NDK。
+
+**设计依据:** `docs/android-client-design.html`、`docs/killswitch-design.html`。
+
+## Global Constraints
+
+- sing-box 内核版本 `v1.13.12`(`app/kernel/VERSION` 锚定,禁止脚本内硬编码)。
+- Go 工具链 **降到 `1.24.3`、保 gomobile pin `v0.0.0-20240604150348-70c4c5da7d30`**(贴 SFA 官方验证组合;本机 Go 1.26.1 不动,用官方 `golang.org/dl/go1.24.3` 并存)。
+- libbox build tags:`with_quic,with_utls,with_clash_api,with_gvisor`(脚本默认,勿改)。
+- `minSdkVersion 21`(libbox `-androidapi 21` 要求),`targetSdkVersion` 跟随 flutter。
+- Channel 契约冻结(`pangolin/vpn` + `pangolin/vpn/status` + `pangolin/vpn/stats`),方法签名见 `client/lib/bridge/vpn_bridge.dart`,**不得私改**。
+- 配置来源:服务端 `POST /v1/nodes/:id/connect`,**客户端绝不拼配置**。
+- **禁止乐观显示**:连接键 `on` 必须由 `bridge.statusStream`(内核回调)确认,`start()` 返回 ≠ on。
+- 测试基线:x86_64 模拟器跑通 → Vivo X200(Android 16)主力真机 → 华为 HarmonyOS 4.2 兼容性抽查。
+- NDK:`/Users/wangjia/Library/Android/sdk/ndk/27.0.12077973`(构建前 export `ANDROID_NDK_HOME`)。
+
+---
+
+### Task 1: Go 1.24.3 工具链就位
+
+**Files:** 无(环境配置)。
+
+**Interfaces:**
+- Produces: 可执行 `go1.24.3`(`$(go env GOPATH)/bin/go1.24.3`),供 Task 2 构建用。
+
+- [ ] **Step 1: 安装官方 Go 1.24.3 多版本 shim**
+
+Run:
+```bash
+go install golang.org/dl/go1.24.3@latest
+go1.24.3 download
+```
+Expected: `Downloaded ... go1.24.3 ...`,无错误。
+
+- [ ] **Step 2: 验证版本与 NDK**
+
+Run:
+```bash
+go1.24.3 version
+ls -d /Users/wangjia/Library/Android/sdk/ndk/27.0.12077973
+```
+Expected: `go version go1.24.3 darwin/arm64`;NDK 目录存在。
+
+- [ ] **Step 3: 无需 commit**(纯环境,无文件改动)。
+
+---
+
+### Task 2: 构建 libbox.aar(里程碑 A)
+
+**Files:**
+- 产出: `app/kernel/dist/android/libbox.aar`(git 忽略产物,不入库)。
+
+**Interfaces:**
+- Consumes: Task 1 的 `go1.24.3`。
+- Produces: `libbox.aar`,含 `go.libbox.*` 类,供 Task 3 解析、Task 5 链接。
+
+- [ ] **Step 1: 用 Go 1.24.3 跑构建脚本**
+
+`build-android.sh` 内部用 `command -v go` 取 Go,需临时让 `go` 指向 1.24.3。用 PATH 注入 shim 目录的方式(不改脚本):
+
+Run:
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/app/kernel
+export ANDROID_NDK_HOME=/Users/wangjia/Library/Android/sdk/ndk/27.0.12077973
+GOROOT_1243="$(go1.24.3 env GOROOT)"
+PATH="${GOROOT_1243}/bin:${PATH}" go version # 确认此 shell 内 go=1.24.3
+PATH="${GOROOT_1243}/bin:${PATH}" bash build-android.sh
+```
+Expected: 末尾 `✓ 构建完成: .../dist/android/libbox.aar` + 三个 `.so`(arm64-v8a/armeabi-v7a/x86_64)列出。
+注意:首次会 `git clone sing-box`(约数分钟)+ `gomobile bind`(数分钟~十几分钟)。
+
+- [ ] **Step 2: 验证产物三 ABI 齐**
+
+Run:
+```bash
+unzip -l app/kernel/dist/android/libbox.aar | grep -E '\.so$'
+```
+Expected: 至少 `jni/arm64-v8a/libgojni.so`、`jni/armeabi-v7a/...`、`jni/x86_64/...` 三条。
+
+- [ ] **Step 3: 无需 commit**(aar 为忽略产物)。若 `gomobile bind` 失败,记录报错——多半是 Go/gomobile 组合问题,回到 Task 1 复核版本。
+
+---
+
+### Task 3: 解析真实 libbox API(里程碑 B 发现)
+
+**Files:**
+- 创建: `app/kernel/dist/android/libbox-api.txt`(javap 输出存档,便于 Task 5/9 对照;可入库或留作临时参考)。
+
+**Interfaces:**
+- Consumes: Task 2 的 `libbox.aar`。
+- Produces: `go.libbox` 关键类的真实方法签名清单(`Libbox`、`BoxService`、`TunOptions`、`PlatformInterface`、`CommandClient`、`CommandClientHandler`、`CommandClientOptions`、`StatusMessage`、`OutboundGroupIterator`)。
+
+- [ ] **Step 1: 解包 aar 取 classes.jar,javap 导出签名**
+
+Run:
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/app/kernel/dist/android
+mkdir -p .inspect && cd .inspect
+unzip -o ../libbox.aar classes.jar >/dev/null
+for c in Libbox BoxService TunOptions PlatformInterface CommandClient CommandClientHandler CommandClientOptions StatusMessage OutboundGroupIterator BoxService\$Companion; do
+ echo "===== go.libbox.$c ====="
+ javap -classpath classes.jar "go.libbox.$c" 2>/dev/null || echo "(类不存在,名称可能不同)"
+done | tee ../libbox-api.txt
+```
+Expected: 各类方法签名打印到屏幕与 `libbox-api.txt`。重点记录:
+- 创建 BoxService 的入口(`Libbox.newService` / `new BoxService(...)` / 参数个数与类型)。
+- `TunOptions` 的 getter 真名(`getInet4Address()` vs `inet4Address()`、`getMTU()`/`getMtu()`)。
+- `CommandClient` 选 outbound 的方法(如 `selectOutbound(group, tag)`)。
+- `StatusMessage` 流量字段(`getUploadTotal()` 等)。
+
+- [ ] **Step 2: 比对现有 Kotlin 的「猜测点」**
+
+Run:
+```bash
+grep -n "// libbox API" /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt
+```
+Expected: 列出所有标注「libbox API」的行;逐条对照 `libbox-api.txt` 标记需改的方法名(用于 Task 5)。
+
+- [ ] **Step 3: Commit(存档 API 清单)**
+
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android
+git add app/kernel/dist/android/libbox-api.txt
+git commit -m "docs(android): 存档 libbox.aar 真实 API 签名清单(Task 3)"
+```
+(若 `dist/` 被 .gitignore 忽略,改存到 `docs/` 下并相应调整路径。)
+
+---
+
+### Task 4: Dart provider 接 Android → VpnNativeBridge
+
+**Files:**
+- Modify: `client/lib/bridge/vpn_bridge_provider.dart`(约 27 行,加 Android 分派)。
+
+**Interfaces:**
+- Consumes: 现有 `VpnNativeBridge`(`vpn_bridge.dart`,走 `pangolin/vpn` 通道)。
+- Produces: Android 运行时返回 `VpnNativeBridge` 而非 mock。
+
+- [ ] **Step 1: 改平台分派**
+
+把 `vpn_bridge_provider.dart` 中:
+```dart
+ if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) {
+ return DesktopVpnBridge();
+ }
+```
+之前加入 Android 分支:
+```dart
+ if (Platform.isAndroid) {
+ return VpnNativeBridge();
+ }
+```
+并把文件顶部注释「其他平台(iOS / Web / 测试)→ VpnBridgeMock」更新为「iOS / Web / 测试 → VpnBridgeMock;Android → VpnNativeBridge」。
+
+- [ ] **Step 2: 静态分析**
+
+Run:
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/client
+flutter analyze lib/bridge/vpn_bridge_provider.dart
+```
+Expected: `No issues found!`
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android
+git add client/lib/bridge/vpn_bridge_provider.dart
+git commit -m "feat(android): Dart provider 接 Android→VpnNativeBridge(Task 4)"
+```
+
+---
+
+### Task 5: 修原生代码对齐真实 API 并编译通过(里程碑 B)
+
+**Files:**
+- Modify: `client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt`(按 Task 3 清单逐处校正方法名)。
+- Modify(按需): `MainActivity.kt`、`VpnEventBus.kt`。
+
+**Interfaces:**
+- Consumes: Task 2 的 `libbox.aar`(`build.gradle` 已配 `implementation files(libboxAar)`)、Task 3 的 `libbox-api.txt`。
+- Produces: 可编译的 debug APK。
+
+- [ ] **Step 1: 按 Task 3 清单校正 `PangolinVpnService.kt` 的 libbox 调用**
+
+对照 `libbox-api.txt`,逐处替换标注 `// libbox API` 的方法名(`Libbox.newBoxService`/`service.start()`/`TunOptions.inet4Address()`/`mtu()`/`CommandClientOptions` 字段/`StatusMessage` getter 等)为真实签名。`TunOptions` getter 已有 try-catch 兜底,优先改成真名以免走兜底默认值。
+
+- [ ] **Step 2: 构建 debug APK(只 arm64 加速首轮)**
+
+Run:
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/client
+flutter build apk --debug --target-platform android-arm64
+```
+Expected: `✓ Built build/app/outputs/flutter-apk/app-debug.apk`。若报 Kotlin 编译错(未知方法/类型不符)→ 回 Step 1 对照 `libbox-api.txt` 再修。
+
+- [ ] **Step 3: 全量 analyze**
+
+Run:
+```bash
+flutter analyze
+```
+Expected: `No issues found!`(或仅既有无关告警)。
+
+- [ ] **Step 4: Commit**
+
+```bash
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android
+git add client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/
+git commit -m "fix(android): PangolinVpnService 对齐真实 libbox API,编译通过(Task 5)"
+```
+
+---
+
+### Task 6: 模拟器冒烟——启动 + 通道连通
+
+**Files:** 无代码改动(验证任务)。
+
+**Interfaces:**
+- Consumes: Task 4/5 产物。
+- Produces: 确认 app 在 x86_64 模拟器启动、通道注册、初始状态 `off`。
+
+- [ ] **Step 1: 起 x86_64 模拟器并装运行**
+
+Run:
+```bash
+adb devices # 确认有 emulator-xxxx;无则先在 Android Studio 启一个 x86_64 AVD
+cd /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/client
+flutter run -d emulator --debug # 或 flutter run 选模拟器
+```
+Expected: app 启动到登录/主页,无崩溃。
+
+- [ ] **Step 2: 验证通道初始状态**
+
+观察 logcat:
+```bash
+adb logcat -s PangolinMainActivity PangolinVpnService flutter | head -40
+```
+Expected: 见 `MethodChannel: getStatus`、status channel `onListen` 推 `off`,UI 连接键为 off 态。
+
+- [ ] **Step 3: 无需 commit。**
+
+---
+
+### Task 7: 模拟器端到端连通(里程碑 C)
+
+**Files:** 无代码改动(验证任务;若发现 openTun/DNS 问题则回 Task 5 修)。
+
+**Interfaces:**
+- Consumes: 服务端真实 config(经登录→选节点→`fetchConfig` 自动获取)。
+- Produces: 真隧道连通、可访问被墙站点。
+
+- [ ] **Step 1: 登录并连接**
+
+在 app 内登录(真账号)→ 选 RackNerd 节点 → 点连接。授予 VPN 授权弹窗。
+观察:
+```bash
+adb logcat -s PangolinVpnService | grep -E "openTun|BoxService started|postStatus"
+```
+Expected: `openTun: fd=...` → `BoxService started` → status `on`;连接键转 on(计时开始)。
+
+- [ ] **Step 2: 验证真出网 + DNS(铁律)**
+
+在模拟器浏览器或 `adb shell` 访问被墙站点:
+```bash
+adb shell curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" https://www.google.com
+```
+Expected: `200 ...`。若超时但能连其它国内站 → 多半 DNS 劫持规则缺失,检查服务端下发 config 的 `route.rules` 首条是否 `hijack-dns`。
+
+- [ ] **Step 3: 断开验证**
+
+点断开 → status `off`、连接键复位、`doStop` 日志干净(无重入崩溃)。
+
+- [ ] **Step 4: 无代码则无 commit;若 Task 5 文件有修复则 commit。**
+
+---
+
+### Task 8: 统计走字(里程碑 D)
+
+**Files:** 验证为主;若字段不准则 Modify `PangolinVpnService.kt`(`writeStatus` 映射)。
+
+**Interfaces:**
+- Consumes: libbox `CommandClient` STATUS(或 TrafficStats 兜底)。
+- Produces: `pangolin/vpn/stats` 每秒一帧,UI 统计页跳动。
+
+- [ ] **Step 1: 观察 stats 帧**
+
+连接状态下:
+```bash
+adb logcat -s PangolinVpnService | grep -E "CommandClient connected|TrafficStats fallback"
+```
+Expected: 见 `CommandClient connected`(优先)或兜底日志;UI 统计页上/下行字节与速率每秒更新。
+
+- [ ] **Step 2: 校正字段(如需)**
+
+若速率恒 0 或字节不增 → 对照 Task 3 的 `StatusMessage` getter 真名修 `writeStatus` 映射,重编重测。
+
+- [ ] **Step 3: Commit(如有修复)**
+
+```bash
+git add client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt
+git commit -m "fix(android): 校正统计字段映射(Task 8)"
+```
+
+---
+
+### Task 9: 切节点 selectOutbound + getActiveOutbound(里程碑 E / 11G)
+
+**Files:**
+- Modify: `PangolinVpnService.kt`(加 libbox CommandClient group 选择 + 查询)。
+- Modify: `MainActivity.kt`(`selectOutbound`/`getActiveOutbound` 从 stub 改为转发到 Service)。
+
+**Interfaces:**
+- Consumes: Task 3 的 CommandClient 选 outbound API 签名;服务端 config 的出口组名(`auto` urltest + `reality-out`/`hy2-out`)。
+- Produces: `selectOutbound(tag)` 切换不断连;`getActiveOutbound()` 返回当前出口 tag。语义复刻桌面 `desktop_vpn_bridge.dart` 的 `selectProxy`/`getProxies`。
+
+- [ ] **Step 1: 确认组名**
+
+```bash
+grep -n "\"tag\"\|selector\|urltest\|proxyTags" /Users/wangjia/code/pangolin/.claude/worktrees/feature+android/server/internal/httpapi/clientconfig.go
+```
+Expected: 确认可选组名(当前为 urltest `auto`;如需手动选需服务端含 `selector` 组——若没有则 selectOutbound 作用于 `auto` 组内成员或触发服务端补 selector,按实际 config 决定)。
+
+- [ ] **Step 2: 实现 Service 侧 selectOutbound/getActiveOutbound**
+
+在 `PangolinVpnService` 暴露静态/单例方法,用 libbox `CommandClient`(按 Task 3 真名)执行 group 选择与查询;`MainActivity` 的对应 MethodChannel 分支从 stub 改为调用之。
+
+- [ ] **Step 3: 编译 + 真机/模拟器验证**
+
+Run:
+```bash
+cd client && flutter build apk --debug --target-platform android-arm64
+```
+连接后在节点页切换出口:观察不断连、`getActiveOutbound` 返回值随之变、UI 高亮正确。
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/
+git commit -m "feat(android): selectOutbound/getActiveOutbound 经 libbox CommandClient 切节点(Task 9,11G)"
+```
+
+---
+
+### Task 10: KillSwitch L1 + 引导系统 Always-on(里程碑 F / 11G)
+
+**Files:**
+- Modify: `MainActivity.kt`(`setKillSwitch` 从 stub 改为:缓存偏好 + 触发以 `strict_route` 重启隧道;并暴露跳系统 Always-on 设置的方法)。
+- Modify: `PangolinVpnService.kt`(启动时按偏好将 config TUN `strict_route` 置位——可复用桌面 `applyKillSwitchToConfig` 的等价 JSON 改写逻辑,在 Kotlin 侧实现)。
+- Modify(UI 文案): `client/lib/l10n/strings_zh.dart` + `strings_en.dart`(KillSwitch 旁诚实标注「彻底防泄漏需到系统设置开启 Always-on」)。
+
+**Interfaces:**
+- Consumes: 设计依据 `docs/killswitch-design.html`(L1 语义 + 引导 L3)。
+- Produces: `setKillSwitch(on)` 落 `strict_route`(与 Windows 一致)+ 系统 Always-on 引导入口。
+
+- [ ] **Step 1: Kotlin 侧实现 strict_route 改写**
+
+在 Service 启动前对 configJson 做 JSON 改写:`inbounds` 中 `type==tun` 的项设 `strict_route = on`。`setKillSwitch(on)` 若隧道在跑则以新偏好重启(短暂重连,与桌面一致)。
+
+- [ ] **Step 2: Always-on 引导入口**
+
+`setKillSwitch(on)` 为 true 时(或 UI 按钮)跳转系统 VPN 设置:
+```kotlin
+startActivity(Intent("android.settings.VPN_SETTINGS"))
+```
+(部分 ROM 落不到「Always-on」子页,落到 VPN 列表即可;华为/Vivo 单独适配留 Task 11。)
+
+- [ ] **Step 3: UI 文案诚实标注**
+
+在 KillSwitch 开关旁加说明文案(走 l10n,**无红线词**),明确 app 内 = L1、彻底防泄漏需系统 Always-on。
+
+- [ ] **Step 4: 编译 + analyze + 验证**
+
+Run:
+```bash
+cd client && flutter build apk --debug --target-platform android-arm64 && flutter analyze
+```
+连接下开 KillSwitch:观察重连后 `strict_route` 生效(停内核即断网);点引导跳到系统 VPN 设置。
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/ client/lib/l10n/
+git commit -m "feat(android): KillSwitch L1(strict_route)+引导系统 Always-on(Task 10,11G)"
+```
+
+---
+
+### Task 11: 真机验证(Vivo X200 + 华为 4.2)
+
+**Files:** 验证为主;厂商保活引导若需适配则 Modify `MainActivity.kt`。
+
+**Interfaces:**
+- Consumes: 全部前序产物。
+- Produces: 真机端到端连通 + 后台保活验证 + 厂商电池优化引导适配。
+
+- [ ] **Step 1: Vivo X200(Android 16,主力)全链路**
+
+插线 `adb devices` 确认 → `flutter run -d --release` 或装 release APK。跑通:登录→连接→访问被墙站→切节点→KillSwitch→断开。重点验证 **targetSdk 高版本下前台服务 specialUse 不被限**、后台挂 30 分钟不被杀(需在 Vivo 设置允许后台+自启动)。
+
+- [ ] **Step 2: 华为 HarmonyOS 4.2(兼容抽查)**
+
+装 APK 跑通连接主链路。华为后台保活更激进——验证电池优化引导 Intent 是否落对页面;若落不到,加华为定制 Intent 兜底(`Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` 之外的 ROM 专属页)。
+
+- [ ] **Step 3: Commit(如有厂商适配)**
+
+```bash
+git add client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/MainActivity.kt
+git commit -m "fix(android): 厂商(Vivo/华为)后台保活引导适配(Task 11)"
+```
+
+- [ ] **Step 4: 标记 todo #2 完成待验收**
+
+```bash
+node ~/.claude/skills/todo/todo.mjs status 2 done
+```
+
+---
+
+## 验收总览(对应里程碑)
+
+| 里程碑 | Task | 完成判据 |
+|---|---|---|
+| A 构建内核 | 2 | libbox.aar 三 ABI 齐 |
+| B 编译链接 | 3–5 | debug APK 构建成功、analyze 零警告 |
+| C 端到端连通 | 6–7 | 模拟器实测科学上网、UI 三态正确 |
+| D 统计走字 | 8 | stats 每秒跳动且数值合理 |
+| E 切节点 | 9 | 切出口不断连、当前出口正确 |
+| F KillSwitch | 10 | strict_route 生效 + Always-on 引导 + 诚实文案 |
+| 真机 | 11 | Vivo X200 全链路 + 华为兼容抽查 + 后台保活 |
diff --git a/todo/todo.html b/todo/todo.html
new file mode 100644
index 0000000..98ab342
--- /dev/null
+++ b/todo/todo.html
@@ -0,0 +1,645 @@
+
+
+
+
+
+feature+windows — 项目 TODO
+
+
+
+
+
+
+
feature+windows — 项目 TODO
+
+
+
3全部
+
3待开始
+
0开发中
+
0待验收
+
0已验收
+
+
+
+
+
+
+
+ 重要度
+
+
+
+
+
+
+
+ 状态
+
+
+
+
+
+
+
+
+ 改动等级
+
+
+
+
+
+
+
+ 平台 / 标签
+
+
+
+
+
+
+
+
+
+ 📋 待开始 3
+ ▴ 收起
+
+
+
+
+ -
+
+
+
现状 L0(stub):VpnChannel.swift:69 setKillSwitch 直接 result(nil)、includeAllNetworks()→false。天花板 L3。改 NetworkExtension:includeAllNetworks=true + NEOnDemandRule 常开 + enforceRoutes,由 neagent 系统级强制、扛 app 崩溃。性价比最高的跨端缺口。依据见 docs/killswitch-design.md §4-6。
+
+
+
+
+
+ -
+
+
+
现状 L0(stub):MainActivity.kt setKillSwitch 只打日志(TODO 11G)。目标:app 内做到 L1(config TUN strict_route,复刻 Windows 的 applyKillSwitchToConfig 语义) + 深链引导用户开系统设置「始终开启 VPN + 无 VPN 时阻止连接」拿 L3,UI 诚实标注「彻底防泄漏需在系统设置开启」。属 Android 客户端 11G。依据见 docs/killswitch-design.md §4-6。
+
+
+
+
+
+ -
+
+
+
现状 L1:desktop_vpn_bridge.dart strict_route + 子进程重载,进程被硬杀仍泄漏。目标 L2:app 装 WFP(Windows Filtering Platform) 过滤器阻断非隧道流量,做成系统服务持久化才能扛崩溃。工作量较大,backlog。依据见 docs/killswitch-design.md §4-6。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
拒绝验收
+
+
+
+
请填写拒绝原因
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/todo/todo.json b/todo/todo.json
new file mode 100644
index 0000000..48578ce
--- /dev/null
+++ b/todo/todo.json
@@ -0,0 +1,54 @@
+{
+ "meta": {
+ "title": "feature+windows — 项目 TODO",
+ "updated_at": "2026-06-22T09:32:56.170Z"
+ },
+ "seq": 3,
+ "items": [
+ {
+ "id": 1,
+ "title": "macOS 原生 KillSwitch 补齐 L0→L3",
+ "desc": "现状 L0(stub):VpnChannel.swift:69 setKillSwitch 直接 result(nil)、includeAllNetworks()→false。天花板 L3。改 NetworkExtension:includeAllNetworks=true + NEOnDemandRule 常开 + enforceRoutes,由 neagent 系统级强制、扛 app 崩溃。性价比最高的跨端缺口。依据见 docs/killswitch-design.md §4-6。",
+ "level": "mid",
+ "tier": 2,
+ "tags": [
+ "mac"
+ ],
+ "status": "open",
+ "created_at": "2026-06-22T09:32:42.340Z",
+ "done": false,
+ "completed_at": null,
+ "version": null
+ },
+ {
+ "id": 2,
+ "title": "Android KillSwitch L1 + 引导系统 Always-on",
+ "desc": "现状 L0(stub):MainActivity.kt setKillSwitch 只打日志(TODO 11G)。目标:app 内做到 L1(config TUN strict_route,复刻 Windows 的 applyKillSwitchToConfig 语义) + 深链引导用户开系统设置「始终开启 VPN + 无 VPN 时阻止连接」拿 L3,UI 诚实标注「彻底防泄漏需在系统设置开启」。属 Android 客户端 11G。依据见 docs/killswitch-design.md §4-6。",
+ "level": "mid",
+ "tier": 2,
+ "tags": [
+ "Android"
+ ],
+ "status": "open",
+ "created_at": "2026-06-22T09:32:56.098Z",
+ "done": false,
+ "completed_at": null,
+ "version": null
+ },
+ {
+ "id": 3,
+ "title": "Windows KillSwitch L1→L2 (WFP 防火墙强制)",
+ "desc": "现状 L1:desktop_vpn_bridge.dart strict_route + 子进程重载,进程被硬杀仍泄漏。目标 L2:app 装 WFP(Windows Filtering Platform) 过滤器阻断非隧道流量,做成系统服务持久化才能扛崩溃。工作量较大,backlog。依据见 docs/killswitch-design.md §4-6。",
+ "level": "low",
+ "tier": 1,
+ "tags": [
+ "Windows"
+ ],
+ "status": "open",
+ "created_at": "2026-06-22T09:32:56.169Z",
+ "done": false,
+ "completed_at": null,
+ "version": null
+ }
+ ]
+}