Skip to content

Specola Core Architecture

本文从使用者和集成者视角说明当前 specola-core 的组件边界、启动顺序、控制面和数据面。它不是 类级 API 文档,也不把内部 C++ 类型作为稳定公共接口;稳定集成边界是 TOML、CLI 和 MessagePack RPC 控制接口。

1. 总体结构

Core 把功能分成四层:

  1. 进程与配置:CLI、单实例、TOML 加载和生命周期;
  2. 控制面:MessagePack RPC、状态、配置和运维操作;
  3. 决策面:规则、策略组、Provider、DNS 映射;
  4. 数据面: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+CSIGTERM 或父进程退出后,应用先停止控制 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 构建运行时对象:

text
主 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.portgeneral.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 负责建立实际连接:

text
Route result

   ├─ DIRECT / REJECT
   └─ ProxyInstance
        ├─ optional dialer-proxy multi-hop chain
        ├─ protocol handshake
        ├─ TLS / Reality
        └─ raw / HTTP / WebSocket / HTTP Upgrade / gRPC transport

TCP 与 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(connect4sendmsg4recvmsg4getpeername4sockops 等)上:记录 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++ 类。