Specola Core Architecture
本文从使用者和集成者视角说明当前 specola-core 的组件边界、启动顺序、控制面和数据面。它不是 类级 API 文档,也不把内部 C++ 类型作为稳定公共接口;稳定集成边界是 TOML、CLI 和 MessagePack RPC 控制接口。
1. 总体结构
Core 把功能分成四层:
- 进程与配置:CLI、单实例、TOML 加载和生命周期;
- 控制面:MessagePack RPC、状态、配置和运维操作;
- 决策面:规则、策略组、Provider、DNS 映射;
- 数据面:Mixed Proxy、TUN/eBPF、协议和网络传输。
2. 进程边界
Core 与 GUI 是两个独立进程:
- GUI 不包含 Core 内部头文件,也不直接持有路由、节点或 TUN 对象;
- GUI/API client 只依赖请求和响应契约;
- Core 可以独立于 GUI 运行;
- UI 可传
--parent-pid,父进程退出时 Core 优雅关闭; - 同一用户只允许一个 Core 实例,防止重复 listener、DNS 和路由接管。
macOS/Linux 的实例锁位于 /tmp/specola-core-<uid>.lock,Windows 使用用户会话 mutex。锁只保护 Core 实例,不代表所有配置端口一定空闲。
3. 启动顺序
正常 specola-core --config ... 的主要阶段:
重要区别:
- 配置解析成功不表示远端代理一定可达;
- Core 控制面健康不保证 mixed listener 正在监听;端口为
0、入站为none、绑定失败或手动停止时 都可能只有控制面运行; general.inbound.port在初始化时创建 listener 对象;协议非none且端口大于0时会自动启动,也可由 Runtime API/GUI 停止或重新启动;- 配置的 DNS 和 Enhanced Mode 可在启动阶段自动激活;
- TCP 控制接口在 Core runtime 初始化后由应用层绑定。
收到 Ctrl+C、SIGTERM 或父进程退出后,应用先停止控制 API,再关闭 Core runtime、连接、DNS 和 透明数据面。正常退出是系统 DNS、TUN/BPF 和 helper 状态正确回滚的重要条件。
4. 控制面
Transport
- 本地 IPC 始终启动:POSIX Unix socket 或 Windows named pipe;
[general].api-listen开启 TCP 监听;- 两种 transport 使用同一个 MessagePack RPC session、方法和 DTO;
- 连接保持打开,请求与事件推送复用同一连接;不存在 HTTP adapter。
API facade
CoreApi 按 System、Proxy、Config、Rule、Provider、Subscription 和 Tools 等职责提供 facade。 RPC handler 只负责 MessagePack 边界,实际操作进入 Core service/runtime;公开契约不暴露 C++ 对象。
修改操作通过共享 API context 串行化,避免配置、节点选择和数据面状态被多个控制请求同时修改。
信任边界
- 本地 IPC 依赖 socket/pipe 的操作系统访问控制;
- TCP 控制接口使用
api-secret认证; - 认证不提供传输加密,不要把 TCP 控制接口暴露到不可信网络,默认保持 loopback。
5. 配置与快照
主配置通过 ConfigManager 和 TOML parser 构建运行时对象:
主 TOML
├─ 基础配置、DNS、Enhanced/TUN/eBPF
├─ Proxies 与 Proxy Groups
├─ Rules / Sub-rules
├─ Proxy Providers
└─ Rule Providers + GeoSite/GeoIP/ASN resources
│
▼
Registry + Rule Matcher Snapshot配置加载会先建立节点/组名称空间,再校验规则引用、Provider 和 dialer-proxy 依赖。规则 matcher 最终生成可供 查询的数据结构;大域名集可以使用紧凑索引或映射快照,复杂规则保留顺序匹配,以兼顾内存和语义。
Provider 刷新采用“新内容下载/解析成功后再切换”的思路。失败的下载或解析不会把半成品规则直接 安装到正在使用的路由中。完整配置订阅与普通 Provider 分开管理,需要显式创建、刷新和激活。
Reload 边界
配置 reload 会重建配置、路由、节点、Provider 及 DNS 相关状态,并重新应用 mode、log、DNS、 general.inbound.port 和 general.inbound.protocols。运行中的 mixed listener 遇到端口变化会重新绑定;如果此前由用户 手动停止,reload 不会自动重新开启。
[general].api-listen 和 [general].api-secret 属于应用层 API server 设置,普通 reload 不会重建;修改 后应重启 Core。LAN/WAN 访问由 [gateway] 控制。详细矩阵见 General Config。
6. Mixed Proxy 数据面
Mixed listener 在一个 TCP 端口识别 HTTP proxy 和 SOCKS5。入站解析得到目标域名/IP 后查询 Route:
- Direct 模式立即选择
DIRECT; - Global 模式解析
global-proxy; - Rule 模式构建匹配 context 并按规则选择 outbound;
- 策略组把逻辑名称解析为当前健康/选中的实际节点;
- Dialer 按所选节点的协议、TLS 和 transport 建立连接;
dialer-proxy用于组合多跳链。
REJECT 在数据面终止连接。连接和流量统计进入 StatsManager,控制 API 可以查询或关闭活动连接。
7. 路由与规则
Route 同时维护:
- 节点 registry 和 Provider 节点 registry;
- 策略组选择器与健康检查;
- System Proxy 与透明数据面共用的 rule matcher;
- DNS IP→域名/出口映射;
- 域名、IP 和规则结果缓存。
规则保持全局 first-match 语义。适合索引的精确域名、后缀和 IP 范围进入专门数据结构;Wildcard、 Regex、复杂逻辑和部分进程条件保留在慢路径。优化不能改变用户可见的优先级。
普通 Mixed 流量和透明数据面统一使用 [rule].list。Linux eBPF 只把能够安全提前决定的规则下推内核, 其余继续交给同一个用户态 matcher。
8. Proxy 与 Transport
节点配置由协议 parser 创建对应 ProxyInstance。ProxyInstance 描述身份和能力,Dialer/Outbound 负责建立实际连接:
Route result
│
├─ DIRECT / REJECT
└─ ProxyInstance
├─ optional dialer-proxy multi-hop chain
├─ protocol handshake
├─ TLS / Reality
└─ raw / HTTP / WebSocket / HTTP Upgrade / gRPC transportTCP 与 UDP 能力不完全相同。透明 UDP 通过协议对应的 datagram 实现;不支持 UDP 的节点不能因为 TCP 配置解析成功就自动获得 UDP 能力。协议字段和限制见 Proxies,固定多跳及 dialer-proxy 的当前边界见 Proxy Chain。
9. DNS 子系统
DNS runtime 独立维护 resolver worker、UDP/TCP loopback listener、响应缓存和 Route 中的反向映射:
- DNS worker 只在独立 listener、系统代理域名探测或透明数据面需要时存在;
- Proxy 服务端域名通过 bootstrap nameserver 直连解析,防止循环依赖;
- 查询域名先经过路由,可将上游 DNS 通过代理发送;
- Fake-IP 把域名和选择的出口绑定到合成 IP,透明连接再恢复该信息;
- DNS response cache 与 Route reverse cache 是两类不同缓存。
详见 DNS。
10. Enhanced 数据面
原生 TUN
TUN 后端读取 IP packet,经 TCP/UDP pipeline 形成流,补充 DNS/进程等 context 后使用 tun matcher。 TCP 可通过系统/原生 stack 路径转成 stream,UDP 使用协议原生 datagram 或直连转发。
macOS 和 Windows 把需要更高权限的设备、DNS 或路由操作放到 helper/service;普通 Core 通过受控 IPC 取得设备或会话。helper 不负责用户配置和路由决策。
Linux eBPF
Linux eBPF 后端把程序挂到 cgroup v2 的 socket hook(connect4、sendmsg4、recvmsg4、 getpeername4、sockops 等)上:记录 socket 与进程归属,并在连接发起时把候选流量重定向到 Core 的本地 Mixed 监听端口。进程、IPv4 网段、DNS 地址和 FINAL 组成的候选快照下推到 BPF map; 非候选流量在内核中按 FINAL 处理,候选流量进入用户态按完整规则顺序匹配。
不创建 TUN 或 veth 设备,也不修改路由表。Linux 当前由同一个特权 Core 进程持有 BPF 程序与清理 生命周期,不再启动第二个 sudo 子进程。
11. 观测与工具
Stats 和观测层收集:
- 活动/累计连接与关闭操作;
- 全局、入站和节点流量;
- 路由命中、热门目标和规则统计;
- 进程信息和 Enhanced Mode 归属;
- RSS/主机内存、日志和 Prometheus 文本指标。
Tools service 复用 Core 的节点、Dialer、SSH 和配置能力提供延迟、诊断、探针和部署。工具操作不在 数据面热路径中,但部署会修改本地/远端状态,具有独立的并发和安全限制。
12. 故障与回滚边界
| 故障 | 影响范围 | 处理方式 |
|---|---|---|
| TOML/规则引用无效 | Core 不完成初始化 | 先运行 --test-config |
| 单个代理不可达 | 该节点连接失败;组可按类型切换 | 健康检查、延迟测试、诊断 |
| Provider 刷新失败 | 保留此前可用内容 | 修复网络/缓存后重试 |
| mixed port 被占用 | 控制面仍可运行,自动/手动启动 listener 失败 | 换端口后 reload,必要时重启 |
| DNS port/helper 失败 | 独立 DNS 不可用;Enhanced 可能无法启动 | 检查权限、端口和 helper |
| 控制接口 bind/认证失败 | Core 应用启动失败或请求被拒绝 | 修正 controller/secret/网络边界 |
| TUN/eBPF 安装失败 | Enhanced 保持/回退为关闭 | 检查平台依赖与权限 |
配置和 Provider 安装尽量在完整解析后切换;系统网络资源则依赖正常停止进行恢复。对 TUN/eBPF 使用 SIGKILL 会绕过清理流程,应只作为进程无法响应时的最后手段。
14. 源码目录导航
| 目录 | 责任 |
|---|---|
src/app/ | CLI、平台角色、单实例和应用生命周期 |
src/core/ | CoreRuntime、公开 facade、服务编排和 DTO |
src/api/ | IPC/TCP server、认证和 MessagePack RPC service |
src/config/ | TOML、节点 parser、配置校验和 Geo 数据管理 |
src/route/ | 规则 matcher、节点 registry、策略组、Provider registry 和选择 |
src/inbound/ | Mixed listener、HTTP 和 SOCKS5 入站 |
src/proxy/, src/outbound/, src/dialer/, src/transport/ | 节点实例、协议、连接链、TLS 和 transport |
src/dns/ | 本地 resolver、缓存、Fake-IP 和系统 DNS 租约 |
src/provider/ | Proxy/Rule Provider 下载、解析和刷新 |
src/tun/ | TUN/eBPF、TCP/UDP pipeline、datagram 和 helper |
src/stats/ | 连接、流量、路由和进程观测 |
src/tool/ | 诊断、SSH、部署和配置订阅支持 |
目录结构是维护者导航,不是 ABI。外部集成应使用 MessagePack RPC 控制接口和 CLI,不要链接内部 C++ 类。