ShadowsocksX-NG2
ShadowsocksX-NG 的现代化重写版本。新工程的全部源码与构建配置放在本目录下。
依赖的二进制工具(sslocal、v2ray-plugin 等)不再以子模块方式在仓库内构建,而是直接复制外部项目发布好的可执行文件。HTTP 入站由 shadowsocks-rust 直接提供,2.0 不再携带 Legacy Privoxy。
旧版工程源码已从本仓库移除,归档在 shadowsocks/ShadowsocksX-NG 的 legacy-v1 分支下,仅供参考,不保证可构建。
工程结构
-
project.yml— XcodeGen 工程定义,唯一事实来源。.xcodeproj与App/Resources/Info.plist、*.entitlements、Tests/Info.plist都是生成物(已 gitignore),改工程结构一律改project.yml后重新生成。 -
App/— app target 源码(SwiftUI 菜单栏 app,LSUIElement)。目录即分层,层内按功能分组:Composition/— 组合根与进程生命周期:@main、依赖装配、应用形态与窗口激活策略。Application/— 应用层(无 SwiftUI 视图):*Workflow/工作流 module(UI-facing interface 与实现协作者)、ProxyRuntime/运行时控制器家族与事实投影、Services/LaunchAgent/登录项/helper/防火墙。Presentation/— 跨功能表现层:错误呈现、被多个界面面共用的状态模型。UI/— 视图层,按子视图分目录(Workspace/、Home/、Servers/、Subscriptions/、Rules/、Settings/、Diagnostics/、StatusMenu/,Servers/Plugin/为插件管理子视图)。PlatformEffects/— 平台效应 seam(协议 + InMemory 替身 + AppKit adapter),系统框架访问集中于此。Resources/—Info.plist、entitlements(生成物)与Assets.xcassets、*.xcstrings(入库)。
分层边界由
Tests/SkeletonTests.swift的正向圈定守卫断言:只扫描App/UI/,视图层不得引用运行时控制器、工作流实现协作者或原始目录/凭据存储类型;新增运行时适配器放进Application/即自动落在扫描范围外,无需登记豁免。注意App/下任何非源码文件都会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle,因此文档不放App/(project.yml已排除**/*.md)。分层与主题的组织方式、守卫为何取正面圈定、以及 XcodeGen 的上述约束见 ADR-0029。 -
Agent/— 代理运行时 wrapper(独立可执行文件,装入Contents/MacOS/,由 LaunchAgent 常驻):读sslocal-active.json契约,以绝对路径启动和监管 sslocal(SIGTERM 链式停止、SIGUSR1 热重载/结构重启、崩溃时非零退出交 KeepAlive 重放,spec #21 D2/D5/D7)。PAC HTTP endpoint 已随 issue #67 移除。 -
LaunchAgent/—SMAppService.agent(plistName:)的注册清单,装入Contents/Library/LaunchAgents/;ProgramArguments用 bundle 相对路径,由 launchd 按注册 app 的 bundle 位置解析。 -
Domain/— 领域核心(配置目录树、凭据引用与持久化,spec #21 D3/D5),与 UI 无关。目录即主题,根下不留平铺文件,子目录口径取自GLOSSARY.md的章节划分:Catalog/— 配置目录树、结构不变量错误、目录文件存储、节点与凭据身份,以及ss://编解码SsUri。Subscription/— 订阅文档解析、抓取、订阅记录与刷新失败。Rules/— 代理模式、规则身份与匹配、内置规则快照、规则集合/浏览/分析、离线规则测试;Rules/Custom/为用户自定义规则的编辑草稿、校验与存储。Runtime/— 代理运行服务:运行时文档与监听设置、ACL 与部署收据、路径、原子写入、文件存储、日志与事件、端点/端口/socket 探针。Activation/— 激活候选校验、激活状态机与RuntimeConfiguration。Settings/— 用户设置的持久化:代理设置、监听设置、静默启动。SystemProxy/— 系统代理 typed 配置、property list 映射、计划器与特权 helper 的 XPC 契约/请求引擎。Plugin/— 托管插件、用户插件映射与插件安全校验。Credentials/、LegacyImport/、Diagnostics/— 凭据存储与写入日志、旧版导入、诊断报告。
跨进程契约按文件编入其它 target:
Runtime/的 10 个文件同时编入 wrapper(RuntimePaths、SslocalRuntimeDocument、RuntimeLog等),SystemProxy/全部 6 个文件编入特权 helper。project.yml里这 16 条是显式路径,在Domain/内移动文件必须同步,漏改会让xcodegen generate以缺文件直接失败。布局守卫(Tests/Architecture/SkeletonTests.swift的DomainLayoutTests)断言根下无平铺.swift、树下只有.swift(非源码文件会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle)。 -
Tests/— 单元测试 target,随ShadowsocksX-NG2scheme 运行。目录即主题,根下不留平铺测试文件:Architecture/— 跨切面工程约束守卫(分层与布局、平台效应 seam、开窗落点、清单完整性),扫源码或构建产物而不驱动行为。TestSupport/— 测试等待与清理 helper 的故障回归用例。Support/— 夹具、替身与 helper,不含用例(CatalogFixtures、ProxyRuntimeFixture、SystemProxyFakes、AppArtifact、TestSourceTree等)。Agent/、Activation/、Catalog/(工作流用例在Catalog/Workflow/)、Subscription/、Rules/(自定义规则在Rules/Custom/)、ProxyRuntime/、ProxyControl/、Runtime/、Settings/、SystemProxy/、Plugin/、Diagnostics/、Credentials/、Workspace/、Servers/— 按功能领域的用例,与生产代码的领域主题和*Workflow同名同界。Smoke/— 真实进程冒烟,独立 target 与 scheme(task test:smoke)。Fixtures/RuleSnapshots/— 生成的内置规则快照夹具,作为 folder resource 随测试 bundle 发布(GeneratedRuleSnapshotTests按subdirectory:读)。
断言需要读源码树(
Vendor/清单、App/、Domain/)时一律经Support/TestSourceTree.swift从#filePath沿父目录上溯定位ShadowsocksX-NG2/,不写死父目录层数——测试文件处于不同深度,写死层数会在文件移动后静默指错。布局守卫TestsLayoutTests只断言根层无平铺.swift(比Domain/那条弱一档:根层的Info.plist与Fixtures/下的 JSON 都必须在)。Tests/Support/AppArtifact.swift同时编入 Smoke target,是project.yml里的显式文件路径,移动需同步。 -
Vendor/<name>/manifest.json— 外部二进制的固定供应链清单(tag + 资产 URL + 资产 SHA-256 + bundle 内位置 + 签名 identifier);二进制本体与.fetched.sha256戳是构建缓存,不入库。 -
Vendor/rules/geolocation-cn/— 内置中国域名规则快照(issue #63):snapshot.json(规范化规则 + 元数据 + 损失报告)、manifest.json(上游版本与快照 SHA-256)、NOTICE(许可证与归属)。普通构建只读本地快照,绝不抓取或转换。 -
Vendor/rules/china-ipv4/— 内置中国 IPv4 CIDR 直连候选快照(issue #64):同上三件套,来源为 gaoyifan/china-operator-ipchina.txt固定 commit。规则模式的proxy_allACL 同时编入中国域名与 IPv4 CIDR 直连候选。 -
Vendor/rules/gfwlist/— 内置 GFWList 代理候选快照(issue #65):同上三件套,来源为 gfwlist/gfwlist 官方 Base64 AutoProxygfwlist.txt固定 commit。域名锚点转换为后缀匹配,满足条件的空路径或根路径 URL 前缀转换为精确域名匹配;「未匹配时直连」的bypass_allACL 编入这些代理候选。 -
Scripts/— 供应链脚本(见下节)与打包门槛断言;另有update-geolocation-cn.sh/update-china-ipv4.sh/update-gfwlist.sh(显式维护动作,抓取固定版上游并转换)与verify-rule-snapshots.sh(构建前离线校验快照完整性)。
外部二进制供应链
外部二进制只用上游官方构建产物:sslocal(shadowsocks-rust v1.25.0,装于 Contents/Helpers/)、v2ray-plugin(v1.3.2)与 Cloak ck-client(v2.12.0,两个插件均装于 Contents/Helpers/Plugins/)。信任根是提交在库里的清单哈希;构建链如下(spec #21 D6/D10):
- fetch —
Scripts/fetch-external-binaries.sh下载清单固定 URL 的资产、逐字节校验archiveSHA256,按assetFormat处理 tar 归档(默认,提取archiveMember)或直接二进制(binary),并确认 arm64,落盘Vendor/<name>/并记录戳。资产哈希不符 → 立即失败;本地缓存漂移或清单变更 → 自动重新下载复验。 - generate —
xcodegen generate把清单对应二进制作为 Copy Files 项装入Contents/Helpers[/Plugins]。注意顺序:先 fetch 再 generate,文件在生成时必须已存在,否则 Copy Files 阶段会被静默丢弃(generate 也会直接报缺文件)。 - verify on every build — 预构建脚本阶段重跑 fetch 脚本(缓存命中时只对本地戳快速复验),哈希或清单漂移使构建即失败。
- sign —
Scripts/sign-embedded-helpers.sh(post-build 阶段)对每个二进制 Developer ID 重签:--options runtime --timestamp --identifier <清单 signIdentifier>(插件形如<bundle-id>.plugin.<name>);随后 Xcode 完成 app 外层签名,封存重签后的嵌套代码。 - gate — 构建产物必须过
Scripts/packaging-gate.sh <App.app>:嵌套代码签名严格校验、主应用 DevID + Hardened Runtime 且无 App Sandbox entitlement、逐二进制清单覆盖(Helpers 下每个 Mach-O 都在清单内且 DevID 重签通过)。篡改任一二进制会因签名失效被断言发现。
签名与运行形态基线
- bundle id
com.qiuyuzhou.ShadowsocksX-NG2,与 Legacy(com.qiuyuzhou.ShadowsocksX-NG)区分;运行时数据落~/Library/Application Support/ShadowsocksX-NG2/。macOS 15+ / arm64。 - Hardened Runtime(DevID 发布路径;免证书的 ad-hoc Debug 例外,见下);不启用 App Sandbox,entitlements 为空 dict;签名样式固定
Manual。 - 证书与 Team 都不入库(开源仓库):
project.yml不含CODE_SIGN_IDENTITY/DEVELOPMENT_TEAM,两个键由 project xcconfig 按 config 在构建期注入:-
Configs/Signing-Debug.xcconfig(入库)基线是 ad-hoc(-):新 clone 与 CI 不需要任何证书就能task build/task test。arm64 要求代码至少有 ad-hoc 签名才会被加载,所以「免证书」不是「不签名」。ad-hoc 产物不带 Hardened Runtime(Xcode 对 ad-hoc 签名不传-o runtime,实测;Scripts/sign-embedded-helpers.sh的 ad-hoc 分支同样不加),Hardened Runtime 由 DevID 发布路径与打包门槛断言。 -
Configs/Signing-Release.xcconfig(入库)基线是 Developer ID Application:发布产物必须真签名,这里不给免证书路径。 -
两者都可选包含
Configs/Signing.local.xcconfig(gitignore)。有证书的人(含维护者)用它升级本机身份,例如让 Debug 也走 Developer ID:cat > Configs/Signing.local.xcconfig <<'EOF' DEVELOPMENT_TEAM = YOUR_TEAM_ID CODE_SIGN_IDENTITY = Developer ID Application EOF -
该文件缺失时 Debug 构建为 ad-hoc(可跑单测),Release 构建以 Xcode 的「Signing for … requires selecting either a development team or a provisioning profile」明确失败,不静默降级。
-
测试 target(单测与冒烟)身份随同一基线:去宿主化(ADR-0021)后 xctest 包不再注入宿主 app,ad-hoc 也能加载执行(实测),因此测试无需证书。
-
注意
project.yml一旦重新设置这两个键,pbxproj 构建设置会盖掉 xcconfig(Tests/Architecture/SigningConfigurationTests.swift守卫该约束)。
-
- Release 配置关闭
CODE_SIGN_INJECT_BASE_ENTITLEMENTS,保证发布产物 entitlements 只来自项目文件。
开发、构建与测试
统一验证入口(从仓库根目录调用):
task --exit-code --taskfile ShadowsocksX-NG2/Taskfile.dist.yml validate
# 按指定顺序只运行所需关卡:
task --exit-code --taskfile ShadowsocksX-NG2/Taskfile.dist.yml validate -- lint test
在本目录内可用 task --exit-code validate。从任意目录可用 task --exit-code --taskfile /path/to/ShadowsocksX-NG/ShadowsocksX-NG2/Taskfile.dist.yml validate。入口按 Taskfile 所在位置定位工程目录,内部调用发行 Taskfile 中的单项任务。
默认按 lint → test → test:smoke → build 执行,也可按指定顺序选择子集,每关最多一次;任一关失败立即停止,后续关标记 not_run,进程返回非零。--exit-code 让 Task 透传关卡退出码;省略时 Task 仍返回非零,但可能使用自己的错误码。入口不自动重试,不修改测试并行设置,也不执行会修改源码的格式化。
每次运行创建独立的 .build/validation/<UTC时间>-<随机后缀>/,开始与结束均输出结果绝对路径。summary.txt 保存各关状态、退出码与证据状态;每个已执行关卡在同名子目录下保留合并 stdout/stderr 的 raw.log 和 status.txt(test:smoke 对应 test-smoke/),Xcode 关卡显式保存 result.xcresult。控制台只显示进度和摘要,需要完整错误时直接读取对应日志。所有产物均在现有忽略目录内;task clean 会删除它们,需要长期留存时先复制出去。
测试关通过本机 xcrun xcresulttool get test-results summary 和系统 plutil 尽力读取计数,无需新增脚本或工具依赖。test-summary.json 保存原始摘要,test-summary.stderr.log 保存 xcresulttool 错误,count-reader.log 保存计数读取错误。汇总展示通过、失败、跳过、预期失败及总数(Xcode 汇总口径,参数化测试的实际执行次数可能不同)。关卡与总体退出码由原命令决定,命令失败始终保留失败。结果包缺失或摘要不可读时显著标注 evidence incomplete(证据不完整)、计数未知,不改变命令结果,也不把缺失计数写成零。构建关不读取测试计数;未执行的关卡不等同于测试跳过。
统一入口成功表示所选关卡的命令通过,仍需核对证据是否完整;hostless、真实进程 smoke、原生 UI 和真实系统代理的验收边界仍分别成立,发布打包门槛见下文。
task test 与 task test:smoke 显式启用 Xcode 单测试超时:默认 120 秒,上限 300 秒。这是 runner 兜底,不保证测试任务正常清理,也不是性能门槛。规则阶段等待失败会抛错退出测试体;受保护的测试任务先释放夹具,再取消并等待结束。规则延迟 gate 和同步阻塞夹具的默认等待上限为 10 秒,超时必须报告失败。共享 AsyncGate 保留由用例控制的取消语义。Swift Testing 的 detached worker 超时报告保留创建用例名称和源码位置;应同时核对命令退出码与测试计数,不能仅凭单例通过数判断成功。
在本目录内,日常格式化、静态检查、工程生成、构建和测试优先使用 Taskfile.dist.yml 定义的任务:
task format
task lint
task lint:fix # 需要自动修复 SwiftLint 违规时使用;执行后再运行 task lint
task gen:prj # 生成 Xcode 工程
task build # 构建 Debug 版本 app
task test # 运行单元测试(不含真实进程冒烟)
task test:smoke # 运行真实进程冒烟测试(拉起真实 sslocal,见下节发布门槛)
task format 会格式化 App/、Tests/、Domain/ 和 Agent/ 下的 Swift 文件;task lint:fix 会修改源文件,执行后检查并复核 diff。
新 clone 或清单变更后,在本目录内先取外部二进制,再生成工程(顺序不可颠倒):
Scripts/fetch-external-binaries.sh
task gen:prj
无证书也能构建与跑单测:Debug 基线是 ad-hoc,不需要 Configs/Signing.local.xcconfig。只有要做 Developer ID 签名(发布,或让本机 Debug 产物过打包门槛)时才写该文件,内容见上节「签名与运行形态基线」。
Tests/Smoke/ 是真实进程冒烟(经 wrapper 拉起真实 sslocal 验证启动、握手、
ACL 路由与插件监管),不在默认 task test 内,由独立 scheme 按需运行。
发布前运行发布门槛检查:
# 发布门槛(必须全绿):真实进程冒烟 + 打包门槛
task test:smoke
Scripts/packaging-gate.sh \
.build/derivedData/Build/Products/Debug/ShadowsocksX-NG2.app
打包门槛只接受 Developer ID 签名的产物:Team 期望值取自 Configs/Signing.local.xcconfig(可用 EXPECTED_TEAM_ID=<Team ID> 显式覆盖),两者都没有时该断言直接失败,不静默跳过。无证书的 ad-hoc Debug 产物会在此明确失败(gate: hint 会说明原因),这是预期行为。
代码风格工具
swift format随 Xcode/Swift 工具链提供,无需单独安装(基线:Apple Swift 6.4);SwiftLint 需brew install swiftlint(基线 0.65.0)。工具缺失时,含本目录 Swift 文件的提交会被 pre-commit 钩子拒绝而不是放行。- 配置文件:
.swift-format是 Swift 6.4 工具链默认规则的快照(固定成文件,工具链升级不漂移);.swiftlint.yml默认规则起步,只声明排除项与个别和 format 基线冲突的关闭项(文件内有触发案例注释)。 - 仓库级
pre-commit钩子的启用方式和检查范围见根目录README.md。 - 调整规则只针对实际痛点:修改
.swift-format/.swiftlint.yml的 PR 须给出触发案例,并同步更新本节中的基线版本。
订阅资料
订阅可在 SIP-008 JSON 根级提供 bytes_used、bytes_remaining(非负整数,最大为 UInt64),以及自定义的 expires_at(服务到期时间)、traffic_reset_at(流量重置时间)。日期值使用 RFC 3339 字符串,省略或 null 表示未提供;无效资料仅忽略该项,不影响有效服务器或分组资料。
成功刷新完整替换白名单订阅资料,省略或无效的字段清除旧值;失败或取消保留最后成功资料与独立成功时间。资料和目录记录同文档原子保存,不另存原始响应或含密码的服务器明文快照;密码和插件参数沿用 Keychain 存储。旧目录文件缺少成功时间时不猜测。
订阅卡片显示返回的有效资料;仅在两项流量都有效且总量大于零时显示消耗进度。日期按用户当前语言与时区显示,到期和重置资料不改变代理启停或激活行为。
内置规则快照
规则模式使用的内置快照是显式维护产物:
- geolocation-cn(issue #63):人工复验上游后运行
Scripts/update-geolocation-cn.sh,从固定版 Loyalsoldier/domain-list-customgeosite.dat解析 typed 条目并生成Vendor/rules/geolocation-cn/snapshot.json。 - china-ipv4(issue #64):运行
Scripts/update-china-ipv4.sh,从固定版 gaoyifan/china-operator-ipchina.txt规范化并去重 IPv4 CIDR,生成Vendor/rules/china-ipv4/snapshot.json。 - gfwlist(issue #65):运行
Scripts/update-gfwlist.sh,从固定版 gfwlist/gfwlistgfwlist.txt解码官方 Base64 AutoProxy 列表并生成Vendor/rules/gfwlist/snapshot.json。||host/||host^转换为域名后缀匹配;|scheme://host(可含合法端口)在路径为空或仅为/,无查询、fragment 或用户信息,且 host 为无通配符的合法域名时,提取 host 转换为精确域名匹配,忽略协议和端口。仅含根路径/可转换,包含具体路径的规则继续跳过。@@保留直连动作。其他 URL 前缀、路径、通配符、正则、单标签前缀与 IP 字面量规则逐类计入损失报告。被代理规则完全覆盖的@@例外不写入无效 ACL 项(sslocal 域名匹配 proxy_list 优先于 bypass_list),保留代理规则,遮蔽数只计入absorbedCount;逐条取证输出到维护脚本 stdout。
快照使用 schema 2 / converter 2.0.0 的紧凑 JSON:每条规则只有 action 和 match,来源只在 metadata.source,转换报告只保留计数。被吸收的 .cn 条目与被遮蔽的 GFWList 例外不随快照分发,禁用宽规则后不再恢复;详见 ADR-0024。旧版本快照拒载。
规则领域值与文件 DTO 独立演化。ProxyRule 同时表达规范化规则值与跨来源规则身份,自定义条目另外保留 UUID。自定义规则文件写入 schema 3,不再逐条保存 source;兼容读取 schema 1/2,读取不重写,下一次正常保存才更新格式。内置快照格式不变。见 ADR-0039。
Python 是唯一的上游转换实现;Swift 只加载、校验并消费快照,不另行解析上游输入。转换脚本的离线回归检查:PYTHONDONTWRITEBYTECODE=1 python3 Scripts/test-rule-snapshot-converters.py。默认 task test 也会运行两组 Python 检查;可单独运行 task test:rules。另运行 PYTHONDONTWRITEBYTECODE=1 python3 Scripts/test-rule-conversion-behavior.py,验证转换行为和单测 bundle 使用的固定快照夹具。审查转换变化后,可通过同一命令加 --write-fixtures 重建夹具。夹具时间固定,正常测试逐字节检查生成结果。快照完整性检查:Scripts/verify-rule-snapshots.sh。
更新失败保留上一份有效快照。geolocation-cn 转换会因未知语法/损坏输入失败;china-ipv4 转换还会因异常格式(拒绝率过高)、全部失效或相对上一份快照的异常规模变化失败;gfwlist 转换因未知语法、损坏 Base64、异常规模或相对上一份快照的异常规模变化失败。普通构建由 Scripts/verify-rule-snapshots.sh 离线校验快照存在、摘要匹配且 schema/转换器版本一致,缺失或损坏即构建失败。分发物必须携带各自的 NOTICE(许可证与归属)。
规则模式「未匹配时代理」生成的 proxy_all ACL 同时包含中国域名与中国 IPv4 CIDR 直连候选,固定本地绕过优先。规则模式「未匹配时直连」生成的 bypass_all ACL 只编入 GFWList 可生效代理候选;两种默认动作不把全部内置来源无条件并集。未匹配目标与无可用规则命中的 IP 字面目标直连。CIDR 判定可能触发本地 DNS 查询:sslocal 为未命中域名做 IP 匹配时可能发起本地 DNS 查询,产品不承诺 DNS 查询均经远端 Shadowsocks 服务器。
依赖升级
升级任一外部二进制是显式供应链动作:人工复验新 release(下载归档、静态观测哈希/架构/签名,不执行),随后在一次 PR 中同时改 Vendor/<name>/manifest.json 的 release/asset/url/archiveSHA256 与对应测试锚点。不接受 latest,不在仓库内构建,不做应用内更新通道(升级仅随 app 发版)。
用户插件目录
PluginCatalog 合成当前托管清单与本机 plugins.json 用户记录,名称精确区分大小写。用户映射优先,失效 override 不退回托管程序;删除 override 恢复当前 app 默认值。文件读取错误保留阻断事实,不按空映射处理。用户路径、本地启动参数与插件传输模式不进入服务器资料、订阅或分享文件。用户路径也不进入诊断报告。
设置页底部的“插件”区域统一展示内置与用户插件,按记录展示全部名称,支持新增、编辑名称集合与路径、逐名称覆盖内置、删除记录与恢复同名内置。用户与内置记录可以设置插件传输模式 tcp_only 或 tcp_and_udp;默认 tcp_only,生成 sslocal 配置时省略 plugin_mode。应用不探测插件的 SIP003u 支持。内置编辑器中的“清空参数”只清空草稿启动参数并保留模式,保存后生效。名称与启动参数使用列表编辑,启动参数保留顺序和完整字符串;路径支持手动输入与文件选择。读取失败时隐藏条目并提供只读重试,管理 UI 不提供整体重置。映射提交与显式修复 interface 在组合根统一接入表单、激活与诊断。保存成功后刷新激活候选并异步收敛,运行失败不撤销保存。没有文件替换监控。plugins.json v1 每项独立迁移为带持久身份的用户记录,下一次保存写入 v2,不按相同路径合并。现有内置程序同时提供 v2ray-plugin 和 v2ray-plugin-v1。详见 ADR-0033。
MacOSPluginInspection 只读取下载隔离、签名与系统评估事实,不启动用户插件、不修改系统策略。检查失败或不适用为未知;策略评估拒绝不等于实际执行禁令,也不阻止映射保存或激活。真实 macOS 拦截提示不由 hostless 替身测试证明。
Cloak 托管插件
随应用提供官方 Cloak v2.12.0 的 darwin-arm64 ck-client,服务器引用名称为 cloak 或 cloak-v2,两者共享一条内置记录。ck-client 是打包文件名,不是可引用名称。同名用户插件仍优先;失效覆盖不会自动回退到内置程序。
服务器插件选项原样传给上游,不进行 Cloak 字段语义校验。可以使用分号分隔的 Key=value 参数,或使用外部 JSON 配置文件的绝对路径。外部文件由用户维护,应用不复制文件、不管理文件凭据或生命周期;分享路径不包含文件内容,接收方需要自行提供对应文件。本地启动参数仍独立保存,不随服务器资料分享。
固定版本配置解析存在以下限制:只有同时含 ; 和 = 的字符串才被识别为参数列表,单个 Key=value 会被当作文件路径;转义分号仍会被切分,引号和反斜杠也不能按通用 SIP003 转义方式可靠传递;空段会终止后续解析。需要复杂值时可自行维护 JSON 文件。提供 UID、PublicKey、ServerName、EncryptionMethod,并按需显式设置 NumConn=4,不能依赖 README 所述的默认 4;省略时固定版本实现关闭连接复用。显式端点字段会覆盖 SIP003 环境变量,因此通常应省略 LocalHost、LocalPort、RemoteHost、RemotePort。
默认插件传输模式为 tcp_only。上游 UDP=true 切换为 UDP 监听,不代表同时承载 TCP 与 UDP 的 SIP003u 支持;应用仍允许用户设置现有传输模式,本次只验收 TCP 配置接入。托管程序的 GPL-3.0 许可证、来源与第三方声明随应用装入 Contents/Resources/ThirdParty/Cloak/,源码与构建入口见其中 NOTICE。
本地与局域网监听
后台代理客户端运行时,本地 SOCKS/HTTP 始终监听 127.0.0.1,默认端口为 11086/11087;本地 SOCKS 固定开启 UDP 中继。局域网入口默认关闭,使用独立端口 11088/11089,支持 IPv4、双栈和仅 IPv6 三种方式。四端口必须在 1025...65535 内且全部不同。
局域网 SOCKS 的 UDP 中继和 SOCKS/HTTP 共用的身份验证独立设置。密码保存在 Keychain,运行时派生当前用户专用的认证文件;上游认证失败日志中的密码会隐藏。设置只检查 TCP 占用,不设置独立 UDP 端口。首页命令按选择地址使用本地或局域网端口,局域网身份验证开启时加入编码后的凭据;tooltip 显示完整命令。
未发布的旧监听配置不迁移,新监听使用默认值;其他偏好与服务器资料保留。设计见 ADR-0034。