Files
ShadowsocksX-NG/docs/project-structure.md
T
Qiu Yuzhou 94ef128db8 feat(system-proxy): emit privileged helper events to unified log
The root system-proxy helper logged nothing and launchd discards its
stdout/stderr, leaving helper launches, connection refusals, GUI-side
timeouts and root-side failures invisible. Share the minimal unified-log
emission unit (SystemLog) across GUI, wrapper and helper, and add a
dedicated closed helper event enum (lifecycle minimum: listener ready,
connection accepted/rejected with peer pid, typed apply/clear results)
with ADR-0045 privacy splitting. The emission seam is injected at the
helper engine and listener for test doubles. Helper events are
Console-only: no stderr, no agent.log, no diagnostic view or report
surface (ADR-0047).
2026-10-20 15:59:38 +08:00

64 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工程结构与源码搜索
[返回工程首页](../ShadowsocksX-NG2/README.md)
除特别说明外,命令从 `ShadowsocksX-NG2/` 执行,文中工程路径也相对于该目录。
## 工程结构
- `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/Architecture/SkeletonTests.swift` 的正向圈定守卫断言:只扫描 `App/UI/`,视图层不得引用运行时控制器、工作流实现协作者或原始目录/凭据存储类型;新增运行时适配器放进 `Application/` 即自动落在扫描范围外,无需登记豁免。注意 `App/` 下任何非源码文件都会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle,因此文档不放 `App/`(`project.yml` 已排除 `**/*.md`)。分层与主题的组织方式、守卫为何取正面圈定、以及 XcodeGen 的上述约束见 [ADR-0029](adr/0029-source-layout-declares-layer-and-theme.md)。
- `Agent/` — 独立的后台监管可执行文件,装入 `Contents/MacOS/`,由 LaunchAgent 常驻。只读取非秘密 `agent-supervision.json`,切换稳定配置与 ACL 链接,启动和监管 sslocal;不解析 sslocal 配置或 LAN 凭据。发布与失败处理见 ADR-0037、ADR-0038,GUI 单向配置输出见 ADR-0043。
- `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 与部署收据、路径、原子写入、文件存储、日志与事件(统一日志发射单元 `SystemLog`、统一日志行 `LogLine`、sslocal 输出 intake)、端点/端口/socket 探针。
- `Activation/` — 激活候选校验、激活状态机与 `RuntimeConfiguration`。
- `Settings/` — 代理监听配置文件、独立代理控制偏好、运行计算的内存快照与静默启动偏好。
- `SystemProxy/` — 系统代理 typed 配置、property list 映射、计划器、特权 helper 的 XPC 契约/请求引擎与 helper 侧日志事件。
- `Plugin/` — 托管插件、用户插件映射与插件安全校验。
- `Credentials/`、`LegacyImport/`、`Diagnostics/` — 主密钥存储与加密文件提交、旧版导入、诊断报告。
跨进程契约按**文件**编入其它 target:`Runtime/` 的 9 个文件和 `Settings/ApplicationSupportPaths.swift` 同时编入 wrapper(`RuntimePaths`、`RuntimeSupervision`、`RuntimeLog`、`SystemLog`、`LogLine`、`SslocalOutputIntake` 等),`SystemProxy/` 全部 7 个文件连同 `Runtime/SystemLog.swift` 编入特权 helper(helper 侧日志只进统一日志,ADR-0047)。`project.yml` 里这 18 条是显式路径,在 `Domain/` 内移动文件必须同步,漏改会让 `xcodegen generate` 以缺文件直接失败。布局守卫(`Tests/Architecture/SkeletonTests.swift` 的 `DomainLayoutTests`)断言根下无平铺 `.swift`、树下只有 `.swift`(非源码文件会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle)。
- `Tests/` — 单元测试 target,随 `ShadowsocksX-NG2` scheme 运行。目录即主题,根下不留平铺测试文件:
- `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-ip](https://github.com/gaoyifan/china-operator-ip) `china.txt` 固定 commit。规则模式的 `proxy_all` ACL 同时编入中国域名与 IPv4 CIDR 直连候选。
- `Vendor/rules/gfwlist/` — 内置 GFWList 代理候选快照(issue #65):同上三件套,来源为 [gfwlist/gfwlist](https://github.com/gfwlist/gfwlist) 官方 Base64 AutoProxy `gfwlist.txt` 固定 commit。域名锚点转换为后缀匹配,满足条件的空路径或根路径 URL 前缀转换为精确域名匹配;「未匹配时直连」的 `bypass_all` ACL 编入这些代理候选。
- `Scripts/` — 供应链脚本(见[外部二进制供应链](distribution.md#外部二进制供应链))与打包门槛断言;另有 `update-geolocation-cn.sh` / `update-china-ipv4.sh` / `update-gfwlist.sh`(显式维护动作,抓取固定版上游并转换)与 `verify-rule-snapshots.sh`(构建前离线校验快照完整性)。
### 源码搜索
先按上面的目录分层用 `rg --files` 定位,再限定目录或文件类型搜索内容;未找到时逐步扩大范围。以下命令从 `ShadowsocksX-NG2/` 执行:
```sh
# 查外部二进制版本与来源,先定位对应清单
rg --files Vendor/sslocal -g 'manifest.json'
# 查运行职责,限定相关 Swift 源码
rg -n -g '*.swift' 'sslocal|reload' Agent Domain/Runtime App/Application/ProxyRuntime
# 查规则内容时先只列出命中文件
rg -l '关键词' Vendor/rules Tests/Fixtures/RuleSnapshots
# 需要显示匹配行时,省略超长行并保留命中提示
rg -n -M 300 '关键词' Vendor/rules
```
`Vendor/rules/*/snapshot.json` 与 `Tests/Fixtures/RuleSnapshots/*.json` 是生成的单行 JSON。`-M 300` 按字节限制长行显示,不限制总输出量;`-m` 和 `head -n` 只限制行数,不能防止单条长行撑大输出。需要完整规则时,定位具体快照后按 JSON 字段读取。清单与 `NOTICE` 也保留在各自的 Vendor 目录中,可按需单独搜索。