实现独立加密设备身份、网关 agent、持久生命周期任务和 Surge/FlClash 专属订阅凭证。 完善 Shadowrocket 规则转换与 Clash AI DNS,增加 Surge FFSB 适配、规则快照、运维工具及验收文档。
35 KiB
AGENTS.md
本文件是 Codex 与 Claude Code 共用的项目开发约定。CLAUDE.md 用 @AGENTS.md 导入本文件;项目规则只在这里维护。双工具入口、技能发现和使用方法见 docs/AI-COLLABORATION.md。
协作方式
- 默认用中文沟通,代码标识符沿用项目现有英文命名。
- 按任务读取相关代码与文档;以代码、测试及用户最新要求为准,发现说明过时就同步修正。
- 本地阅读、编辑、类型检查、单测和构建按任务自主完成。部署或生产数据变更须属于用户授权的任务;已有明确授权不重复确认。普通代码修改不隐含上线或修改生产数据。
- 保留用户已有改动。提交、推送与部署分别按用户要求执行。
- 项目技能唯一源文件在
.claude/skills/;.agents/skills/通过相对符号链接供 Codex 发现。修改技能时编辑源文件,避免维护两份副本。
这是什么
本仓库统一维护边缘 VPS 节点的部署管理与 Proxy Station 代理节点、分流规则管理平台。Web 界面管理多协议节点(VMess/Trojan/Shadowsocks/Hysteria2)与策略组、规则,向 macOS Surge、iOS ShadowRocket、Android ClashMeta 下发托管订阅。规则模板来自 ClashConnectRules/Surge,边缘舰队的工具与私有交付记录已从原 ../edge-nodes/ 迁入本项目。
当前生产约定(2026-10-04)
最新盘点、完整组顺序与成员、节点启用范围和 Session 变更索引见 当前生产状态。日期化体检/变更文档保留当时的数量与验证结果;维护前经管理 API 重读,不能用历史快照或初始化模板覆盖最新生产数据。
- 当前登记 30 个节点,12 个启用、18 个停用;12 个启用组、21 个显式成员、130 条规则(128 启用)。停用节点保留凭据,不因文档整理、排序、部署或导入清单核对而自动启用。应用的节点启用状态不等于边缘 VPS 的运行状态。
- 普通代理流量交给
🚀 Proxy,只有它引用⚡️ Automatic。Automatic 为 url-test,成员依次为新加坡、美国、日本;规则不直接指向 Automatic。🤖 AI为 select,成员依次为美国、新加坡、日本、🧠 AI Nodes,不引用 Automatic。 📋 All Nodes与🧠 AI Nodes都是独立 select 组,开启 includeAllNodes、没有筛选正则或显式成员;AI Nodes 紧接 AI 显示。两组收纳相同的订阅范围内可用节点,各自保存客户端选择,不能用引用 All Nodes 代替独立的 AI Nodes。- 两组按完整节点名称字符串升序输出,保留 Direct/Warp 出口词。现有节点 sortOrder 全为默认 0,IR 按 sortOrder、name 读取;新增/导入节点保持默认值。非零权重会先于名称排序,调整它影响节点声明及其他动态列表,不只是这两个组。Web 节点页仍按站点、协议与出口展示。
- Streaming 当前承接 Disney、Netflix、TikTok、YouTube、GoogleSearch 及欧洲/韩国/香港/台湾流媒体集合;美国/日本流媒体集合直接交给对应地区组,Emby 交给 Proxy。组显示顺序不决定规则匹配顺序。
- 规则优先级保持:STUN 与 googlevideo UDP 防护 → Unbreak DIRECT → 启用的 AI 规则 → 业务例外/广告与其余分流 → 唯一 FINAL。FINAL 仍为
🎯 NoAuto,dns-failed,NoAuto → Mainland → DIRECT。保留 Google 更新/翻译直连例外;全局 PROTOCOL DOH 与 Dia 关键词规则继续停用。 - Apple/Global Services/Speedtest/Other 及无节点的香港/台湾/韩国/英国组已删除,不因模板仍存在而恢复。App Store DIRECT、Apple News 美国、Apple Relay AI 例外继续保留。
节点部署管理
edge-nodes/README.md是节点工作区入口;edge-nodes/build-delivery-doc.py从 Git 忽略的edge-nodes/private/fleet.json读取清单并离线生成汇总交付页,不在源码里保存真实站点信息。- 通用部署脚本、参考资料与占位交付模板唯一源码在
.claude/skills/edge-node-multi-protocol/,Codex 从.agents/skills/edge-node-multi-protocol相对链接加载。部署/重建/维护 VPS 节点使用此技能;发布 Proxy Station 应用用deploy,生产订阅数据查询与修改用update-db。 - 真实配置、面板入口、分享链接、二维码、交付文档和各站点脚本工作副本在
edge-nodes/private/<id>-edge-node-docs/,全部禁止入 Git 或公开静态托管,也不发送到在线二维码服务。新增配置/产物用umask 077,私有目录用受控备份转交,新克隆不会携带这些文件。 - 当前舰队为 sg(AWS 新加坡)、la(DMIT 洛杉矶,同时承载 Proxy Station)、rn(RackNerd 洛杉矶,当前工作副本为
rn2-edge-node-docs/)、jp(VMISS 东京,裸 nginx)。rn-edge-node-docs/与private/archive/是历史资料,不作为当前配置来源;原兄弟目录仅保留为迁移前备份,后续在本仓库维护。 - 舰队自 2026-10-03 统一使用 IPv4:系统接口禁用 IPv6,Xray/sing-box 仅解析 IPv4 并拒绝 IPv6 目标(含 WARP),节点域名不保留 AAAA。重建或扩展线路须保留这些约束,详见
docs/EDGE-IPV4-ONLY.md;仅禁用网卡或删除 AAAA 不能阻止 WARP 隧道内的 IPv6 转发。 - TCP 基础线路由 nginx+3x-ui/Xray 承载 VMess/Trojan/SS × direct/WARP,QUIC(HY2/TUIC)由 sing-box 独立承载。3x-ui 客户端不能只写投影表,必须保留
settings.clients,否则升级迁移会丢失认证。改远端前读取节点交付记录与技能参考,备份受影响配置和数据库,并保留已有证书续期动作。 - 迁移工程、修改脚本或复制文件不会授权远端部署;节点与应用的部署各自按任务执行。应用运行时仍通过管理 API/Web 导入节点,不自动读取私有目录。TUIC/SOCKS5 交付线路不属于本系统当前支持的导入类型。
命令
CI=true pnpm install --frozen-lockfile # 按锁文件安装依赖
pnpm dev # server :3000 + web :5173(都监听 0.0.0.0,局域网可访问)
pnpm -r typecheck # 三个包全量类型检查
pnpm test # 服务端单测(vitest)
pnpm --filter @proxy-station/web build
# macOS Surge:先取实际 HTTPS 订阅到私有临时文件,再用官方 CLI 与规则集快照验证
python3 scripts/check-surge.py /private/tmp/<profile>.conf --fetch-rulesets
# 单测过滤(在仓库根目录下)
pnpm --filter @proxy-station/server exec vitest run parse
pnpm --filter @proxy-station/server exec vitest run -t "round-trip"
pnpm --filter @proxy-station/server exec vitest # watch 模式
# 部署到 LA 生产机(web|server|all),详见 .claude/skills/deploy/SKILL.md 与 docs/DEPLOY.md
bash .claude/skills/deploy/scripts/deploy.sh all
# 本机离线生成边缘舰队交付页(清单与产物均在 Git 忽略的私有目录)
python3 edge-nodes/build-delivery-doc.py
# 校验实际 Clash 配置;生产验收改用实际 HTTPS 入口,真实 URL 仅在本机使用
umask 077
check_dir="$(mktemp -d /private/tmp/proxy-station-check.XXXXXX)"
curl -fsSL --max-time 45 "http://localhost:3000/sub/<token>/clash" -o "$check_dir/clash.yaml" && \
test -s "$check_dir/clash.yaml" && mihomo -t -d "$check_dir" -f "$check_dir/clash.yaml"
本机开发及当前生产部署使用 Node 22。依赖使用 pnpm 与已提交的 pnpm-lock.yaml;不要混用 npm/yarn 或无故更新锁文件。pnpm 在无 TTY 时安装需要 CI=true,原生模块(better-sqlite3、esbuild、vue-demi)的构建许可写在 pnpm-workspace.yaml 的 allowBuilds。
本机会话注入的代理曾造成 git/安装/网络检查挂起;遇到此问题,在单条命令作用域内用 env -u HTTPS_PROXY -u https_proxy -u HTTP_PROXY -u http_proxy -u ALL_PROXY -u all_proxy <命令> 直连。现有运维脚本已自行处理代理。不依赖其他工具的全局指令文件,也不修改用户全局代理配置。
架构
应用部分为 pnpm monorepo:shared/(zod schema 与 DTO,前后端共用源码,非构建产物)+ server/(Hono + better-sqlite3 + Drizzle)+ web/(Vue 3 + Naive UI + Pinia)。生产模式由 server 静态托管 web 产物,单端口。edge-nodes/ 与节点部署技能是 Bash/Python 运维工具,不加入 pnpm 包,也不随应用部署上传;Docker 构建上下文排除它们。
配置生成流水线(核心)
SQLite → buildProfileIR({nodeIds}) → ProfileIR → 四个序列化器
├─ generateSurge(ir, {selfUrl})
├─ generateShadowrocket(ir) 节点订阅
├─ generateShadowrocketConf(ir) 策略组+规则
└─ generateClash(ir, {convertBaseUrl})
server/src/services/ir.ts 是配置生成流水线的统一读库入口,把节点、策略组、规则、Surge 原文段落收成与客户端无关的 ProfileIR;四个生成器只读 IR,互不依赖。新增客户端就是加一个序列化器,不要在生成器里再查数据库。
IR 层承担三件防护,改动时不要绕过:
- 停用或已删除的策略组会被剔除,引用它的成员与规则一并跳过
FINAL规则永远保留,其策略组失效时降级为DIRECTnodeIds为空数组表示「不限制」,非空则按订阅分配的节点范围过滤
客户端能力矩阵
shared/src/protocols.ts 的 clientSupports() 决定某节点能否下发给某客户端,生成器据此排除并回报 excluded。Surge 不支持 Shadowsocks over WebSocket;本项目对 Surge 的 VMess 仅下发 alterId=0,加密方式只支持 auto/aes-128-gcm/chacha20-poly1305(转换为 IETF 名称)。其他客户端的能力独立判断。不要把无法表达的输入静默改成另一个协议组合。
协议字段差异
同一协议在三端写法不同,映射集中在 shared/src/protocols.ts 与各生成器:
- SS cipher:分享链接用 Xray 命名
chacha20-poly1305,Surge/Clash 要chacha20-ietf-poly1305(normalizeSsCipher()) - VMess
alterId=0必须输出 Surge 的vmess-aead=true,否则连不上且症状隐晦 - Hysteria2 的 salamander 混淆:Surge 用
salamander-password=,Clash 用obfs/obfs-password - SS over WS 在 Clash 侧唯一可行方案是
plugin: v2ray-plugin
这些写法参考了 ../sub-router 项目(用户已在生产使用的同类系统),遇到新的客户端适配问题优先查它。
策略组的两种取节点方式
includeAllNodes 与 filterRegex 可以共存,生成器里 filterRegex 分支优先:
- 有
filterRegex→ 动态筛选:Surge 保留显式节点/内置成员,全部成员组作为include-other-group来源,并可同时启用include-all-proxies=true;正则只筛动态来源,不筛显式成员。Clash 用include-all-proxies: true+filter并保留proxies显式成员;ShadowRocket 保留显式成员并用policy-regex-filter动态收节点。新增同地区节点会自动进组 - 仅
includeAllNodes→ Surge 静态展开全部节点名,Clash/ShadowRocket 仍用动态引用
地区组靠节点名称正则匹配,不看 region 字段。节点命名规范与新增地区的完整步骤见 docs/ADDING-REGIONS.md——命名不对就进不了地区组。
节点与组共用客户端的策略名称空间:禁止名称中的配置分隔符、引号、反斜线、控制字符和首尾空格;不能与内置策略或另一类实体重名。导入预览与实际写入都走同一 schema。组成员必须存在且类型正确,创建/更新(包括改名)校验完整引用图无环,并在事务内写组、替换成员与级联引用。
Surge 不支持组级 url=;组的 testUrl 仅用于 Clash/ShadowRocket。Surge 使用 General 的 proxy-test-url 或节点 test-url,fallback 不输出 tolerance。协议、规则和参数规范以 Surge 官方手册 与本机 surge-cli --check 为依据,不能凭其他客户端语法推断。
规则集:映射与兜底转换
模板里的 RULE-SET/DOMAIN-SET 指向外部 URL。Surge/ShadowRocket 直接透传;Clash 需要 rule-providers,services/rulesets/mapping.ts 按序尝试映射到 skk.moe 的 Clash 版与 blackmatrix7 的 Clash 目录,映射不到的标记 needsConvert,provider URL 指向自托管端点 /sub/:token/ruleset/:id.yaml——该端点实时拉取 Surge 版并用 convert.ts 转成 classical provider。规则集拉取带 24h 缓存 + etag,失败回落陈旧缓存。
ShadowRocket 分两步导入
ShadowRocket 的原生订阅格式只承载节点,因此拆成两个端点:/shadowrocket(节点 base64 URI 列表)与 /shadowrocket-conf(策略组与规则的 .conf)。节点订阅按完整名称字符串升序排列,保留出口词;该序列化器对副本排序,不修改 IR/数据库,跨客户端名称排序由上述节点权重约定保证。ShadowRocket 没有 Surge 的 use= 订阅引用语法(真机验证过:use=true 会被当成解析不了的成员残留),策略组动态收节点靠 policy-regex-filter 从 App 内全部节点里筛(「收纳全部节点」输出 policy-regex-filter=.*),显式成员也要保留。因此订阅名随意,但用户在 App 里挂的其他订阅的节点也会被筛进来;这些节点及 App 自定义排序不由本服务控制。Shadowrocket conf 在提供转换基址时把 DOMAIN-SET 转成本服务鉴权 .list 端点的普通 RULE-SET(精确主机→DOMAIN,前导点→DOMAIN-SUFFIX);未提供转换基址的离线调用仍跳过。LAN 按 Surge 官方文档列出的 14 条域名/CIDR 规则展开,保留原参数;明确的 macOS /Applications/*.app/ 进程路径不下发 iOS。AND/OR/NOT/URL-REGEX/PROTOCOL 仍保守跳过并回报 skippedRules;这不是客户端不支持这些类型的断言。2026-10-13 已部署并完成实际 HTTPS 订阅/转换端点验收,依据与验证见 docs/MOBILE-CLIENT-FIX-2026-10-13.md。官方更新记录已确认存在逻辑规则与 QUIC 匹配,具体配置语法仍须以 App 内帮助和真机编译验证,详见 docs/MOBILE-CLIENT-AUDIT-2026-10-04.md。ShadowRocket 的 conf 策略名不可用 Surge 引号机制:策略名(含空格/emoji)一律裸写,加引号会被当作名字的一部分,导致规则指向不存在的策略组而整条失效、流量落到默认行为走代理——Surge 生成器可以加引号,ShadowRocket 生成器绝不可以。
Clash 节点必须明确下发 UDP 能力:VMess/Trojan/纯 TCP SS/HY2 输出 udp: true,SS WS 插件仅转发 TCP,输出 false,不能假设 nginx 的同端口提供 SS UDP。VMess cipher 按 mihomo 命名映射、未知 cipher 明确排除;SS plugin 的 TLS 参数在 plugin-opts 内。RULE-SET 保留 no-resolve;逻辑规则逐个转换子类型,仅把 PROTOCOL TCP/UDP 映射 NETWORK,任一子条件无法表达时整条跳过。两端 url-test 保留零 tolerance,fallback 不输出 tolerance。完整体检需分别核对客户端能力与生成器范围,原生解析成功不等于设备连接或隐私验收。
鉴权与订阅
除公开的 /api/health 外,/api/* 走 adminAuth:设置了 ADMIN_TOKEN 则要求 Bearer,未设置时仅放行 localhost 与 RFC1918 私有网段(公网 IP 一律拒绝)。/sub/:token/* 公开,token 用常量时间比对。订阅 URL 完全由请求来源决定——后端只返回路径,前端用 window.location.origin 拼接,#!MANAGED-CONFIG 与 Clash provider URL 取本次请求的 origin,所以没有也不需要「订阅基址」配置项。
未匹配的 /api/* 显式返回 404,不落到 SPA 回退(否则会拿到一页 HTML,调接口时极难排查)。
Surge Mac 与 Tailscale 共存
修改增强模式、DNS、STUN 或 VPN 路由前,先读 docs/SURGE-TAILSCALE.md。2026-10-06 已实测:独立 Tailscale 客户端设置 --accept-dns=false,Surge 仅将具体 tailnet 域名交给 100.100.100.100,配合 always-real-ip 和实际 Tailscale 网络扩展/CLI 的优先 DIRECT 规则。保留原有公网代理、AI DNS 和其他应用的 STUN 阻断;不使用 Exit Node,不需要向项目保存 Tailscale auth key。
不能机械套用 tun-excluded-routes = 100.64.0.0/10:本机实测该项创建指向物理网关的路由,覆盖 Tailscale 路由。最终配置不添加这些排除项。skip-proxy、DIRECT 和 VIF 路由排除是不同层次,必须检查实际路由表及普通应用连接。
tailscale ping 可通不代表 curl/SSH 可通;客户端显示 Running 也不代表公网未受影响。测试必须同时覆盖原代理、AI DNS、MagicDNS、普通 tailnet 服务和外网入站 SSH。使用 Tailscale CLI 做 netcheck 时也需进程例外,避免把自身 STUN 被阻断误判为网络不支持 UDP。测试 VPN 重连前设置失败自动 tailscale down 的恢复措施,并确认用户未同时手动切换。
现有订阅的持久来源是生产 settings/rules,模板仅供首次初始化;通过 update-db 技能先备份再走 API,不直接改托管文件或重置规则。结论仅限实测环境与场景:两种启动顺序、IPv4/IPv6 服务和蜂窝网络入站 SSH 已通过;睡眠唤醒、长期运行及网络切换尚未验证,不宣称“完美兼容”。
同账号/同 Surge 订阅不会同步 Tailscale 的本机 accept-dns 与 Exit Node 设置。新 Mac 必须逐台检查;MacBook Air 使用相同配置时仍曾保留 accept-dns=true,需在该机单独关闭。不要仅凭 Surge 自带探测成功认定普通应用正常,须另外测不使用显式代理的 curl。
Telegram 跨平台分流(2026-10-11)
最新修复记录见 docs/TELEGRAM-RULES.md,规则快照见 rules/telegram.list。生产已通过管理 API 添加 37 条高优先级自定义规则,Telegram 域名及 IPv4/IPv6 地址统一交给 🚀 Proxy;本机更新订阅后登录二维码恢复显示。仅配置 osx.telegram.org 不足以覆盖数据中心连接。此变更晚于上面的 2026-10-04 生产盘点,历史数量与规则顺序不能视为当前值;维护前仍须经 API 重读。规则快照不会自动跟随上游更新。
前端约定
UI 走「站牌导视」设计方向(轨道交通导视系统语言):白底 #f7f7f4、深藏青重点色 #2b3f5c、琥珀 #b26a00 = 直连出口、青 #007a72 = WARP 净出口,协议各有线路色圆标。颜色只在承载信息时出现——避免发光、亮边条、装饰性渐变。四个方向的设计对比留在 docs/design-demos.html。
两个共用工具:
utils/nodeName.ts的nodeDisplayName():与出口标签并排显示时剥掉名字末尾的出口词。下发给客户端的名字必须保留出口词,客户端里没有标签,用户只能靠名字区分同协议的两条线路utils/clipboard.ts的copyText():navigator.clipboard只在 HTTPS/localhost 可用,局域网 HTTP 访问时是undefined,直接调用会静默失败。必须走这个带execCommand降级的封装
数据与安全
SQLite 落在 server/data/,存有节点真实凭据。data/、*.db、.env、edge-nodes/private/、config.env 及其备份、node-links/ 已在 .gitignore——绝不入 git。可入库的示例、测试、模板一律用占位凭据;填写后的交付副本只放私有目录。节点通过 Web 界面粘贴分享链接导入,应用不直接读 edge-nodes/private/(见 docs/SEEDING.md)。
schema 变更同时更新 shared/src/schemas.ts/DTO(涉及对外字段时)、server/src/db/schema.ts 的 Drizzle 定义和 server/src/db/index.ts 的启动 SQL。新表在 BOOTSTRAP_SQL 中加 CREATE TABLE IF NOT EXISTS,旧库补列用同文件下方的轻量 ALTER TABLE 循环(吞掉「列已存在」错误)——没有引入 migration 工具。
公网部署必须设 ADMIN_TOKEN 并用反代提供 HTTPS:订阅 token 就在 URL 里,明文 HTTP 会泄露。
初始化与已有数据
server/src/db/seed.ts 只在策略组为空时从 server/assets/surge-template.conf 初始化策略组、规则和 Surge 段落。修改模板不会更新已有数据库;既有订阅仍以数据库为准。生产数据变更使用 update-db 技能,经管理 API 修改并先做 SQLite 在线备份,确认备份 root 属主、600 权限,避免绕过校验和级联更新。
组的 members 是整体替换;排序请求始终先读完整列表并提交全量、无重复 ids。规则 reorder API 强制校验完整性与末尾 FINAL;当前组 reorder API 只逐条写顺序,调用方须自行核对完整性。组 PUT 不更新 sortOrder,显示排序使用独立 /api/groups/reorder。节点 PUT 使用私有文件提交完整原字段,排序调整后重读确认除 sortOrder/updatedAt 外没有变化。
server/src/env.ts 直接读 process.env,没有 dotenv 加载;仅复制 .env.example 成 .env 并不会让 pnpm dev 自动读取它。变量须由启动环境提供(Docker Compose 与 systemd 有各自注入方式)。相对 DATA_DIR 始终基于 server/ 解析。
Surge AI DNS 与协议规则
改 DNS/隐私行为先读 docs/SURGE-PRIVACY.md、server/src/services/generators/surge-ai-dns.ts 及对应测试。 Clash 的本地候选从同一 IR 域名规则按顺序派生 nameserver-policy,使用 inline classical matcher 保留早期非 AI 例外,并复用远程 provider;AI DoH 通过官方 #策略组名 参数绑定 AI,节点域名用独立普通 DNS 引导。该改动不作用于 Surge,未开启全局 respect-rules。既有 DoH 会话可能复用旧出口,新建连接才随 AI 选择;不要声称切组立即迁移全部 DNS 会话。Shadowrocket 的 AI DNS 写法尚缺可靠语法/真机依据,仍未实现。AI 域名 DNS 映射来自 IR 内启用的 AI 路由规则;不要把全局 DNS 或所有端口 53 请求改送 AI。Surge 的 PROTOCOL 应用协议规则不能直接复制到其他客户端:Clash 只把 TCP/UDP 转成 NETWORK,其他应用协议跳过;ShadowRocket 跳过 PROTOCOL。生成配置校验不能替代设备 DNS/WebRTC 真机验证。
手写 Host 项优先;生成的域名 DNS 映射按连接规则顺序保留 AI 之前的非 AI 域名/规则集例外,不把 Host 当成能表达进程、协议或 IP 条件的路由系统。宽泛 AI 域名规则仍须审阅覆盖范围。
Net.Coffee 的 Claude 页通过随机 *.d.ip.net.coffee 探测递归 DNS 出口,不直接查询 Claude 域名;使用 DOMAIN-SUFFIX,ip.net.coffee,🤖 AI 同时覆盖页面和探测子域。Surge 派生根域/通配 Host 到 Cloudflare DoH,并随 AI 出口。本机 Chrome+Surge 已复测为美国 Cloudflare「未检测到泄露」,仅代表该探测路径的实际结果,不能推广为所有客户端/域名/远端解析器已验收,见 docs/NET-COFFEE-DNS-2026-10-04.md。
规则变更先读 docs/SURGE-CHANGES.md。API 必须保住一条启用且末尾的 FINAL;禁止停用、删除、改类型或创建重复 FINAL,排序必须提交完整且无重复的 ids。IR 对遗留坏数据防御性补齐 FINAL,其失效策略降级 DIRECT。重置规则在事务内保留自定义规则,不重复插入原主键,也不能用 reset 代替定向调整生产规则。
规则按首条匹配执行(另留意 pre-matching)。业务例外须在覆盖它的广告/通用规则集之前,特定 UDP 拦截须在对应服务域名规则之前。靠前的 IP 规则集是否加 no-resolve 必须按意图决定:它能避免主动解析,但会改变未解析域名的 IP 拦截覆盖。普通规则值与策略引用用 surge-format.ts 编码,逻辑表达式保留结构;不要手拼含逗号的正则或含换行的字段。
共享订阅不附公开默认控制口令;默认关闭 Remote Controller/HTTP API,确需使用时按设备配置独立凭据与访问限制。MITM 不关闭上游服务器证书验证;需要 MITM 时另做设备 CA、开关与 HTTPS 行为验证。
验证与完成标准
WireGuard 已有实现见 docs/WIREGUARD-LAN.md:历史身份曾放在 sub_tokens.wireguard(已迁移为独立加密设备,见下段),设备使用独立 token/地址/密钥,不加入共享公网代理池。irFor() 只附加当前 token 身份,原共享订阅保持无 WG;历史 token PUT 省略 wireguard 时保留身份;当前通过独立设备映射兼容,token API 不再接受身份写入。标准 WG 配置保留独立授权导出;Shadowrocket WG YAML 只属于历史实现,本轮已移除生成器与 DTO 链接,旧专用路径明确返回 404。Mac Surge 入站要求 6.11+,旧版本 CLI 解析通过不等于入站验收。撤销订阅 token 不等于撤销已导入的 WireGuard peer。2026-11-01 实测 Apple VNC 成功组合为 iPad 官方 WireGuard → njcq → Mac Surge 6.11.0 build 12660,MTU 1280;同机 Shadowrocket 路径出现媒体缺包/黑屏,具体实现原因尚未确认。
WireGuard 最新产品约定(2026-11-02,已部署):开发先读 实现交接、完整设计 与同目录可点击原型。用户个人 macOS/iPadOS/iOS 已统一 Surge。设备页保留固定 IP、状态、最后活动、流量、管理操作及独立 WG 导出,删除“使用客户端”。Surge 订阅增加可选设备下拉:不选为原默认纯代理 URL,选中生成带设备分发凭证的派生 URL,只附加此设备身份;清除选择恢复默认 URL,不改变其他已生成链接。设备身份迁到独立实体,旧 token 兼容保留;不实现身份模块方案,也不把 UI 选择写成共享 token 的全局设备状态。凭证不可枚举,#!MANAGED-CONFIG 必须保留完整设备参数;暂停需真正撤销 peer。原型附加设备均为演示,不能拿去初始化生产。实现状态、API、网关安装与验收边界见 WIREGUARD-DEVICES。已将实际旧 token 身份迁移到加密 wg_devices,旧 token 用 wg_device_id 兼容映射;/api/tokens 不再接受 wireguard 写入,普通列表不返回密钥。wg-master.key(或启动环境 WG_MASTER_KEY)必须与 SQLite 一同受控备份,丢失不得重新生成覆盖;暂停/恢复/重置/移除由持久任务等待运行与持久配置双确认,LA 与 njcq agent 已部署,原有两台设备只读核对并 confirmed,网关持久/运行 peer 身份不变、普通代理正文不变。用户随后已在 macOS/iPadOS/iOS 全面验证 Surge + WireGuard 可工作;真实新增/暂停/恢复/重置等未由本轮助手执行,不把组网验证扩写为所有生命周期、长期/睡眠/换网均已验收;FFSB 原生 WG 试验属于不同身份与网络,不合并进 njcq。
同日后续:单一主订阅:用户已明确只保留一个订阅,生产通过 API 删除两条 NJCQ WireGuard - iPad Pro/Mac Studio 旧订阅,仅保留原 Proxy Station 主订阅及原默认 URL。两台设备身份、IP、peer 和独立导出仍保留,统一从主订阅选择设备生成带凭证的派生 URL;旧设备 token URL 已 404,不再按早期兼容约定恢复这两条卡片。该要求是当前生产收拢,不是将某台设备写成主订阅的全局身份,也不限制其他部署的通用多订阅能力。详情与备份见 单一主订阅记录。
同日后续:停止 Shadowrocket WG 兼容:用户确认 macOS/iPadOS/iOS 的 Surge + WireGuard 已实测可工作,Shadowrocket 的 Apple VNC 失败排查仅作历史记录,不再推进该路径。Shadowrocket 节点与 conf 仅下发普通代理数据:路由不附加/读取设备 WG 身份,序列化器忽略 IR.wireguard,不生成 WG 私网策略规则或额外节点导入提示,也不再按 WG 身份改写动态组筛选。已删除 Shadowrocket 专用 WG YAML 生成器与 DTO 导出链接,/sub/:token/wireguard-shadowrocket 显式返回 404,防止落入 SPA。保留 Surge 可选设备、标准 WG 独立导出/二维码、设备与网关管理,普通 Shadowrocket 代理兼容仍保留。该清理当前仅完成本地代码验证,尚未重新部署。
FlClash Android(2026-11-02):已核对官方 v0.9.2 及其固定内核,WireGuard 出站支持;当前标准 Android 实现未提供 Surge 式 allow-peer-access 本机服务入站桥接。不能用 allow-lan、TUN、内部栈 Listen 方法或 WG 握手成功代替双向设备验收。FlClash 可选设备身份已部署,旧 /clash 默认仍为纯代理,设备凭证按用途隔离;尚未进行 Android 真机测试,不恢复 Shadowrocket WG 兼容。完整依据及兼容性审查见 FlClash 核对 与 配置兼容性。
| 改动 | 建议验证 |
|---|---|
| 服务端逻辑/解析器/生成器 | pnpm --filter @proxy-station/server typecheck + 相关 Vitest;新增行为用占位凭据构造 IR/分享链接用例 |
| shared schema/DTO/协议能力 | pnpm -r typecheck + pnpm test,兼顾前后端消费者与各客户端产物 |
| Vue 页面/样式/状态 | pnpm --filter @proxy-station/web typecheck + pnpm --filter @proxy-station/web build;页面交互与局域网 HTTP 复制按需验证 |
| 数据库结构/启动行为 | 临时 DATA_DIR 或 createDb(':memory:') 验证新库与旧库升级;注意下述导入副作用 |
| 文档/协作入口/技能 | 检查路径、导入、符号链接、技能 frontmatter 与脚本语法;不为纯文档修改触发部署 |
| 节点部署工具/交付页生成器 | Bash 语法与 Python 编译检查、占位配置离线验证;迁移核对原文件、私有路径 Git 忽略及技能链接,不为迁移执行远端脚本 |
涉及节点、组、规则、DNS 或 Surge 段落时,在上述检查之外对实际订阅运行 scripts/check-surge.py;涉及规则集或顺序用 --fetch-rulesets,审阅域名遮蔽提示。脚本调用官方 Surge CLI,不更换活动配置;输出 OK 不表示测速、业务连接或设备隐私已验收。敏感配置用权限 600 的临时文件,不进入仓库或对话。生产写入先在线备份,再 API 更新、重读、HTTPS 拉订阅与 CLI 验证;必要时同时用 mihomo 校验受影响的 Clash 产物。
取订阅后先核对下载成功、非空及预期结构,再交给原生解析器。mihomo 在目标配置不存在时可能创建默认配置并报告成功,不能用这个结果验收订阅;所有检查遇到失败即停止,避免前一步失败而最后一条命令成功掩盖错误。
区分数据库、实际 HTTPS 产物、客户端活动配置与设备网络结果。用户授权的同机复测需刷新既有托管配置时,先核对托管 URL、备份并读取最新组选择,用 surge-cli managed-profile update 后检查 effective 配置与实际选项。组改名会改变客户端保存选择的身份;必要时仅将原选定节点及父组路径迁移到新名称,保留 All Nodes/AI Nodes 的独立选择,不擅自切换出口或活动配置。客户端模块可覆盖生成的组类型与参数,不能把本机模块效果写回生产数据库。
db/index.ts 在模块加载时就执行 export const db = createDb(),包括只导入 createDb 的情形;临时数据库测试/启动验证应在加载模块前设置绝对路径的临时 DATA_DIR,不要拿本地真实 server/data/ 做试验。现有生成器单测使用内存中的 IR;运行服务还会执行初始化。
改生成器时检查节点过滤、组成员引用、规则顺序和 FINAL 兜底;Clash 可用 mihomo 校验,Surge/ShadowRocket 的客户端语法与实际连接仍需真机验收。excluded/skippedRules 是生成器返回字段,订阅 HTTP 响应只返回配置正文。
完成后说明改动、已通过的验证与尚未完成的实际验证。新增业务约束或修正旧认识时同步更新这里或相应 docs/,让后续 Codex/Claude Code 会话能接续。
FFSB / Claude 手动订阅分流(2026-10-19)
最新方案见 docs/FFSB-MANUAL-SUBSCRIPTION-2026-10-19.md,已替代早先本机协调层。当前仅用正式托管 Proxy Station;不恢复 Proxy Station Claude 或 LaunchAgent。可见 🦀 Claude 为 select,新增 🦁 FFSB;仅源清单 25 项受它选择,合并的集合外地址始终经隐藏 Claude-Surge-Internal → 🧠 AI Nodes。Surge-only 生成器适配读取生产 settings 的 FFSB JSON,其他客户端保持原行为。25 项唯一源 server/assets/ffsb-domains.json,rules/ffsb-domains.json 为符号链接。
必要设备前提:FFSB disableDns=true 并重连生效,本机实测仍保留内部 DNS;Surge 增强模式用 fake-IP 接住普通域名流量。不要给这 25 项添加 always-real-ip,否则真实地址可被 FFSB 系统路由提前接走,绕过手动选择。选择 FFSB 时绑定 utun100 并向 100.70.255.254 查询;选择代理时远端解析;FFSB 不运行时选它允许失败,不自动回退。其他 Mac 需一次性确认接口、DNS 和客户端设置,不能声称仅导入就通用;iOS 未验收。用户已授权本次一次性设置调整和有恢复保证的 FFSB 断开/重连测试,仍未授权重启 Surge 引擎。真实模型流、长期/睡眠/换网及完整启动顺序仍未验收。
本机 FFSB 原生 WireGuard 独立验证(2026-11-02)
探索记录、管理连接边界与接续步骤见 docs/FFSB-WIREGUARD-SURGE-2026-11-02.md。用户授权导出既有 WG 身份并独立验证:测试期间 FFSB GUI/daemon 停止,27 项原生规则、隧道内 DNS、35/35 多轮请求、错误端点反向对照及 39 秒公开响应传输通过。原生方案只使用已下发配置快照,没有替代 NetBird 管理连接;长期/睡眠/换网/重新认证、真实模型业务与完整在线规则集快照未验收。真实 WG 身份和证据仅在本机权限 700/600 的私有归档中,绝不入 Git;不写入共享订阅。
当前状态(同日 11:05 回退验收):按用户要求恢复正式托管 Proxy Station+FFSB,Claude → 🦁 FFSB,FFSB GUI/daemon 和 utun100 已恢复,disableDns=true;3 轮 24/24 请求、原清单 25/25 规则、FFSB 内层 utun100/外层 DIRECT en0、集合外美国 AI 与普通日本 Proxy 分流通过。试验 Proxy Station WG Validation 已移出 Surge 配置列表并私有归档,原托管文件、生产数据、生成器及其他手动组选择未改,未重启引擎。FFSB 下发新增 hCaptcha 两项尚未同步生产 Surge 清单,保留原方案范围。后续若再试原生 WG,须先断开并停止 FFSB;两者复用相同身份,不可并行连接。不要依据旧试验快照擅自重新激活原生 WG。
Surge CLI 的 profile dump 会隐藏认证字段,不能将其输出直接复制成可用的完整配置;需保留真实凭据时在本机私有文件中使用磁盘原件并校验,输出与备份仍须脱敏、受限权限。