thurgio
thurgio 是一个代理配置管理工具——一个现代化的、基于 Rust 构建的 surgio 替代品。它可以聚合多个代理源,应用过滤和转换规则,并将结果渲染为 Clash、Surge、sing-box、V2RayN、SIP002 及其他代理客户端格式的输出文件。
主要特性
- 多源聚合 — 从 Clash、Clash.Meta、V2RayN、SIP002、Surge、QX、Loon、SIP008、SSD、Telegram 或本地文件获取代理(
ruleset.yaml远程规则集自动物化)。 - 协议支持 — 25 种代理协议(SS、SSR、VMess、VLESS、Trojan、Hysteria(2)、TUIC、WireGuard、SOCKS5、HTTP、Snell、AnyTLS、ShadowTLS、Juicity、Naive、Ssh、Mieru 等)。
- 过滤与重命名 — 关键字(前缀/后缀/包含/列表)、正则和代理类型过滤器 +
filter/rename模板过滤器(_rules上下文)+ruleset命名规则集;支持正则反向引用的重命名规则。 - 表情模式 — 根据检测到的国家/地区自动添加旗帜表情前缀,可全局或按订阅源配置(启用/禁用/去除)。
- 模板引擎 — Tera 模板(Jinja2/Django 语法),30+ 内置过滤器(
clash/surge/qx/loon/mellow/mixed/sibox/stash/clashr/js/sort_nodes/clash_groups/surge_groups/ruleset等)+get_env/now自定义函数;tpl=auto按 User-Agent 自适应输出。 - 热重载 — 配置文件(
thurgio.yaml+ 5 个 sibling)和模板目录变更时自动重新构建;ruleset远程 URL 条目更新管线自动拉取。 - HTTP API — 内置 Axum 服务器,支持按作用域(Read/Admin)划分的令牌认证、provider/snippet 渲染(
tpl=auto)、订阅商店/健康/流量元数据/延迟/UDP/GEO/解锁探测、SSE 构建通知和 Prometheus 指标。 - 安全性 — 作用域令牌、可选的 TLS、IP 速率限制、并发限制、CORS 配置(热重载)。
- 自包含 — 单一二进制文件,使用嵌入式 SQLite 数据库持久化(无需外部数据库服务器,WAL 多进程共享)。
快速开始
cargo install thurgio
thurgio init
thurgio build
详细说明请参阅快速开始指南。
链接
- 源代码: github.com/thurgio/thurgio
- Telegram: t.me/thurgio
- 发布版本: github.com/thurgio/thurgio/releases
- crates.io: crates.io/crates/thurgio
致谢
快速开始
从零开始运行 Thurgio 的教程。
前提条件
- 选项 A — 预编译二进制:从 GitHub Releases 页面下载适用于你平台的最新版本。
- 选项 B — 从源码构建:Rust 1.85+(2024 edition)。
安装
从源码安装(cargo)
从本地仓库安装(开发构建):
cargo install --path crates/thurgio-cli
或从 crates.io 安装最新发布版本:
cargo install thurgio
两种命令都会编译 thurgio 二进制文件并将其放置到 ~/.cargo/bin/。
从预编译二进制安装
- 从 Releases 页面下载适用于你操作系统/架构的压缩包。
- 解压并将
thurgio二进制文件放到$PATH中的某个目录(例如/usr/local/bin)。
验证
thurgio --version
快速初始化
使用一条命令生成最小可用配置:
thurgio init
这会在当前目录创建以下文件:
thurgio.yaml— 主配置(setting)provider.yaml/filter.yaml/rename.yaml/params.yaml/snippet.yaml— 各配置节(均可留空,按需填充)tpls/base.tpl与tpls/ui.tpl— 示例模板与 UI 模板
也可以指定目标目录或强制覆盖:
thurgio init --dir /path/to/dir
thurgio init --force # 覆盖已存在的文件
验证生成的配置:
thurgio check
你会看到配置是否有效、提供者/片段的数量、令牌总数、监听地址/端口、TTL,以及每个提供者来源(URL 格式、本地文件是否存在)和输出目录的检查结果。
你的第一个配置文件
创建工作目录和一个最小化的 thurgio.yaml:
mkdir ~/my-thurgio && cd ~/my-thurgio
# thurgio.yaml
setting:
tokens:
admin:
- "my-secret-token"
output:
provider:
"proxies.tpl":
- "output/proxies.yaml"
# provider.yaml
my_provider:
name: my_provider
type: clash
source: "https://example.com/proxy-config"
template: proxies.tpl
filename:
- output/proxies.yaml
该配置:
- 从 Clash 兼容的 URL 加载代理数据。
- 使用模板
tpls/proxies.tpl渲染结果。 - 将最终输出写入
output/proxies.yaml。
注意:
setting使用tokens配置(按作用域分组,至少配置一个)。旧的单值token字段已移除。
创建模板
mkdir tpls
{# tpls/proxies.tpl #}
# Generated by Thurgio — {{ now() | date(format="%Y-%m-%d %H:%M:%S") }}
proxies:
{%- for proxy in l.proxies %}
- {{ proxy | json_str(pretty=false) }}
{%- endfor %}
模板上下文提供以下变量:
l.proxies— provider 的代理对象列表。l.name— provider 名称。content— 片段内容(仅在 snippet 模板中)。p.*— 自定义参数(参见配置指南)。config— 完整配置对象(config.snippet、config.params等)。headers— 请求头映射(HTTP API 渲染时)。version— 版本字符串。now()— 当前时间戳(Tera 自定义函数)。get_env(name="VAR", default="...")— 读取环境变量(Tera 自定义函数)。
可用过滤器和模板变量的完整列表请参阅配置指南。
构建
获取订阅源数据并渲染产物:
thurgio build
成功后,输出文件 output/proxies.yaml 将包含渲染后的代理配置。
Thurgio 会自动在工作目录中创建 SQLite 数据库(thurgio.db,可用 DATABASE_URL 环境变量覆盖路径)来缓存订阅源响应。无需外部数据库服务器。
强制更新所有订阅源(忽略缓存状态):
thurgio build --force
构建完成时发送系统通知(Linux 桌面,依赖 notify-send):
thurgio build --notify
监听模式
当源文件变更时自动重新构建:
thurgio build --watch
进程将持续运行,在当前目录(递归)发生任何修改时触发新的构建。按 Ctrl+C 停止。
启动服务
启动 HTTP 服务器按需提供配置:
thurgio serve
服务器默认监听 0.0.0.0:10100。所有需要认证的端点位于 /app/ 命名空间下,令牌通过 ?token= 查询参数或 Authorization: Bearer 请求头传递。
测试 API
# 健康检查
curl http://localhost:10100/health
# 获取渲染后的 provider 配置(需要令牌)
curl "http://localhost:10100/app/read/l?name=my_provider&token=my-secret-token"
# 通过 Server-Sent Events 实时获取构建通知
curl -N "http://localhost:10100/app/read/events?token=my-secret-token"
SSE 在每次构建完成时推送
build_complete事件,支持实时更新的仪表盘或代理配置。
使用自定义设置启动服务
# 自定义地址和端口
thurgio serve --address 127.0.0.1 --port 8080
# 开发模式(每次请求重新加载模板)
thurgio serve --dev
# 空闲模式(30 秒无请求后自动退出,配合 systemd Restart=always 按需拉起)
thurgio serve --idle
# 空闲时清空数据缓存(默认开启;可显式关闭:--evict=false)
thurgio serve --evict
# 启用 TLS(同时提供明文 HTTP 时需指定不同端口)
thurgio serve --tls-port 8443 --tls-cert cert.pem --tls-cert-key key.pem
# 仅提供 HTTPS
thurgio serve --tls-only --tls-port 8443 --tls-cert cert.pem --tls-cert-key key.pem
# 启动时打开浏览器
thurgio serve --open
检查更新
thurgio upgrade
查询 GitHub Releases 检查是否有新版本(仅提示,不会自动安装)。
Shell 自动补全
为 bash、zsh 或 fish 生成 shell 自动补全脚本:
# Bash
thurgio generate --shell bash > /etc/bash_completion.d/thurgio
# Zsh
thurgio generate --shell zsh > /usr/local/share/zsh/site-functions/_thurgio
# Fish
thurgio generate --shell fish > ~/.config/fish/completions/thurgio.fish
也可以使用 -o/--output 直接写入文件。
下一步
- 配置指南 — 添加多个订阅源、过滤器、表情模式和 TLS 配置。
- 部署指南 — systemd 服务、Docker、反向代理和生产环境检查清单。
- API 参考 — 完整的 HTTP API 端点参考。
- 配置参考 — 所有配置节的字段说明。
架构
概述
Thurgio 组织为 23 个 crate(thurgio-bench 为仅开发用的基准 crate,fuzz 为 workspace 成员,共 23 crates/* + fuzz),分布在 5 层中,具有严格的依赖方向。上层依赖于下层的抽象;下层从不依赖上层。
关于详细的接口合约和 ADR,请参阅 design/architecture-detailed.md 和 design/technical-decision.md。
接口层 (thurgio-cli, thurgio-server)
↓ (仅通过 app facade)
应用层 (thurgio-app, thurgio-app-update, thurgio-app-build, thurgio-app-state, thurgio-event)
↓
数据访问层 (thurgio-db)
↓
领域层 (thurgio-domain, thurgio-proxy, thurgio-proxy-singbox, thurgio-formats,
thurgio-filter, thurgio-renderer, thurgio-renderer-api, thurgio-script,
thurgio-config, thurgio-config-api)
↓
基础设施层 (thurgio-types, thurgio-infra, thurgio-request, thurgio-test-utils)
基础设施层
thurgio-types— 基础类型(Ar<T>、Rw<T>、Fields、ThurgioError+ErrorKind、Udp、UpdateState)与包身份标识(identity模块:NAME、VERSION、GIT_BUILD、get_version(),由build.rs嵌入git describe快照)。thurgio-infra— 日志初始化(init_logger)、请求追踪(request_tracing)、HTTP 客户端工厂(create_client)、熔断器(CircuitBreaker/CircuitBreakerPool)、指标抽象(MetricsRecorder/NoopMetrics)、set_root_dir工作目录管理。thurgio-request— HTTP 响应包装(Res)、来源抽象(Source:File/Url)、AppHttpClienttrait。thurgio-event— 进程内部事件总线(EventBus、DomainEvent、EventHandler、Envelope)。使用tokio::sync::broadcast,慢订阅者会丢失事件。thurgio-test-utils— 集成测试工具(测试应用工厂、测试配置构建器)。
领域层
thurgio-domain— 核心业务类型:Provider/RawProvider、Snippet、Output/RawOutput(provider 具名输出),以及更新生命周期(UpdateState、Cache/Lifecycle/Parsabletrait,proxies_version版本化 +cached_json缓存)。thurgio-proxy— 25 种代理协议类型(SS、VMess、VLESS、Trojan、Hysteria(2)、TUIC、AnyTLS、ShadowTLS、Juicity、Naive、Ssh、Mieru 等),支持解析(ConfType:clash/meta/v2rayn/sip002/surge/qx/loon/singbox/sip008/ssd/telegram)和序列化;ProxyFormat<F>trait(markerV2rayN/Sip002/Surge/Qx/Loon)提供格式解析分发,formats.rs能力表为单一事实源。全部协议无条件编译。thurgio-formats— 共享行解析辅助(Surge/QX/Loon 行解析抽离,供 proxy 与 renderer 复用)。thurgio-proxy-singbox— Sing-box 特定的代理类型转换(CoreSingBox、ToSingBoxtrait,SProxyTy::supports单一能力源)。thurgio-filter— 代理过滤(Filter/RawFilter/Mode)、重命名(Rename)、emoji 模式(EmojiMap,aho-corasick)与规则集(RuleSet/RuleFormat,assets/rulesets/*.list内置表)。thurgio-script— 内联 JS 引擎(ScriptEngine::transform,boa_engine,js过滤器薄包装)。thurgio-renderer-api— 轻量级 API 合约:RenderTarget/Rendertrait +context::base_context/insert单一装配入口。零 Tera 依赖。thurgio-renderer— Tera 模板渲染:30+ 内置过滤器(general+proxy/qx/qx_config/loon/loon_config/mellow/mixed/stash/surfboard/clashr/singbox+rules/ruleset/sort_nodes/groups/js)+get_env/now;渲染线程池(Competing Consumers,EMA 平滑 backlog + 滞回控制 + FreeList,min=1 / max=64)。tpl=auto在 serverua.rs完成。thurgio-config— 配置加载、校验(模板存在性、来源 URL 格式、Setting::validate)与热重载(notify文件监听 +ArcSwap<T>无锁切换)。拆分读取provider.yaml/params.yaml/rename.yaml/filter.yaml/snippet.yaml/ruleset.yaml(后者支持http(s)://远程 URL 条目,更新管线物化)。thurgio-config-api— 轻量纯配置类型(Setting、Ttl、TokenScope、Tokens、EmojiMode、AdhocTplSetting),零领域/代理/过滤依赖。
数据访问层
thurgio-db— 基于 SQLite(sqlx,WAL 模式)的嵌入式持久化:KvStore(键值存储)、EntityStore/SqliteEntityStore(类型化实体存储)、Stored(领域对象 ↔ 存储模型关联)。
应用层
thurgio-app-state— 中央应用状态AppState:持有 SQLite 数据库连接、HTTP 客户端、渲染器、事件总线、熔断器池、构建/更新双锁(build_lock+update_lock,ADR-055)、data_generation(缓存降级用)、AdhocStore(临时资源内存+DB 缓存)与 dev/idle/evict 标志;render_context(build_render_context/build_base_context,ADR-057)为渲染装配单一入口。AppConfig为Ar<Config>新类型包装。thurgio-app— 外观(facade,分组 re-export:update/render/adhoc/config/admin,ADR-046):initialize三步初始化(Source → AppState → 熔断器恢复 → 事件处理器注册)。接口层 crate 仅经分组路径访问下层。thurgio-app-update— 更新管道编排(refresh_and_record统一入口,ADR-013;UpdateItemsStage<T: Stored>无界 fan-out,ADR-073;ensure_ready/refresh_in_background/refresh_and_wait_for分流,ADR-049/072;record私有ItemKind分发,ADR-032)。thurgio-app-build— 构建管道编排(build:update+record → 渲染 → 原子发布StagingDir,ADR-047)、渲染管道(RenderItemsStage,provider 主+具名输出平铺)、事件处理器(AutoBuildHandler/LoggingHandler/WebhookHandler/TelegramHandler)、模板目录监听(init_template_watcher)、并发任务工具(task::run_concurrent/try_join)。
接口层
thurgio-cli— CLI 二进制入口点(build/serve/init/check/diff nodes|file/config backup/restore/list/upgrade/generate/version,spawn_config_watcher统一监听thurgio.yaml+ 6 siblings +tpls/)。依赖thurgio-app外观 +thurgio-proxy::diff(节点级 diff)。thurgio-server— 基于 Axum 的 HTTP 服务器(build_router导出,12 read + 10 admin 端点,tpl=autoUA 自适应,SSE 事件流,限流/CORS/指标/认证中间件,静态资源include_str!嵌入)。
统一错误类型
ThurgioError(位于 thurgio-types)使用 thiserror 派生枚举,配套 ErrorKind 分类(Copy,用于状态码映射与重试决策):
ErrorKind | 含义 | HTTP | 可重试 |
|---|---|---|---|
Network | 网络请求失败(reqwest::Error) | 502 | ✅ |
Timeout | 请求超时 | 502 | ✅ |
Persistence | 数据库错误 | 500 | ❌ |
Parse | 数据解析失败 | 422 | ❌ |
Config | 配置错误 | 422 | ❌ |
Render | 模板渲染失败 | 500 | ❌ |
NotFound | 资源未找到 | 404 | ❌ |
Validation | 字段验证失败 | 422 | ❌ |
Unauthorized | 认证失败 | 401 | ❌ |
CircuitOpen | 熔断器打开 | 503 | ❌ |
Canceled | 操作取消 | 499 | ❌ |
Internal | 内部错误(anyhow::Error) | 500 | ❌ |
trace_id 通过外部包装 TracedError 携带,不嵌入 ThurgioError。详见 design/error-handling.md。
事件系统
事件总线实现解耦通信:
- 7 种事件:
RebuildRequested、ProviderUpdated、SnippetUpdated、BuildCompleted、RendererReloaded、ConfigReloaded(配置全量重解析并原子替换,ADR-048)、ServerEvent - 3 个默认订阅者:
AutoBuildHandler(RebuildRequested → 强制重建,仅重跑更新+渲染管线、不重载配置)、LoggingHandler(所有事件 → INFO/WARN)、WebhookHandler(BuildCompleted → 向THURGIO_WEBHOOK_URLPOST JSON,未设置时为空操作)
管道抽象
更新与渲染管道由阶段(stage)构成:
- 更新管道(
thurgio-app-update):UpdateItemsStage(providers)→UpdateItemsStage(snippets) - 渲染管道(
thurgio-app-build):providers(主输出 + 具名输出)与 snippets 两个阶段并发执行(共享不可变上下文,无共享可变状态)
特性门控
thurgio-proxy 不再按协议门控(25 种协议全部无条件编译),full/filters 拆分已移除(ADR-064),thurgio-domain 的 update/render-impl/db-impl 门控亦移除:
| 特性 | 说明 |
|---|---|
test-utils | 测试示例构造器(开发/测试用,仅 thurgio-proxy/thurgio-request 定义,thurgio-test-utils 消费 16 例) |
thurgio-cli 另有两个互斥的 TLS 后端特性:rustls-tls(默认)与 native-tls,通过 thurgio-app → thurgio-infra 转发到 reqwest。
依赖关系图
thurgio-cli ──> thurgio-app (facade)
├── thurgio-app-state ──> thurgio-config ──> thurgio-config-api
│ ├── thurgio-domain ──> thurgio-filter / thurgio-proxy / thurgio-proxy-singbox
│ ├── thurgio-renderer ──> thurgio-renderer-api
│ ├── thurgio-db / thurgio-request / thurgio-infra / thurgio-types
├── thurgio-app-update ──> thurgio-db / thurgio-infra / thurgio-request
├── thurgio-app-build ──> thurgio-renderer / thurgio-event
└── thurgio-event
thurgio-server ──> thurgio-app (facade)
关键 Trait
| Trait | Crate | 用途 |
|---|---|---|
Stored, EntityStore, SqliteEntityStore | thurgio-db | 存储模型关联 / 类型化实体存取 |
KvStore | thurgio-db | 键值持久化(SQLite) |
Proxy, ConfType, ProxyFormat | thurgio-proxy | 代理类型定义与格式解析分发(marker V2rayN/Sip002/Surge) |
CoreSingBox, ToSingBox | thurgio-proxy-singbox | Sing-box 转换 |
EventHandler, EventBus | thurgio-event | 事件订阅与分发 |
Render, RenderTarget | thurgio-renderer-api | 渲染数据合约(无 Tera 依赖) |
Renderable, Render, AppRenderEngine | thurgio-renderer | 模板渲染执行 |
设计原则
- 单一职责 — 每个 crate 有一个清晰的用途。
- 依赖反转 — 上层依赖于抽象。
- 关注点分离 — 各层干净分离。
- 外观模式 — 接口层 crate 通过
thurgio-app的重新导出访问下层。
配置指南
Thurgio 常见用例的实际配置模式。
最小工作配置
每个 Thurgio 设置至少需要一个 provider(提供者),在 provider.yaml 中定义获取、处理与渲染所需的一切:
# thurgio.yaml
setting:
tokens:
admin:
- "your-api-token" # 至少配置一个令牌
output:
provider:
"clash_list.tpl":
- "output/proxies.yaml" # 默认输出路径(按模板名映射,详见配置参考)
# provider.yaml
example:
name: example
type: clash
source: "https://example.com/clash-config"
template: clash_list.tpl
filename:
- output/proxies.yaml
管道流程为:Provider → 获取 → 解析代理 → pre_rename → filter → rename → 使用模板渲染 → 写入输出。
添加多个提供者
你可以从多个来源聚合代理:
# provider.yaml
vps_provider:
name: vps_provider
type: clash
source: "https://example.com/clash-config"
template: clash.tpl
filename:
- output/vps.yaml
rename:
- "add_vps_prefix"
free_provider:
name: free_provider
type: sip002
source: "https://example.com/sip002.txt"
template: sip002.tpl
filename:
- output/free.txt
local_backup:
name: local_backup
type: v2rayn
source: "backup/v2rayn.json"
template: v2rayn.tpl
filename:
- output/backup.txt
支持的提供者类型(provider.type):
| 类型 | 描述 |
|---|---|
clash | Clash YAML 格式 |
meta | Clash.Meta YAML 格式 |
v2rayn | V2RayN 分享链接格式(Base64 JSON 数组) |
sip002 | SIP002 URI 格式(逐行 ss:// 等) |
sip008 | SIP008 JSON 订阅格式(Shadowsocks Android,version+servers) |
surge | Surge 配置格式(解析 [Proxy] 段) |
qx | Quantumult X 节点行(shadowsocks=/vmess= 等 7 种) |
loon | Loon 节点行(10 种) |
ssd | SSD 订阅格式(ssd:// Base64 JSON 文档) |
singbox | sing-box 配置 JSON(解析 outbounds 数组,基础字段,含 wireguard/naive/ssh) |
telegram | TG 类 HTTP/SOCKS5 代理链接(tg://http / tg://socks / t.me/* → Http/Socks5) |
auto | 仅渲染侧 tpl=auto 的辅助值(provider type 仍需显式指定真实输入格式;auto 由服务端 UA 探测选模板) |
每个 provider 独立渲染自己的输出;同一个提供者可以通过 outputs 定义多个具名输出,用不同模板渲染同一份代理列表:
# provider.yaml
all_proxies:
name: all_proxies
type: clash
source: "https://example.com/clash-config"
template: clash.tpl
filename:
- output/clash.yaml
rename:
- "clean_names"
outputs:
v2rayn:
template: v2rayn.tpl
filename:
- output/v2rayn.txt
过滤代理
过滤规则按名称保留或剔除代理。在 filter.yaml 中定义过滤规则(声明式语法,详见配置参考):
filter:
# 简写:包含任一关键字的代理(忽略大小写)
only_uk_us: "英国|美国|UK|US|London|New York"
# 仅保留 ss 类型
ss_only:
- type: ss
# 正则
hk:
- regex: '\W+(US|us|United States)\W+'
在提供者中引用过滤规则:
# provider.yaml
my_list:
name: my_list
type: clash
source: "https://example.com/proxy-config"
template: clash.tpl
filter:
- "only_uk_us"
- "ss_only" # 多个过滤规则依次应用
匹配器
| 匹配器 | 值 | 匹配条件… |
|---|---|---|
contain | 字符串或字符串数组 | 名称包含任一关键字(数组 = OR;默认忽略大小写) |
start | 字符串 | 名称以关键字开头 |
end | 字符串 | 名称以关键字结尾 |
type | 字符串 | 代理类型(ss、vmess 等) |
regex | 字符串 | 代理名称匹配正则表达式 |
共享选项:ignore_case(缺省 true)、negate(缺省 false,命中则剔除)。列表(AND)仅支持 contain/start/end。
重命名代理
重命名规则使用正则替换(支持反向引用)清理或标准化代理名称:
rename:
add_vps_prefix:
- "^(.*)$"
- "VPS-$1"
clean_names:
- "(美国)"
- "🇺🇸 US"
- "(日本)"
- "🇯🇵 JP"
- "带宽.*"
- "Standard"
room_number:
- "(\\d+)-(\\d+)"
- "$1-$2" # 支持正则反向引用
在提供者中应用(pre_rename 在过滤前、rename 在过滤后):
# provider.yaml
my_provider:
name: my_provider
type: clash
source: "https://..."
template: clash.tpl
pre_rename: # 在过滤前应用(解析后立即执行)
- "add_vps_prefix"
rename: # 在过滤后应用
- "clean_names"
- "room_number"
每条规则是 [正则, 替换串] 列表,对代理名称执行 replace_all。
使用 Emoji 模式
Emoji 模式根据检测到的国家/地区自动在代理名称前添加旗帜 emoji。
全局设置
setting:
mode: Enable # 全局启用 emoji(默认值)
其他可选值:
| 值 | 效果 |
|---|---|
Disable | 不添加 emoji 前缀 |
Enable | 检测到国家/地区时添加旗帜 emoji 前缀(默认) |
Hide | 去除代理名称中已有的 emoji 前缀 |
按提供者覆盖
为特定提供者覆盖全局设置:
# provider.yaml
my_provider:
name: my_provider
type: clash
source: "https://..."
template: clash.tpl
emoji: false # 仅对此提供者禁用 emoji(默认 true)
模板示例
模板使用 Tera 引擎(语法类似 Jinja2/Django)。
上下文变量
| 变量 | 可用范围 | 描述 |
|---|---|---|
l.proxies | provider、具名输出 | 代理对象列表 |
l.name | provider、具名输出 | 当前 provider 的名称 |
l.rename | provider、具名输出 | 当前 provider 的重命名规则(键名) |
l.filter | provider、具名输出 | 当前 provider 的过滤规则(键名) |
s.response.content | snippet | 当前 snippet 的源内容(原样文本);adhoc snippet 绑定为 content |
p.* | 全部 | 来自配置的自定义参数(params.yaml + /app/admin/p 热更新) |
hp.* | 全部 | 本次 HTTP 请求的查询参数(与 p 分离;无请求参数时为 {}) |
config | 全部 | 完整配置对象(config.snippet、config.params、config.setting 等) |
headers | 全部 | 请求头映射(HTTP API 渲染时) |
version | 全部 | 版本字符串 |
now() | 全部 | 当前时间戳(Tera 函数) |
get_env(name=..., default=...) | 全部 | 读取环境变量(Tera 函数) |
自定义过滤器(30+,register_all 统一注册)
| 过滤器 | 描述 |
|---|---|
slugify(separator="-") | 字符串 slug 化 |
urlencode() | URL 编码 |
indent(width=2, first=false, blank=false) | 缩进内容 |
json() / yaml() | 将字符串解析为 JSON/YAML 值 |
json_str(pretty=true) | 将代理序列化为紧凑或美观的 JSON |
yaml_str() | 将代理列表序列化为 YAML |
to_yaml_vec() | 逐行转为 YAML 列表 |
clear(num=3, n=2) | 压缩连续空行 |
uniq() | 按行去重 |
base64(mode="encode", config=...) | Base64 编码/解码 |
clash(symbol=..., policy=...) | 格式化为 Clash 代理条目(单条规则文本输入) |
cla(meta=true) | Clash 代理条目(数组输入,V2rayN/Sip002 已按能力谓词过滤) |
surge(symbol=..., policy=...) | 格式化为 Surge 代理条目(单条规则文本输入) |
surge_proxy() | 代理数组 → Surge [Proxy] 段行(ss/trojan/vmess/snell/http/socks5/wireguard 8 种) |
qx() | 代理数组 → Quantumult X 节点行(ss/ssr/vmess/vless/trojan/http/socks5 7 种) |
qx_config(check_url=, dns=, group=) | 代理数组 → 完整 Quantumult X 配置四段([general]/[dns]/[server_local]/[policy]) |
loon() | 代理数组 → Loon 节点行(10 种) |
loon_config(dns=, group=) | 代理数组 → 完整 Loon 配置四段([General]/[Proxy]/[Proxy Group]/[Rule]) |
mellow(port=1080) | 代理数组 → Mellow/V2Ray-core JSON(socks inbound + per-proxy outbounds,ss/vmess/vless/trojan/http/socks5 + TCP/WS/TLS) |
mixed() | 代理数组 → 混合单链接列表(V2RayN 优先、SIP002 回退逐节点分发) |
stash() | 代理数组 → Stash(Clash 兼容)节点行 |
surfboard() | 代理数组 → Surfboard(Surge 兼容)节点行 |
clashr() | 代理数组 → ClashR 节点行 |
ssd() | 代理数组 → ssd:// 订阅(仅 ss) |
sip008() | 代理数组 → SIP008 JSON 订阅(仅 ss) |
shadowrocket() | 代理数组 → Shadowrocket 分享链接(复用 sip002/v2rayn,wg 跳过) |
quantumult(symbol=..., policy=...) | 格式化为 Quantumult X 规则集(host/host-suffix/host-keyword/ip-cidr/ip6-cidr/geoip/user-agent/final;MATCH→final) |
singbox(pretty=true, policy=...) | 格式化为 sing-box 代理条目(单条规则文本输入) |
sibox() / singbox() | 代理数组 → sing-box outbound 对象数组(12 变体,TryFrom<ProxyTy> 单向) |
clashbox(premium=..., meta=...) | clash 规则文本 → sing-box rule 对象数组 |
v2rayn() | 代理数组 → V2RayN 分享链接(V2RAYN_CAPABILITIES 5 种仅元类型) |
sip002() | 代理数组 → SIP002 URL(除 WireGuard 外全部) |
proxy_suffix(suffix=...) | 在代理名称后添加协议后缀 |
js(src="...") | 代理数组 → 内联 JS 变换($input 全局,boa_engine,须显式 return) |
sort_nodes(by=name/region/latency, order=asc/desc, latencies=...) | 代理数组 → 按 name/region/latency 排序 |
clash_groups / surge_groups | 代理数组 → Clash/Surge 策略组(select + 按地区 url-test) |
ruleset(name=, format=clash/surge, text=) | 命名规则集格式化(内联 text > _rules.ruleset > 内置 assets/rulesets/*.list) |
no_resolve(symbol=..., value=...) | 标记带 no-resolve 的代理 |
sort_domain_ip(symbol=...) | 按域名/IP 排序代理规则 |
rule(symbol=..., policy=...) | 应用路由规则 |
fil() | 应用额外的过滤(clash/surge/cla/v2rayn/sip002) |
filter(rules=...) | 按配置的过滤规则动态过滤代理(rules 为 filter.yaml 中的规则名,逗号分隔或数组,_rules.filter 表) |
rename(rules=...) | 按配置的重命名规则动态改名(rules 为 rename.yaml 中的规则名,_rules.rename 表) |
示例:带测试部分的 Clash 输出
{# tpls/clash.tpl #}
{%- set format = "json" -%}
{%- set pretty = false -%}
# {{ l.name }}
# 生成时间 {{ now() | date(format="%Y-%m-%d %H:%M:%S") }}
test: &test
url: {{ p.test.url }}
interval: {{ p.test.interval }}
proxies:
{% filter indent(width=2) %}{% if format == "json" %}
{%- for i in l.proxies %}
- {{ i | json_str(pretty=pretty) }}
{%- endfor %}{% elif format == "yaml" %}
{{ l.proxies | yaml_str() }}{% endif %}{% endfilter %}
示例:V2RayN 输出
{# tpls/v2rayn.tpl #}
{{ l.proxies | v2rayn() }}
示例:模板内动态应用过滤/重命名规则
模板可在 l.proxies 基础上按需应用 filter.yaml / rename.yaml 中的规则,多个过滤器可链式组合(顺序即应用顺序):
{# 只保留美国节点,去掉名称中的 "US-" 前缀,再输出 V2RayN 格式 #}
{{ l.proxies | filter(rules="us") | rename(rules="us_prefix") | v2rayn() }}
rules 参数接受逗号分隔的规则名字符串或名字数组;未定义的规则名会被静默忽略(与配置解析行为一致)。
示例:SIP002 输出
{# tpls/sip002.tpl #}
{{ l.proxies | sip002() }}
使用自定义参数
在配置中定义参数:
params:
test:
url: "http://www.gstatic.com/generate_204"
interval: 600
region: "asia"
然后在模板中使用:
test-url: {{ p.test.url }}
test-interval: {{ p.test.interval }}
region: {{ p.region }}
使用 config.snippet
片段可以为模板提供额外数据:
snippet:
ruleset:
source:
url: "https://example.com/rules.yaml"
p:
clash_url: "https://example.com/clash-rules.yaml"
在模板中:
{%- for k, v in config.snippet %}
rule-provider {{ k }}:
url: {{ v.p.clash_url }}
{%- endfor %}
TLS 设置
通过 TLS 提供 HTTPS 代理服务。
使用 CLI 标志
thurgio serve \
--tls-only \ # 仅提供 HTTPS(无明文 HTTP)
--tls-port 8443 \ # TLS 端口
--tls-cert /etc/ssl/cert.pem \ # 证书文件(-c)
--tls-cert-key /etc/ssl/key.pem # 私钥文件(-k)
TLS + 明文在不同端口上
thurgio serve \
--port 10100 \ # 明文 HTTP
--tls-port 8443 \ # 同时提供 HTTPS
--tls-cert /etc/ssl/cert.pem \
--tls-cert-key /etc/ssl/key.pem
注意:
--tls-port默认等于--port。同时提供明文与 HTTPS 时必须显式指定不同的 TLS 端口,否则两个服务会绑定同一地址导致启动失败。
使用环境变量
export TLS_CERT=/etc/ssl/cert.pem
export TLS_CERT_KEY=/etc/ssl/key.pem
export TLS_PORT=8443
export TLS_ONLY=true
thurgio serve
拆分配置为多个文件
Thurgio 解析 thurgio.yaml 后,会自动读取配置文件所在目录下的配套 YAML 文件并合并(模板目录同样相对该目录解析,ADR-066):
| 文件 | 配置段 | 缺失时 |
|---|---|---|
provider.yaml | provider | 合并空 |
params.yaml | params | 合并空 |
rename.yaml | rename | 报错(必须存在空或有内容) |
filter.yaml | filter | 报错 |
snippet.yaml | snippet | 合并空 |
ruleset.yaml | ruleset | 合并空(唯一缺失合法的 sibling,ADR-086):本地行列表或 http(s):// 远程 URL(更新管线物化进 _rules.ruleset) |
这对于组织大型配置很有用:
~/.config/thurgio/
├── thurgio.yaml # 主配置(setting)
├── provider.yaml # 所有提供者
├── filter.yaml # 所有过滤规则
├── rename.yaml # 所有重命名规则
├── snippet.yaml # 所有片段
└── tpls/ # 模板目录(THURGIO_TPL_DIR,默认 "tpls")
├── clash.tpl
├── surge.tpl
└── v2rayn.tpl
所有被引用的模板文件必须在模板目录中存在,否则配置解析失败。
命名规则集(ruleset.yaml)
可选 sibling 文件 ruleset.yaml 按名称定义命名规则集(本地行列表或 http(s):// 远程 URL),生成 _rules.ruleset 上下文面供 ruleset 过滤器消费({{ l.proxies | ruleset(name="ads") }})。远程条目由更新管线定时拉取并物化,本地声明优先于同名远程。
# ruleset.yaml
ads:
- DOMAIN-SUFFIX,ads.example.com
- DOMAIN-KEYWORD,tracker
cnlist: https://example.com/cn.list
过滤器调用:{{ l.proxies | ruleset(name="ads", format="clash") }} / surge;内联 text 参数优先于命名表,命名表优先于内置 assets/rulesets/*.list。
备份与回滚
编辑配置前可以用 config backup 把当前声明文件(thurgio.yaml、5 个 sibling YAML 与 tpls/ 模板目录,清单与配置热重载的监听一致;ruleset.yaml 不在清单内)整体复制到备份目录:
thurgio config backup
# ✔ Backup created: <root>/backups/20260814-153000
thurgio config list
# Available backups in <root>/backups:
# 20260814-153000 2026-08-14 15:30:00
默认备份到 <root>/backups/<YYYYMMDD-HHMMSS>/,可用 -d/--dir 指定其他位置;只复制实际存在的文件。
配置改坏后,用 list 找到备份名并恢复:
thurgio config restore 20260814-153000
restore 接受备份名(backups/ 下的子目录名)或备份目录路径(绝对路径,或相对 root 的路径)。恢复前会把当前状态自动再备份一份到 backups/(保险),然后覆盖写回声明文件与 tpls/;仅恢复备份中包含的内容,不会删除 root 中的其他文件。
数据库
Thurgio 使用嵌入式 SQLite 数据库(WAL 模式)进行持久化,无需外部数据库服务器。数据库文件(默认 thurgio.db,可用 DATABASE_URL 环境变量覆盖;兼容 sqlite:// 前缀写法)会在首次使用时自动创建于工作目录。持久化以下数据:
- Provider 响应 — 缓存的响应,避免每次构建时重新获取。
- Snippet 数据 — 获取的片段内容。
- 熔断器快照 — 重启后保持打开状态的熔断器。
- KV 存储 — 通用键值数据(
kv_store表)。
环境变量参考
| 变量 | 覆盖项 | 默认值 |
|---|---|---|
ROOT | 工作根目录(-r/--root) | 当前目录 |
FORCE | build 强制更新(-f/--force) | false |
WATCH | build 监听模式(-w/--watch) | false |
NOTIFY | build 完成时发送系统通知(-N/--notify) | false |
SHELL | generate 补全目标 shell(-s/--shell) | bash |
OUTPUT | generate 输出文件(-o/--output) | stdout |
ADDRESS | serve 监听地址(-a/--address) | 配置 setting.address(默认 0.0.0.0) |
PORT | serve 监听端口(-p/--port) | 配置 setting.port(默认 10100) |
DEV | serve 开发模式(-d/--dev) | false |
IDLE | serve 空闲模式(-i/--idle):无请求时自动退出 | false |
EVICT | serve 空闲时清空数据缓存(-e/--evict) | true |
TLS_ONLY | serve 仅提供 HTTPS(-s/--tls-only) | false |
TLS_PORT | serve TLS 端口(-t/--tls-port) | 同 PORT |
TLS_CERT | TLS 证书路径(-c/--tls-cert) | 无 |
TLS_CERT_KEY | TLS 私钥路径(-k/--tls-cert-key) | 无 |
OPEN | serve 启动时打开浏览器(-O/--open) | false |
THURGIO_CONCURRENCY_LIMIT | 并发限制(渲染/持久化任务) | 10 |
THURGIO_TPL_DIR | 模板目录 | tpls |
THURGIO_RENDERER_RELOAD_TIMEOUT | 渲染器重载超时(秒) | 3 |
THURGIO_RENDERER_MIN_THREADS | 渲染线程池最小线程数 | 1 |
THURGIO_RENDERER_MAX_THREADS | 渲染线程池最大线程数 | 64 |
THURGIO_RENDERER_IDLE_TIMEOUT | 渲染线程空闲退出时间(秒) | 5 |
THURGIO_RENDERER_IDLE_COOLDOWN | 缩容冷却次数 | 3 |
THURGIO_RENDERER_SCALE_UP_THRESHOLD | 扩容阈值(平滑 backlog 倍数) | 2.0 |
THURGIO_RENDERER_SCALE_DOWN_THRESHOLD | 缩容阈值 | 0.5 |
THURGIO_RENDERER_EMA_ALPHA | backlog EMA 平滑系数 | 300 |
THURGIO_WEBHOOK_URL | 构建完成通知 webhook(POST JSON) | 无(禁用) |
EVICT_TIMEOUT | 空闲后清空数据缓存的等待时间(秒,--evict 开启时) | 10 |
IDLE_TIMEOUT | 空闲模式退出等待时间(秒,--idle 开启时) | 30 |
DATABASE_URL | 数据库文件路径 | thurgio.db |
REQUEST_TIMEOUT | HTTP 客户端请求超时(秒) | 9 |
THURGIO_MAX_CONCURRENT_REQUESTS | 服务器最大并发请求数 | 600 |
THURGIO_RATE_LIMIT_REQUESTS | 每个 IP 时间窗内最大请求数 | 600 |
THURGIO_RATE_LIMIT_WINDOW_SECONDS | 速率限制时间窗(秒) | 60 |
THURGIO_MAX_RATE_LIMIT_ENTRIES | 速率限制条目上限 | 10000 |
RUST_LOG | 日志级别过滤(如 info,thurgio=debug) | info |
RUST_LOG_ANSI | 是否启用日志颜色(true/false) | 自动检测(stdout 为 TTY) |
RUST_LOG_FORMAT | 日志格式(json 启用 JSON 输出) | 文本 |
RUST_INVALID_TLS | 跳过 TLS 证书校验(仅测试用) | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP 导出端点(otlp 特性,gRPC) | http://localhost:4317 |
模板示例
Thurgio 的核心扩展面是 Tera 模板:每个 provider / snippet 通过 template 字段指定一个模板,渲染上下文携带代理列表(l.proxies)、配置(config)、参数(p)等数据,配合内置过滤器即可产出任意格式的订阅(Clash / sing-box / V2RayN / Surge / 纯文本 / Base64…)。
本页索引 fixtures/examples/tpls/ 中的示例模板,并给出过滤器快速参考。按输出格式分类的精选索引(用途 / 依赖过滤器 / 适用客户端)见 fixtures/examples/README.md。模板语法详见配置指南。
目录结构
fixtures/examples/
├── README.md # 精选模板索引(按输出格式分类)
├── thurgio.yaml # 主配置(setting)
├── provider.yaml # provider 定义(模板引用在这里)
├── snippet.yaml # snippet 定义
├── filter.yaml # 过滤规则
├── rename.yaml # 改名规则
├── params.yaml # 渲染参数
├── tpls/ # 模板目录(THURGIO_TPL_DIR,默认 "tpls")
├── prov/ # 订阅源(本地文件)
├── rules/ # 规则集(snippet 源)
└── prod/ # build 输出
模板清单
以下模板全部默认接线:
provider.yaml的outputs与snippet.yaml条目已引用它们,thurgio build一次产出所有格式。
| 模板 | 适用 | 输出格式 | 说明 |
|---|---|---|---|
base.tpl | provider | Clash YAML | 最小模板(thurgio init 生成),json_str 逐节点输出 |
clash.tpl | provider | Clash YAML | 紧凑 JSON 节点 + test 块 + rule-providers(遍历 config.snippet 的 p.clash_url) |
clash_groups.tpl | provider | Clash YAML | 按 p.groups 规则名动态生成 proxy-groups(filter(rules=...) 实时筛选成员) |
clash_list.tpl | provider | Clash YAML | clash.tpl 变体:头部注释打印 l.rename / l.filter |
surge.tpl | provider | Surge 配置 | 手写 [Proxy] 段(for 循环 + 字段展开,教学向) |
surge_proxy.tpl | provider | Surge 配置 | surge_proxy 过滤器版 [Proxy] 段({{ l.proxies | surge_proxy() }}) |
surge_full.tpl | provider | Surge 完整配置 | [General] / [Proxy] / [Proxy Group] / [Rule] 四段齐全,可直接订阅导入(surge_proxy 节点行 + select/url-test 组 + p.rules 规则聚合,首行 #!MANAGED-CONFIG) |
v2rayn.tpl | provider | 纯文本 | 每行一个 vmess:// 分享链接(v2rayn 过滤器) |
sip002.tpl | provider | 纯文本 | 每行一个 ss:// 分享链接(sip002 过滤器) |
sip008.tpl | provider | JSON | SS Android 官方订阅:version + servers 数组(sip008 过滤器,仅 ss 节点) |
ssd.tpl | provider | 纯文本 | ssd:// 订阅:UrlSafe Base64 JSON(ssd 过滤器,仅 ss 节点) |
shadowrocket.tpl | provider | 纯文本 | 每行一个分享链接(shadowrocket 过滤器,复用 sip002/v2rayn 转换,wg 跳过) |
base64_sub.tpl | provider | Base64 | 机场式订阅:v2rayn + sip002 链接整体 base64(mode="encode") 编码 |
plain_list.tpl | provider | 纯文本 | 人类可读节点清单:名称 | 类型 | 服务器:端口(for 循环 + 字段访问) |
singbox.tpl | provider | sing-box JSON | 完整 sing-box 配置:sibox 转 outbounds + direct/block |
singbox_rules.tpl | snippet | sing-box JSON | clash 规则文本 → sing-box rule 数组(clashbox 过滤器),与 singbox.tpl 组合成完整配置 |
qx.tpl | provider | QX 节点行 | 每行一个 QX 节点(qx 过滤器,ss/ssr/vmess/vless/trojan/http/socks5) |
qx_config.tpl | provider | QX 完整配置 | 完整 Quantumult X 配置:[general]/[dns]/[server_local]/[policy](qx_config 过滤器) |
loon.tpl | provider | Loon 节点行 | 每行一个 Loon 节点(loon 过滤器,10 种类型) |
loon_config.tpl | provider | Loon 完整配置 | 完整 Loon 配置:[General]/[Proxy]/[Proxy Group]/[Rule](loon_config 过滤器) |
mellow.tpl | provider | Mellow JSON | Mellow/V2Ray-core JSON:socks inbound + per-proxy outbounds(mellow 过滤器,ss/vmess/vless/trojan/http/socks5 + TCP/WS/TLS) |
mixed.tpl | provider | 混合分享链接 | 混合单链接列表:逐节点 V2RayN 优先、SIP002 回退,mixed 过滤器 |
rules_aggregate.tpl | snippet | Quantumult X 规则集 | 多规则集聚合:拼接 config.snippet 中各规则集内容 → uniq 去重 → sort_domain_ip 排序 → quantumult 输出(s.p.rules 指定要聚合的 snippet 名,s.p.policy 指定策略);换 clash / surge 过滤器即得对应格式规则列表 |
snippet.tpl | snippet | 原样 | 原样输出 snippet 内容(s.response.content) |
rules_pipeline.tpl | provider | 教学 | 演示模板内 filter → rename → v2rayn/sip002/cla 过滤器链 |
date.tpl | 片段 | 文本 | {% include %} 引入的生成时间注释 |
ui.tpl | — | HTML | Dashboard 页面(服务器自带,勿删除) |
auto/*.tpl | provider | UA 自适应 | tpl=auto 时按 User-Agent 选择的模板(auto/cla.tpl/surge.tpl/qx.tpl/loon.tpl/sibox.tpl/shadowrocket.tpl/v2rayn.tpl,见 examples/templates-auto/,复制到 tpls/auto/ 即用) |
配置引用方式:
provider.yaml中template: clash.tpl即相对模板目录的模板名;outputs下每个具名输出可指定不同模板,同一份代理列表渲染多种格式:
provider:
example:
type: clash
source: "prov/example.yaml"
template: clash_list.tpl
filename:
- prod/list/example.yaml
outputs:
base64_sub:
template: base64_sub.tpl
filename:
- prod/example_base64.txt
plain_list:
template: plain_list.tpl
filename:
- prod/example_plain.txt
surge_full.tpl — Surge 完整配置
产出可直接被 Surge 客户端订阅的完整配置([General] / [Proxy] / [Proxy Group] / [Rule] 四段齐全)。节点行来自 surge_proxy 过滤器;[Proxy Group] 含一个 select 组(Proxy)与一个 url-test 自动测速组(Auto);[Rule] 段把 p.rules 指定的规则集 snippet 聚合后经 surge 过滤器输出,末尾补 FINAL,Proxy 兜底。模板不硬编码任何服务器信息,全部来自上下文变量。
参数放在 params.yaml(渲染上下文 p 绑定来自全局参数):
# params.yaml
rules: # [Rule] 段聚合的 snippet 名(config.snippet 的键,缺省空)
- China
rule_policy: Proxy # [Rule] 默认策略名(缺省 Proxy)
test: # url-test 测速参数(缺省 http://cp.cloudflare.com/generate_204 / 300)
url: "http://cp.cloudflare.com/generate_204"
interval: 300
在 provider.yaml 的 outputs 中挂载(template / filename 字段):
provider:
example:
type: clash
source: "prov/example.yaml"
template: clash_list.tpl
filename:
- prod/list/example.yaml
outputs:
surge_full:
name: example
template: surge_full.tpl
filename:
- prod/example_surge_full.conf
首行 #!MANAGED-CONFIG 的拉取地址来自环境变量 SURGE_CONFIG_URL(未设置时输出占位 URL,需改成自己的订阅地址),Surge 客户端会按 interval=86400 定期更新。
[Proxy Group]的节点名列表与[Proxy]段保持一致:仅列出 Surge 支持的 ss/trojan/vmess/snell/http/socks5/wireguard 七种类型(与Surge::supports能力谓词同步)——vless/ssr/hysteria/hysteria2/tuic/anytls 会被surge_proxy跳过,组内引用必须匹配,避免引用不存在的节点。
跑通示例
cd fixtures/examples
thurgio -r . build # -r 是顶层选项,必须放在子命令前
或从仓库根目录:
cargo run -q -p thurgio-cli -- -r fixtures/examples build
构建成功后输出位于 fixtures/examples/prod/:
| 输出 | 模板 | 格式 |
|---|---|---|
prod/list/example.yaml | clash_list.tpl | Clash |
prod/example.yaml | clash.tpl | Clash |
prod/example_groups.yaml | clash_groups.tpl | Clash(动态 proxy-groups) |
prod/example_surge.txt | surge.tpl | Surge(手写 [Proxy] 段) |
prod/example_surge_proxy.txt | surge_proxy.tpl | Surge(过滤器版 [Proxy] 段) |
prod/list/example_v2rayn.txt / prod/example_v2rayn.txt | v2rayn.tpl | 分享链接 |
prod/list/example_sip002.txt / prod/example_sip002.txt | sip002.tpl | 分享链接 |
prod/example_sip008.json | sip008.tpl | SS Android JSON 订阅 |
prod/example_ssd.txt | ssd.tpl | ssd:// 订阅 |
prod/example_shadowrocket.txt | shadowrocket.tpl | Shadowrocket 分享链接 |
prod/example_base64.txt | base64_sub.tpl | Base64 订阅 |
prod/example_plain.txt | plain_list.tpl | 纯文本节点清单 |
prod/example_singbox.json | singbox.tpl | sing-box 完整配置 |
prod/example_singbox_rules.json | singbox_rules.tpl | sing-box 规则数组 |
prod/example_china.list | snippet.tpl | 原样规则集 |
prod/example_quantumult.txt | rules_aggregate.tpl | Quantumult X 规则集 |
挂载新模板:在 provider.yaml 的 outputs(或 snippet.yaml 条目)中按 key 复制现有条目,改 template / filename 即可。注意 clash.tpl 会遍历所有 snippet 读取 p.clash_url,新 snippet 也要带该参数。
fixtures/examples被集成测试锁定(ADR-060),修改声明文件后跑cargo test -p thurgio-app --test render_examples验证。
过滤器快速参考
完整说明见配置指南,此处给出最常用的调用形式:
输出过滤器(代理数组 → 目标格式)
| 过滤器 | 参数 | 示例 |
|---|---|---|
v2rayn | — | {{ l.proxies | v2rayn() }} |
sip002 | — | {{ l.proxies | sip002() }} |
sip008 | — | {{ l.proxies | sip008() }} — SS Android JSON 订阅(仅 ss 节点) |
shadowrocket | — | {{ l.proxies | shadowrocket() }} — 每行一个分享链接(wg 跳过) |
ssd | — | {{ l.proxies | ssd() }} — ssd:// 订阅(仅 ss 节点) |
surge_proxy | — | {{ l.proxies | surge_proxy() }} — Surge [Proxy] 段行(ss/trojan/vmess/snell/http/socks5/wireguard) |
qx | — | {{ l.proxies | qx() }} — Quantumult X 节点行(7 种类型) |
qx_config | check_url=, dns=, group= | {{ l.proxies | qx_config(group="Proxy") }} — 完整 QX 配置四段 |
loon | — | {{ l.proxies | loon() }} — Loon 节点行(10 种类型) |
loon_config | dns=, group= | {{ l.proxies | loon_config(group="Proxy") }} — 完整 Loon 配置四段 |
mellow | port=1080 | {{ l.proxies | mellow(port=1080) }} — Mellow/V2Ray-core JSON(socks inbound + outbounds) |
mixed | — | {{ l.proxies | mixed() }} — 混合单链接列表(V2RayN 优先、SIP002 回退) |
stash | — | {{ l.proxies | stash() }} — Stash(Clash 兼容)节点行 |
surfboard | — | {{ l.proxies | surfboard() }} — Surfboard(Surge 兼容)节点行 |
clashr | — | {{ l.proxies | clashr() }} — ClashR 节点行 |
sibox | — | {{ l.proxies | sibox() | json_str(pretty=true) }} — sing-box outbound 对象数组 |
singbox | — | {{ l.proxies | singbox() }} — sing-box 完整 JSON(别名 sibox) |
cla | meta=true | {{ l.proxies | cla(meta=true) | json_str() }} — 过滤出 clash 兼容节点 |
proxy_suffix | suffix="..." | {{ l.proxies | proxy_suffix(suffix="HK") }} — 给节点名加后缀 |
js | src="..." | {{ l.proxies | js(src="return $input.map(p=>...)") }} — 内联 JS 变换(boa_engine) |
sort_nodes | by=name/region/latency, order=asc/desc, latencies= | {{ l.proxies | sort_nodes(by="latency", latencies=map) }} |
clash_groups | groups="a,b" | {{ l.proxies | clash_groups() }} — Clash/Mihomo 策略组 |
surge_groups | — | {{ l.proxies | surge_groups() }} — Surge 策略组 |
规则过滤器(模板内动态应用 filter.yaml / rename.yaml)
| 过滤器 | 参数 | 示例 |
|---|---|---|
filter | rules="a,b"(或数组) | {{ l.proxies | filter(rules="hk") | v2rayn() }} |
rename | rules="a,b"(或数组) | {{ l.proxies | rename(rules="rm_brackets") | v2rayn() }} |
未定义的规则名静默忽略;过滤器可链式组合,顺序即应用顺序。
规则文本过滤器(snippet 内容 / 规则集)
| 过滤器 | 参数 | 示例 |
|---|---|---|
clash | policy=..., symbol=... | {{ s.response.content | clash(policy="Proxy") }} — 规则文本 → clash 规则列表 |
surge | policy=..., symbol=... | {{ s.response.content | surge(policy="Proxy") }} |
quantumult | policy=..., symbol=... | {{ s.response.content | quantumult(policy="Proxy") }} — 规则文本 → Quantumult X 规则集(host/host-suffix/host-keyword/ip-cidr/ip6-cidr/geoip/user-agent/final;无对应类型的规则丢弃) |
clashbox | policy=..., premium=..., meta=..., pretty=true | {{ s.response.content | clashbox(policy="proxy") }} — clash 规则文本 → sing-box rule 对象 |
no_resolve | value=true/false | {{ s.response.content | no_resolve(value=true) }} |
sort_domain_ip | — | 域名/IP 规则排序 |
通用过滤器
| 过滤器 | 参数 | 示例 |
|---|---|---|
base64 | mode="encode"/"decode" | {{ lines | base64(mode="encode") }} |
json_str | pretty=false | {{ i | json_str(pretty=false) }} |
yaml_str | — | {{ l.proxies | yaml_str() }} |
indent | width=2 | {{ body | indent(width=2) }} |
uniq | — | 按行去重 |
clear | num=3, n=2 | 压缩连续空行 |
slugify / urlencode | separator="-" | 字符串处理 |
函数
| 函数 | 参数 | 示例 |
|---|---|---|
now | format="[year]-[month]-[day]...", tz="+08:00", utc=true, timestamp=true | {{ now() }}、{{ now(timestamp=true) }} |
get_env | name="VAR", default="..." | {{ get_env(name="HOME", default="") }} |
渲染上下文速查
| 变量 | 可用范围 | 说明 |
|---|---|---|
l.proxies | provider | 代理对象数组(每个元素有 name / type / server / port 等字段) |
l.name / l.rename / l.filter | provider | 名称 / 配置层规则名 |
s.response.content | snippet | snippet 源内容(原样文本) |
p.* | 全部 | 自定义参数(params.yaml + /app/admin/p 热更新) |
hp.* | 全部 | 本次 HTTP 请求的查询参数(与 p 分离,ADR-081) |
config | 全部 | 完整配置对象(config.snippet、config.params…) |
headers / version | 全部 | 请求头 / 版本字符串 |
部署指南
使用 systemd、Docker 或反向代理在生产环境中运行 Thurgio。
从 GitHub Release 安装二进制
Thurgio 不发布包管理器制品;release 二进制由 CI 构建并上传到 GitHub Releases,同时发布私有 Docker 镜像(见 使用 Docker)。每个 release 包含各架构的二进制与配套校验文件,资产名带目标架构后缀:thurgio-<target> 与 thurgio-<target>.sha256。
| 目标架构 | 适合场景 | Release 资产 |
|---|---|---|
x86_64-unknown-linux-musl | 绝大多数 x86_64 Linux 服务器 | thurgio-x86_64-unknown-linux-musl |
aarch64-linux-android | Android 设备(不是服务器) | thurgio-aarch64-linux-android |
aarch64-unknown-linux-musl | ARM64 Linux 服务器(如树莓派) | 自行构建:just build-arm-linux |
Release 目前只附带
x86_64-unknown-linux-musl与aarch64-linux-android两个构建产物(见.github/workflows/ci.yml)。ARM64 服务器用户请用仓库内just build-arm-linux交叉编译,或查看 交叉编译 章节。
下载并校验(<tag> 替换为版本号,如 v0.18.0;下面以 x86_64 Linux 服务器为例,Android 设备把 ARCH 换成 aarch64-linux-android):
TAG=v0.18.0
ARCH=x86_64-unknown-linux-musl
curl -fLO "https://github.com/xylylab/thurgio/releases/download/${TAG}/thurgio-${ARCH}"
curl -fLO "https://github.com/xylylab/thurgio/releases/download/${TAG}/thurgio-${ARCH}.sha256"
# 校验 sha256;校验文件记录的文件名是构建时的 `thurgio`,先改名再校验,
# 输出 `thurgio: OK` 才说明二进制与 release 一致
mv thurgio-${ARCH} thurgio
sha256sum -c thurgio-${ARCH}.sha256
sudo install -m 0755 thurgio /usr/local/bin/thurgio
thurgio version
install 会把二进制安装为 root 所有(systemd 单元以专用用户运行二进制,见下节)。若仓库 fork 或迁移,替换上面 URL 中的 xylylab/thurgio 为实际仓库。
作为 systemd 服务
创建一个 systemd 单元来将 Thurgio 作为长期运行的后台服务管理。
1. 创建专用用户
sudo useradd -r -s /usr/sbin/nologin -m -d /var/lib/thurgio thurgio
2. 放置二进制文件
sudo cp thurgio /usr/local/bin/thurgio
sudo chown thurgio:thurgio /usr/local/bin/thurgio
3. 创建配置目录
sudo mkdir -p /etc/thurgio/tpls
sudo chown -R thurgio:thurgio /etc/thurgio
4. 编写配置文件
# /etc/thurgio/thurgio.yaml
setting:
tokens:
admin:
- "your-production-token" # 使用强随机令牌
address: "127.0.0.1" # 绑定到 localhost;反向代理处理公网访问
port: 10100
output:
provider:
"clash.tpl":
- "output/proxies.yaml"
# /etc/thurgio/provider.yaml
my_provider:
name: my_provider
type: clash
source: "https://example.com/clash-config"
template: clash.tpl
filename:
- output/proxies.yaml
5. 创建 systemd 单元
仓库提供了一份带注释与安全加固的单元文件,直接复制即可:
sudo install -m 644 deploy/systemd/thurgio.service /etc/systemd/system/thurgio.service
关键设置说明(完整注释见文件本身):
ExecStart=/usr/local/bin/thurgio -c /etc/thurgio/thurgio.yaml -r /var/lib/thurgio serve—serve在前台运行(无 daemon 模式),配合Type=simple由 systemd 直接管理进程;Restart=always+RestartSec=5s在崩溃或空闲退出(--idle)时自动拉起,StartLimitIntervalSec/StartLimitBurst防 crash 循环。-r /var/lib/thurgio会把工作目录切换到数据根目录:thurgio.db(DATABASE_URL默认值)与配置中的相对输出路径都相对它解析。- 拆分 YAML(
provider.yaml/params.yaml/rename.yaml/filter.yaml/snippet.yaml)与模板目录则相对thurgio.yaml所在目录 解析(即/etc/thurgio),与-r无关。 Environment=DATABASE_URL=/var/lib/thurgio/thurgio.db显式指定数据库位置;RUST_LOG=info控制日志级别(默认即 INFO);模板目录默认tpls(相对配置目录),只有模板不在默认位置时才需要设置THURGIO_TPL_DIR。- 硬化:
NoNewPrivileges、ProtectSystem=strict(仅ReadWritePaths=/var/lib/thurgio可写)、ProtectHome、PrivateTmp/PrivateDevices、RestrictSUIDSGID、RestrictAddressFamilies、UMask=0027等。 - 空闲退出(
--idle):最后一个请求完成 30 秒(IDLE_TIMEOUT)后无新请求时进程正常退出,Restart=always会在下一个请求到达时自动拉起——这正是 idle 模式的设计语义(配合缓存降级--evict,空闲 10 秒先清空数据缓存)。
6. 启动并启用
sudo systemctl daemon-reload
sudo systemctl enable --now thurgio
7. 验证与健康检查
sudo systemctl status thurgio
# 就绪探测:/ready 是公开端点(无需 token),数据库可应答时返回
# 200 {"status":"ready",...},否则 503;curl -f 对非 2xx 退出非零
curl -fsS http://127.0.0.1:10100/ready
/ready 与 Docker 镜像内置的 HEALTHCHECK 使用同一端点(见下文),适合接入负载均衡器或外部监控。/health 是另一个公开端点,返回版本、数据库状态与内存占用等更详细的信息。
8. 查看日志
服务日志输出到 stdout/stderr,由 journald 收集:
journalctl -u thurgio -f # 跟随最新日志
journalctl -u thurgio -n 200 # 最近 200 行
journalctl -u thurgio --since "1 hour ago"
需要结构化日志时,在单元中设置 Environment=RUST_LOG_FORMAT=json。
9. 日志轮转
journald 自带容量与轮转管理(journalctl --vacuum-* 可手动清理),无需额外配置。仅当你自行把日志重定向到文件时才需要 logrotate,示例:
# /etc/logrotate.d/thurgio
/var/log/thurgio/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
}
10. 升级
# 1. 下载新版本二进制并校验(见上方「从 GitHub Release 安装二进制」)
sudo systemctl stop thurgio
sudo install -m 0755 thurgio /usr/local/bin/thurgio
sudo systemctl start thurgio
# 2. 确认新版本与健康状态
thurgio version
sudo systemctl status thurgio
curl -fsS http://127.0.0.1:10100/ready
数据库与输出文件都位于 /var/lib/thurgio,替换二进制不影响数据;/etc/thurgio 配置不动,无需迁移。也可以运行 thurgio upgrade 检查是否有新版本(它只检查并打印下载地址,不会自动安装)。
使用 Docker
仓库根目录提供多阶段 Dockerfile,可直接从源码构建;CI 也会把镜像发布为 GHCR 私有包 ghcr.io/xylylab/thurgio(默认 private,见 发布私有镜像):
build阶段 —rust:alpine(默认 musl 目标,直接产出静态二进制);先复制Cargo.toml/Cargo.lock/crates/以缓存依赖编译层,再以cargo build --release --locked -p thurgio-cli编译;runtime阶段 —alpine:latest+ca-certificates(HTTPS 抓取订阅需要根证书)+tzdata,仅复制二进制。
拉取私有镜像
GHCR 包默认私有,拉取前需要登录(一次性):
# GitHub CLI:输出登录 token 给 docker login
docker login ghcr.io --username <你的 GitHub 用户名>
# 提示输入密码时粘贴以下命令的输出:
gh auth token
# 或使用 PAT(需要 read:packages scope)
docker login ghcr.io --username <你的 GitHub 用户名> --password-stdin <<< "<PAT>"
登录后即可拉取:
docker pull ghcr.io/xylylab/thurgio:v0.18.0 # 或 :latest
从源码构建镜像
docker build -t thurgio:latest .
# 或 just docker-build
.dockerignore 已排除文档、设计文件与构建产物,上下文只包含构建所需的 Cargo.toml/Cargo.lock/crates/、fuzz/Cargo.toml 与 fixtures/examples/tpls/(thurgio-cli 编译期嵌入该模板)。
运行容器
镜像的目录约定:
| 路径 | 用途 |
|---|---|
/config(只读挂载) | 配置目录:thurgio.yaml 及其拆分的 provider.yaml/filter.yaml/rename.yaml/snippet.yaml、模板 tpls/ |
/data(卷) | 数据目录:SQLite 数据库(DATABASE_URL,默认 /data/thurgio.db)与渲染输出 |
# 私有镜像(已登录 GHCR):
docker run -d \
--name thurgio \
-p 127.0.0.1:10100:10100 \
-v "$PWD/config:/config:ro" \
-v thurgio-data:/data \
ghcr.io/xylylab/thurgio:latest
# 或本地构建的镜像:
docker run -d \
--name thurgio \
-p 127.0.0.1:10100:10100 \
-v "$PWD/config:/config:ro" \
-v thurgio-data:/data \
thurgio:latest
- 默认命令为
-r /config serve:-r会切换到配置根目录,thurgio.yaml(-c默认值)及其拆分文件、模板都相对/config解析。 - 输出路径:配置中的输出路径请使用
/data/...绝对路径(相对路径会落在/config只读卷上,导致写入失败)。 - 环境变量(CLI 参数均支持环境变量注入):
DATABASE_URL(数据库位置,默认/data/thurgio.db)、ROOT(等价于-r/--root)、ADDRESS/PORT(监听地址/端口)、TZ(时区)。注意-c/--config没有对应环境变量,配置文件路径只能通过命令行传入。 - 健康检查:镜像内置
HEALTHCHECK,每 30 秒用 busyboxwget探测公开端点http://127.0.0.1:10100/ready(DB ping 通过时返回 200 与{"status":"ready"},否则返回 503;wget对非 2xx 退出非 0);用docker inspect --format '{{.State.Health.Status}}' thurgio查看状态,或在docker run时通过--health-cmd覆盖。
注意:默认监听
0.0.0.0:10100。请通过-p 127.0.0.1:10100:10100仅暴露到本机,并使用反向代理处理外部访问;不要在没有 TLS 的情况下将 Thurgio 直接暴露到互联网。
使用 Docker Compose 运行
# docker-compose.yml
services:
thurgio:
image: ghcr.io/xylylab/thurgio:latest # 私有镜像;或本地构建的 thurgio:latest
container_name: thurgio
restart: unless-stopped
ports:
- "127.0.0.1:10100:10100"
volumes:
- ./config:/config:ro
- thurgio-data:/data
environment:
- TZ=Asia/Shanghai
volumes:
thurgio-data:
发布私有镜像
镜像发布到 GitHub Container Registry(GHCR)的私有包 ghcr.io/xylylab/thurgio,含 linux/amd64 与 linux/arm64 两个架构(manifest list)。包默认私有——只有已登录且有访问权的账号能拉取;如要公开,去 GitHub 仓库的 Packages 页面把该包的可见性改为 public。
CI 自动发布(.github/workflows/docker.yml):推送 v* 标签(即 just release 的 tag)时自动构建并推送 ghcr.io/xylylab/thurgio:vX.Y.Z 与 :latest;手动触发(Actions 页面 Run workflow)推送 dev-<sha> 快照。
手动发布(无需 CI):
# 一次性登录(需要 write:packages scope;GitHub CLI 输出登录 token)
docker login ghcr.io --username <你的 GitHub 用户名>
gh auth token # 输出到密码提示
# 构建并推送 amd64 + arm64
just docker-push tag=v0.18.0
# 覆盖默认 registry(如自建 registry:2):
THURGIO_REGISTRY=registry.example.com/thurgio just docker-push tag=v0.18.0
注意:镜像与 release 二进制一样是发布制品,构建后无法审计源码,请确保镜像与 tag 对应的 git 提交一致(CI 发布路径天然满足)。
自托管文档站点
docs/ 目录自带一份多阶段 Dockerfile:编译期安装最新 mdBook 构建本书(与 docs CI 工作流同一约定),运行期为 Caddy 静态站点(含 404 页与 gzip)。适合内网或离线环境查阅用户文档:
# 从仓库根目录构建(构建上下文即 docs/)
docker build -t thurgio-docs:latest docs/
docker run -d \
--name thurgio-docs \
-p 127.0.0.1:8080:80 \
thurgio-docs:latest
# 打开 http://127.0.0.1:8080/
反向代理
始终将 Thurgio 运行在反向代理(nginx、Caddy)之后,以实现 TLS 终止、速率限制和访问控制。
Nginx
# /etc/nginx/sites-available/thurgio
upstream thurgio_backend {
server 127.0.0.1:10100;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name thurgio.example.com;
ssl_certificate /etc/ssl/certs/thurgio.pem;
ssl_certificate_key /etc/ssl/private/thurgio-key.pem;
# 加固 TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
# 速率限制
limit_req zone=thurgio:10m rate=30r/s;
limit_req_status 429;
# 代理所有请求到 Thurgio 后端
location / {
proxy_pass http://thurgio_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
# 可选:阻止对敏感端点的访问
location /favicon.svg {
proxy_pass http://thurgio_backend;
}
location /health {
proxy_pass http://thurgio_backend;
}
location /metrics {
# 将 metrics 限制为内部监控
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;
proxy_pass http://thurgio_backend;
}
}
Caddy
# Caddyfile
thurgio.example.com {
reverse_proxy 127.0.0.1:10100
health_uri /health
health_interval 30s
}
# 可选:限制 metrics 端点
@metrics {
path /metrics
not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
}
handle @metrics {
respond 403
}
rate_limit {
zone dynamic {
key {remote_host}
events 30
window 1s
}
}
}
Caddy 通过 Let’s Encrypt 自动配置 TLS 证书。
交叉编译
Thurgio 通过 cross 支持多平台交叉编译(见 justfile):
| 目标 | 命令 |
|---|---|
| Linux(musl,默认) | just build-linux(x86_64-unknown-linux-musl) |
| Windows | just build-win(x86_64-pc-windows-gnu) |
| Android | just build-android(aarch64-linux-android) |
| Linux ARM | just build-arm-linux(aarch64-unknown-linux-musl) |
CI(.github/workflows/ci.yml)会在推送时构建 x86_64-unknown-linux-musl 与 aarch64-linux-android 的 release 二进制并上传制品(含 SHA256)。
生产环境检查清单
上线前,请逐项确认:
安全
- API 令牌 已从默认值改为强随机值,并按作用域分组:
yaml setting: tokens: admin: - "admin-xyz789" # 管理操作(写、刷新、重载等) read: - "readonly-abc123" # 只读渲染 - TLS 已启用——通过 Thurgio 内置 TLS 或反向代理。
- 反向代理已配置——Thurgio 绑定到
127.0.0.1,而非0.0.0.0。 - CORS 来源已限制——将
["*"]替换为具体域名:yaml setting: cors_origins: - "https://example.com" - 速率限制已启用——Thurgio 内置限流器(默认每 IP 每 60 秒 600 次,可用
THURGIO_RATE_LIMIT_REQUESTS/THURGIO_RATE_LIMIT_WINDOW_SECONDS调整)、nginx 或 Caddy。
运维
- systemd 单元 包含
Restart=always和安全加固(使用 idle 模式时必须为always,on-failure不会拉起正常退出)。 - 服务日志 可通过
journalctl -u thurgio查看(journald 自带轮转;仅当重定向到文件时才需要 logrotate)。 - 输出目录已创建 且 Thurgio 用户可写入。
- 模板目录 已填充且可读。
- 数据库路径 已明确(
DATABASE_URL,默认thurgio.db),且所在目录可写。
监控
- 健康端点 已监控(
GET /health返回版本、数据库状态、内存使用等)。 - 就绪端点 用于负载均衡健康检查(
GET /ready)。 - 已通过 Prometheus 收集指标(
GET /metrics,包含请求指标、熔断器快照与渲染线程池指标)。 - 缓存 TTL 配置合理:
yaml setting: ttl: provider: 600 # Provider 缓存有效期(秒,最小 60) snippet: 86400 # Snippet 缓存有效期(秒,最小 300)
性能
- 并发限制已根据服务器调整:
yaml setting: concurrency_limit: 20 # 根据 CPU 核心数和网络情况调整(默认 10) - 渲染线程池参数已调整(如模板渲染量大):
THURGIO_RENDERER_MIN_THREADS/THURGIO_RENDERER_MAX_THREADS。 - 反向代理中已启用 Keepalive(见上方 nginx/Caddy 配置)。
示例:完整生产环境布局
/usr/local/bin/
└── thurgio
/etc/
└── thurgio/
├── thurgio.yaml
├── provider.yaml
├── filter.yaml
├── rename.yaml
├── snippet.yaml
└── tpls/
├── clash.tpl
└── v2rayn.tpl
/var/
└── lib/
└── thurgio/
├── output/ # 渲染的输出文件
└── thurgio.db # SQLite 数据库(自动创建)
/etc/systemd/system/
└── thurgio.service
/etc/nginx/sites-enabled/
└── thurgio.conf
常见问题
如何开始?
运行 thurgio init 生成最小配置(含 provider.yaml、filter.yaml 等拆分文件与 tpls/ 模板目录),然后执行 thurgio build。用 thurgio check 验证配置。
如何添加代理提供者?
编辑 provider.yaml,填入你的提供者 URL 和类型(clash/meta/v2rayn/sip002/surge)。本地文件直接用路径字符串 source: "path",远程 URL 用 source: "https://..."(http(s) 开头自动识别为 URL)。
如何过滤代理?
在 filter.yaml 中定义过滤规则。示例:
# 简写:包含任一关键字的代理(忽略大小写)
jp: "日本|Japan"
# 排除(negate)
rm_jp:
- contain: [日本, Japan]
negate: true
contain/start/end/type/regex 五种匹配器,详见配置参考。
如何重命名代理?
在 rename.yaml 中定义正则替换规则([正则, 替换串],支持 $1 反向引用):
us: ["(.*)", "🇺🇸 $1"]
如何启用 TLS?
使用 thurgio serve --tls-cert cert.pem --tls-cert-key key.pem(可加 --tls-port、--tls-only),或用环境变量 TLS_CERT / TLS_CERT_KEY。
如何监控配置变更?
使用 thurgio build --watch 在当前目录文件变更时自动重建;thurgio serve 也会监听配置文件变更并自动重建。模板目录(tpls/)变更会自动重载渲染器。
如何获取实时构建通知?
通过 Server-Sent Events 连接到 /app/read/events(需令牌认证,?token= 或 Authorization: Bearer)。每次构建完成会推送 build_complete 事件。也可设置 THURGIO_WEBHOOK_URL 在构建完成时收到 HTTP 通知。
如何查看渲染后的配置?
启动 thurgio serve 后请求 /app/read/l?name={provider} 或 /app/read/r?name={snippet},例如:
curl "http://localhost:10100/app/read/l?name=my_provider&token=my-token"
输出文件在哪里?
在 thurgio.yaml 的 setting.output 中配置(按渲染目标 + 模板名分组):
setting:
output:
provider:
"clash.tpl":
- "output/proxies.yaml"
snippet:
"rule.tpl":
- "output/rules.yaml"
条目键为模板名,值是要写入该模板默认输出的目录列表。
如何升级?
运行 thurgio upgrade 检查 GitHub Releases 是否有新版本;或 cargo install --path crates/thurgio-cli(开发构建)/ 下载最新发布版本。
故障排查
配置改坏了,起不来怎么办?
用 thurgio check 定位解析错误;改坏前做过备份的话直接 thurgio config restore <备份名> 回滚——config 命令不加载配置,坏配置下也能运行。
订阅源挂了构建会失败吗?
不会立即失败。抓取失败时回退数据库中的 last-known-good 缓存;连续失败触发熔断器(open 后跳过该源一段时间),恢复情况看 GET /app/read/health 聚合端点。
过滤/改名规则好像没生效?
规则名拼错会被静默忽略(解析期丢弃未知名字)。跑 thurgio check,悬空引用会以 WARNING 列出。
模板报 rule set 'xxx' not found?
ruleset 过滤器的名字在 ruleset.yaml 与内置资产中都找不到。注意它与 filter/rename 不同:未定义直接报错而非静默忽略。
输出文件没有生成?
检查两点:provider 的 filename 是否写了;若期望默认输出,setting.output 的键是模板名且目录条目要匹配 item 自己的模板。thurgio check 会报告输出父目录缺失。
改了模板但不生效?
thurgio build --watch下模板变更直接触发重建。serve默认只监听主配置文件,模板变更不监听——开发时加--dev(每次渲染前重载模板)或调POST /app/admin/reload。
渲染结果不对怎么调试?
RUST_LOG=debug,thurgio=trace thurgio build # 详细日志
RUST_LOG_FORMAT=json thurgio serve # 结构化日志
另可用 GET /app/read/c 直接查看渲染上下文 JSON,确认变量取值。
401 但 token 明明对?
令牌分 Read/Admin 作用域:Read 令牌访问 /app/admin/* 会被拒。检查用的是哪组令牌、请求的是哪个命名空间。
API 参考
概述
基础 URL:http://<address>:<port>/(默认 0.0.0.0:10100)。
认证方式:令牌通过 ?token= 查询参数或 Authorization: Bearer <token> 请求头传递(查询参数优先)。令牌按作用域分组:
- Read — 可访问只读渲染端点。
- Admin — 可访问全部端点(Admin 令牌同时满足 Read 要求)。
所有受保护的端点都位于 /app/ 命名空间下。未认证或作用域不足的请求返回 401 Unauthorized。
中间件
每个请求都会经过以下中间件栈(自外向内):
| 中间件 | 行为 |
|---|---|
| 超时 | 请求超过 9 秒返回 504 Gateway Timeout |
| 追踪 | HTTP 请求追踪(TraceLayer) |
| 压缩 | 透明负载压缩(CompressionLayer) |
| 请求体限制 | 超过 10 MB 返回 413 Payload Too Large |
| 请求追踪器 | 为每个请求添加 x-request-id 响应头并记录日志 |
| 指标 | 为每个请求记录 Prometheus 指标 |
| CORS | 可配置的来源(setting.cors_origins,默认 ["*"]);所有响应附带 Access-Control-Allow-* 与 HSTS 头。来源列表每请求从 live config 读取,修改 cors_origins 后热重载立即生效 |
| 速率限制 | 按 IP 限流(默认每 60 秒 600 次)+ 并发限制(默认 600),超限返回 429 Too Many Requests(带 Retry-After 头) |
| 内容类型校验 | POST/PUT/PATCH 必须携带 Content-Type: application/json 或 application/x-www-form-urlencoded,缺失返回 400,类型不符返回 415 |
| 认证 | 按路由作用域校验令牌(Read/Admin) |
公开端点
以下端点无需认证。
GET /
健康检查。返回纯文本问候信息。
响应:200 OK
Hello world!
GET /v
版本信息。
响应:200 OK
thurgio 0.18.0 (git-describe 快照)
GET /health
以 JSON 格式返回详细的系统健康状态。
响应:200 OK
{
"status": "healthy",
"version": "thurgio 0.18.0 (...)",
"database": "connected",
"memory_usage_mb": 42,
"cpu_cores": 8,
"timestamp": "2025-01-01T00:00:00Z"
}
字段说明:
| 字段 | 类型 | 描述 |
|---|---|---|
status | string | 始终为 "healthy" |
version | string | 名称 + 版本号 + git 快照 |
database | string | 始终为 "connected" |
memory_usage_mb | integer | RSS 内存(MB,来自 /proc/self/status 的 VmRSS) |
cpu_cores | integer | 可用并行度 |
timestamp | string | RFC 3339 时间戳(UTC) |
GET /ready
就绪探针——检查 SQLite 数据库是否可读。
响应 200 OK(数据库可读):
{
"status": "ready",
"database": "connected"
}
响应 503 Service Unavailable(数据库不可读):
{
"status": "not_ready",
"database": "disconnected"
}
GET /metrics
Prometheus 格式的指标数据,包含请求指标、熔断器快照、渲染线程池指标(renderer_active_threads、renderer_backlog_smoothed、renderer_threads_spawned_total、renderer_threads_exited_total、renderer_slots_total)与内存占用。
响应:200 OK
纯文本 Prometheus 暴露格式。
GET /favicon.svg / GET /favicon-dark.svg
编译期嵌入的站点图标(crates/thurgio-server/assets/favicon.svg 与 favicon-dark.svg)。登录页与 Dashboard 通过 <link rel="icon" media="(prefers-color-scheme: ...)"> 按系统配色自动切换亮/暗图标。
响应:200 OK,内容类型 image/svg+xml。
GET /icon-app.svg
编译期嵌入的渐变 App 图标(crates/thurgio-server/assets/icon-app.svg),Dashboard 头部 brand 标记使用。
响应:200 OK,内容类型 image/svg+xml。
GET /app/login / POST /app/login
令牌登录页与校验接口(无需认证)。
GET返回登录页 HTML。POST接收 JSON{"token": "..."},校验令牌作用域:
响应 200 OK(有效令牌):
{ "ok": true, "scope": "admin" }
scope 为 "admin" 或 "read"。
响应 400 Bad Request(缺少 token 字段):
{ "ok": false, "error": "missing token" }
响应 401 Unauthorized(令牌无效):
{ "ok": false, "error": "invalid token" }
只读端点(Read/Admin 令牌)
以下端点接受 Read 或 Admin 作用域令牌。
渲染 Provider
GET /app/read/l?name={name}
按名称渲染 provider(name 为必填查询参数)。渲染前会按 TTL 检查并更新数据。
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
name | string | 必填——要渲染的 provider 名称(缺失或为空 → 400) |
d | string | 可选——设置 Content-Disposition: attachment; filename="..."(为空时使用 name) |
token | string | 认证令牌(也可通过 Authorization: Bearer 头传递) |
| (任意) | string | 其他查询参数将作为渲染参数(hp)传入 |
请求级规则覆盖(ADR-061,仅 provider 端点,不写入配置):
| 参数 | 语义 |
|---|---|
pre_rename / filter / rename | 覆盖配置中对应阶段(逗号分隔的规则名);空值 = 清空该阶段 |
ap_pre_rename / ap_filter / ap_rename | 追加到配置对应阶段规则之后 |
emoji | 覆盖 emoji 策略(enable/disable/hide;非法值 → 400) |
udp | 覆盖 udp 策略(none/false/try/true;非法值 → 400) |
tpl | 覆盖本次渲染模板(空值回退 provider template) |
同一阶段同时传普通与 ap_ 参数(如 rename= + ap_rename=)→ 400;未知规则名静默忽略;带规则参数时从原始响应内容重新解析(仅 tpl 时复用缓存)。snippet(/app/read/r)与 adhoc 遮蔽命中时不支持规则覆盖。
# 覆盖 filter:本次请求只保留 us + vmess 规则命中的节点
curl ".../app/read/l?name=my-provider&token=...&filter=us,vmess"
# 追加规则 / 清空过滤
curl ".../app/read/l?name=my-provider&token=...&ap_filter=vmess"
curl ".../app/read/l?name=my-provider&token=...&filter="
# 切换模板
curl ".../app/read/l?name=my-provider&token=...&tpl=clash.tpl"
响应:渲染后的 provider 内容,并转发提供者响应的 subscription-userinfo 头。
渲染 Snippet
GET /app/read/r?name={name}
按名称渲染 snippet(name 为必填查询参数)。
查询参数:与 /app/read/l?name= 相同(但不支持请求级规则覆盖)。
响应:渲染后的 snippet 内容,并转发请求携带的 subscription-userinfo 头。
渲染 UI
GET /app/read/ui
后台触发一次更新,然后以 HTML 形式渲染 ui.tpl 模板。上下文注入当前令牌的 scope 变量("admin"/"read")。
响应:200 OK,内容类型为 text/html。
渲染上下文
GET /app/read/c
以 JSON 格式渲染完整的 Tera 上下文(config、p、hp、headers、version 等),同样注入 scope 变量;config 内的 proxies/content 字段已剔除(dashboard 只消费资源键名)。
响应:200 OK,内容类型为 application/json。
构建通知(SSE)
GET /app/read/events
Server-Sent Events 流。每次构建完成推送 build_complete 事件,每 15 秒发送一次 keep-alive 心跳。
响应:200 OK,text/event-stream。
订阅健康度
GET /app/read/health
订阅健康度聚合:为每个 provider/snippet 报告熔断器状态(closed/open/half-open)、失败计数、最后失败时间(Unix 秒)与最后成功更新时间(RFC 3339),并汇总为总体状态。数据源为熔断器池快照与 DB 中持久化的抓取时间戳,不触发更新。
响应:200 OK
{
"overall": "degraded",
"summary": { "total": 3, "closed": 2, "open": 1, "half_open": 0 },
"items": [
{
"key": "provider:example",
"name": "example",
"type": "provider",
"breaker": "open",
"failure_count": 5,
"last_failure_at": 1755160000,
"last_update_at": "2026-08-14T12:00:00Z"
}
]
}
overall 取值:healthy(无 open/half-open)、degraded(存在但未全部 open)、unhealthy(全部 open);空配置视为 healthy。未抓取过的 item 两个时间字段均为 null。
订阅商店
GET /app/read/subs
聚合列出全部配置订阅。provider 在前(声明顺序),带解析后节点数(proxies)与流量元数据(来自 subscription-userinfo 头,无该头或不可解析时各字段为 null);snippet 在后(无节点数与元数据字段)。adhoc 临时资源不列出。
响应:200 OK
{
"count": 2,
"items": [
{
"name": "example",
"kind": "provider",
"proxies": 42,
"upload": 1073741824,
"download": 10737418240,
"total": 107374182400,
"used": 11811160064,
"expire": 1893456000,
"expire_iso": "2030-01-01T00:00:00Z"
},
{ "name": "ruleset", "kind": "snippet", "proxies": null, "upload": null, "download": null, "total": null, "used": null, "expire": null, "expire_iso": null }
]
}
除数据就绪门卫外不触发抓取,纯只读聚合。
订阅元数据
GET /app/read/meta?name={name}
报告单个 provider 的流量/配额元数据,解析其缓存响应中的 subscription-userinfo 头。
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
name | string | 必填——provider 名 |
响应:200 OK
{
"name": "example",
"upload": 1073741824,
"download": 10737418240,
"total": 107374182400,
"used": 11811160064,
"expire": 1893456000,
"expire_iso": "2030-01-01T00:00:00Z"
}
| 字段 | 类型 | 描述 |
|---|---|---|
used | integer | null | 已用流量(upload + download),两者均未知时为 null |
expire_iso | string | null | 到期时间的 UTC RFC 3339 形式 |
| 其余 | integer | null | 上传/下载/总量/到期 Unix 秒,未知时为 null |
仅支持 provider 名:snippet 或 adhoc 同名 → 400;未知 → 404;响应无该头或无可解析字段 → 404。
Provider 历史序列
GET /app/read/history?name={name}&limit={n}
报告单个 provider 持久化的快照时间序列——每次成功同步上游记录一个点(时间戳、节点数、当时的流量元数据),按时间升序。只读 KV 持久层:无就绪门卫、不触发网络;从未同步过的 provider 返回空列表。
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
name | string | 必填——provider 名 |
limit | integer | 可选——只保留最近 N 个快照(结果仍升序) |
响应:200 OK
{
"name": "example",
"snapshots": [
{
"ts": 1755153600,
"proxies": 40,
"upload": null, "download": null, "total": null, "expire": null
},
{
"ts": 1755160000,
"proxies": 42,
"upload": 1073741824, "download": 10737418240, "total": 107374182400, "expire": 1893456000
}
]
}
provider-only 策略与 /app/read/meta 一致:snippet/adhoc 同名 → 400;未知 → 404。
历史快照对比
GET /app/read/history/diff?name={name}&from={a}&to={b}
对比同一 provider 时间序列中的两个快照。from/to 先按快照时间戳精确匹配,匹配不到再作为 0 基序列索引解析;非数字、无法定位、或 from 新于 to 均 → 400。
响应:200 OK
{
"name": "example",
"from": { "ts": 1755153600, "proxies": 40, "...": "..." },
"to": { "ts": 1755160000, "proxies": 42, "...": "..." },
"proxies_delta": 2
}
快照只持久化聚合约(时间戳/节点数/流量元数据),不含逐节点明细,因此没有节点级 added/removed 分解——需要时用 CLI 的 thurgio diff nodes 对比原始订阅源。
延迟测试
GET /app/read/latency?name={name}&timeout_ms={ms}&retry={n}
对单个 provider 的全部已解析代理做 TCP 连接延迟测试。结果按次计算、永不缓存(延迟是时效性数据)。代理列表来自已解析的代理库(与渲染缓存同源),不重新解析原始内容。
查询参数:
| 参数 | 类型 | 缺省 | 描述 |
|---|---|---|---|
name | string | — | 必填——provider 名 |
timeout_ms | integer | 3000 | 单次探测超时(钳制到 [100, 10000]) |
retry | integer | 1 | 每个代理重试次数(钳制到 [1, 3]) |
并发度取 setting.concurrency_limit。
响应:200 OK(结果按延迟升序,不可达代理排在最后)
{
"name": "example",
"count": 2,
"results": [
{ "proxy_name": "HK-01", "alive": true, "latency_ms": 45 },
{ "proxy_name": "US-01", "alive": false, "latency_ms": null }
]
}
UDP 能力
GET /app/read/udp?name={name}
逐代理报告 UDP 转发生效标志。这是订阅元数据的静态能力报告(非实时探测):标志在解析期应用全局 setting.udp 策略后得出——源条目省略 udp 字段但策略为 Try 时可能报 true。
查询参数:name(必填,provider 名)。
响应:200 OK(按 provider 内顺序)
{
"name": "example",
"count": 2,
"results": [
{ "proxy_name": "HK-01", "udp": true },
{ "proxy_name": "US-01", "udp": false }
]
}
入口落地检测
GET /app/read/geo?name={name}&timeout_ms={ms}
把每个已解析代理的 server 主机做 DNS 解析,并经 ip-api 批量 GeoIP 查询国家/地区。
查询参数:
| 参数 | 类型 | 缺省 | 描述 |
|---|---|---|---|
name | string | — | 必填——provider 名 |
timeout_ms | integer | 5000 | 单主机解析超时(钳制到 [100, 10000]) |
并发解析上限 8。单主机失败不传播错误——对应条目字段置 null。
响应:200 OK(按 provider 内顺序,重复 server 保留)
{
"name": "example",
"count": 2,
"results": [
{ "server": "hk.example.com", "ip": "103.1.2.3", "country": "Hong Kong", "country_code": "HK" },
{ "server": "us.example.com", "ip": null, "country": null, "country_code": null }
]
}
流媒体解锁探测
GET /app/read/unlock?services={csv}&timeout_ms={ms}&proxy={url}
探测流媒体服务在当前出口网络的可用性与地区。
查询参数:
| 参数 | 类型 | 缺省 | 描述 |
|---|---|---|---|
services | csv | 全部三项 | 要探测的服务:netflix、youtube、disney(别名 disneyplus);未知值 → 400 |
timeout_ms | integer | 5000 | 单服务探测超时(钳制到 [100, 10000]) |
proxy | URL | 无 | 本次请求使用的一次性上游代理(http(s):// 或 socks5://),非法格式 → 400 |
出口语义:探测的是 thurgio 进程自身的出口网络(或
proxy=指定的上游)——不是逐节点的出口。逐节点测试需要内嵌代理核心,超出 v1 范围。并发探测上限 3。
响应:200 OK(按请求顺序)
{
"results": [
{ "service": "netflix", "available": true, "region": "JP", "latency_ms": 230 },
{ "service": "youtube", "available": false, "region": null, "latency_ms": 180 },
{ "service": "disney", "available": false, "region": null, "latency_ms": null, "error": "timeout" }
]
}
被拦截的 HTTP 状态归类为“不可用“而非错误;只有网络错误/超时才填 error 字段。
管理端点(仅 Admin 令牌)
以下端点仅接受 Admin 作用域令牌。
设置参数
GET /app/admin/p
通过查询字符串设置渲染参数,与内存中的配置参数深度合并(点分键如 dns.enabled 会写入嵌套结构)。
查询参数:任意键值对——每个都成为一个渲染参数。
响应:空内容,200 OK。
设置提供者源 URL
GET /app/admin/s
动态更新提供者的源 URL。不使用查询参数——依赖于请求头。
必需请求头:
| 请求头 | 值 |
|---|---|
prov | 要更新的提供者名称 |
url | 提供者的新源 URL |
更新后会自动重置该提供者的熔断器并触发一次强制更新。
响应:200 OK
{ "status": "ok", "provider": "<name>", "source": "<url>" }
响应 400 Bad Request(缺少 prov 或 url 头);404 Not Found(提供者不存在)。
构建并写入输出
GET /app/admin/write
完整构建流程:更新所有提供者,渲染所有 provider/snippet,并将输出文件写入磁盘。
响应:空内容,200 OK(失败时返回错误 JSON)。
强制刷新
GET /app/admin/refresh
强制更新所有提供者和 snippet。与 /app/admin/write 不同,此端点仅执行更新步骤,不写入输出。
响应:空内容,200 OK(失败时返回错误 JSON)。
重新加载模板引擎
GET /app/admin/reload
重新加载 Tera 模板引擎,拾取磁盘上模板文件的所有变更。
响应:空内容,200 OK。
持久化到数据库
GET /app/admin/save
将所有当前 provider/snippet 响应持久化到数据库(未 fetch 过的 item 跳过,避免覆盖 last-known-good 缓存行)。
响应:空内容,200 OK。
设置开发模式
GET /app/admin/dev?value={value}
启用或禁用开发模式(每次渲染前重新加载模板)。
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
value | boolean | 必填——true 或 false(缺失或非法值 → 400) |
响应:设置后的当前开发模式值的字符串——"true" 或 "false"。
创建临时 Provider/Snippet
GET /app/admin/adhoc
创建一次性 provider/snippet(不写入配置文件),立即渲染返回;同名临时资源可经 /app/read/l、/app/read/r 后续引用,且对配置中的同名正式项呈遮蔽效果。缓存持久化到 DB,重启后命中缓存即恢复;按 adhoc.max_items/adhoc.ttl_seconds 自动清理。
查询参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
type | string | 是 | provider / snippet |
url | string | 是 | 数据源 URL |
name | string | 否 | 名称,缺省自动生成 adhoc-<8 位 hex> |
format | string | 否 | clash/meta/v2rayn/sip002/surge/auto,默认 auto(仅 provider 生效) |
tpl | string | 否 | 模板名;缺省回退 setting.adhoc.tpl.provider/tpl.snippet |
pre_rename / filter / rename | string | 否 | 逗号分隔的规则名,按顺序应用(未知规则名静默忽略) |
force | boolean | 否 | true 忽略内存 + DB 缓存强制重新获取 |
响应:渲染后的内容,内容类型 text/plain,并携带 Content-Disposition(d 参数)与抓取响应的 subscription-userinfo 头。
备份配置到 GitHub Gist
GET /app/admin/gist?token={gh_pat}&description={text}
把进程工作目录(即 -r/--root 切换后的目录)下的声明配置 YAML 备份为 GitHub 私密 Gist。
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
token | string | 可选——GitHub PAT;缺省时依次回退 config params 的 gist.token、环境变量 GITHUB_TOKEN |
description | string | 可选——Gist 描述,默认 thurgio config backup |
备份文件清单:thurgio.yaml / provider.yaml / params.yaml / snippet.yaml / filter.yaml / rename.yaml / ruleset.yaml 中实际存在的文件;一个都不存在 → 400。token 三个来源全空 → 400。v1 无状态,不持久化任何备份记录。
响应:200 OK
{
"id": "abc123...",
"html_url": "https://gist.github.com/xxx/abc123",
"files": ["thurgio.yaml", "provider.yaml"]
}
上游失败 / 非 2xx / 错误信封 → 502 并携带上游消息。
短链接生成
GET /app/admin/shorten?url={long}&service={name}
经配置的短链接服务缩短 URL。服务模板取自 params:?service= 命中 shortlink.<service> 键时用之,否则用 shortlink.default;模板必须包含 {url} 占位符(点路径深合并产生的嵌套 mapping 形态同样接受)。内置 TinyURL 预设。
# params.yaml 示例
shortlink:
default: "https://tinyurl.com/api-create.php?url={url}"
查询参数:
| 参数 | 类型 | 描述 |
|---|---|---|
url | string | 必填——长链接,必须以 http:// / https:// 开头(否则 → 400) |
service | string | 可选——params 中的服务模板名 |
响应:200 OK
{ "short": "https://tinyurl.com/2p2xxxxx", "long": "https://example.com/very-long-url" }
未配置服务模板 → 400;上游请求失败或响应中提取不到短链 → 502。
查询参数语义
渲染端点(l/r/p/ui/c)的查询参数:
token用于认证,不会进入渲染参数。- 其余参数的值按 YAML 解析(
true→ 布尔、9090→ 数字、其他 → 字符串),作为hp注入模板(请求参数面,与配置参数面p分离)。 - 键中包含
.的参数(如dns.enabled=true)会深度合并进嵌套结构(hp.dns.enabled)。 - 单次请求参数超过 100 个会被拒绝(参数整体忽略并告警)。
d参数额外触发Content-Disposition: attachment; filename="<d>"响应头(用于订阅下载场景)。
响应状态码
| 状态码 | 含义 |
|---|---|
200 OK | 成功 |
400 Bad Request | 缺少必需请求头 / POST 缺少 Content-Type / 登录缺少 token |
401 Unauthorized | 令牌无效或缺失(或作用域不足) |
404 Not Found | 资源不存在(provider、snippet 等) |
413 Payload Too Large | 请求体超过 10 MB 限制 |
415 Unsupported Media Type | POST/PUT/PATCH 的 Content-Type 不受支持 |
429 Too Many Requests | 超过速率限制或并发限制(带 Retry-After 头) |
500 Internal Server Error | 服务器内部错误 |
503 Service Unavailable | 数据库未就绪(仅就绪探针) |
504 Gateway Timeout | 请求处理超过 9 秒 |
CLI 参考
thurgio [OPTIONS] [COMMAND]
全局参数
| 参数 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
-c, --config <FILE> | — | thurgio.yaml | 配置文件路径。sibling YAML 与模板目录相对其所在目录解析 |
-r, --root <DIR> | ROOT | 当前目录 | 工作根目录(进程 chdir 到该目录;数据库与输出相对路径均按它解析) |
-h, --help | — | — | 帮助 |
-V, --version | — | — | 版本 |
全局参数必须放在子命令之前:thurgio -r . build(thurgio build -r . 会报错)。
子命令总览
| 命令 | 别名 | 说明 | 文档 |
|---|---|---|---|
build | b | 更新 + 渲染 + 写盘;--watch 监听重建 | build |
serve | s | 启动 HTTP/TLS API 服务器 | serve |
init | i | 生成最小可用配置脚手架 | init |
check | c | 校验配置并打印摘要与逐项检查结果 | check |
diff | d | 输出文件 diff 或订阅节点级 diff | diff |
config | cfg | 配置声明文件备份 / 回滚 / 列表 | config |
upgrade | u | 检查 GitHub Releases 新版本 | upgrade |
generate | g | 生成 shell 补全脚本 | generate |
version | V | 打印版本信息 | version |
不带子命令直接运行 thurgio 只加载配置后退出(可用于验证解析是否成功)。
命令的初始化需求
各命令对环境的依赖不同(排错时有用):
| 阶段 | 命令 |
|---|---|
| 不需要 logger/配置 | generate, version |
| 只需要 logger | init |
| 只需要 root 目录 | config(备份/回滚在坏配置下也能运行) |
| 完整初始化(配置 + 数据库 + 渲染器 + 熔断器恢复) | build, check, diff, serve, upgrade |
启动流程细节见 app。
环境变量
所有带环境变量的参数遵循同一优先级:命令行 > 环境变量 > 默认值/配置值。完整清单见配置指南。
App
启动流程(thurgio_cli::cli::init):
- 若子命令为
generate或version,直接执行并返回(不需要 logger)。 - 初始化 logger:
- 读取
RUST_LOG过滤级别,默认info(如info,thurgio=debug表示所有 crate 启用 info 级,thurgio 启用 debug 级;也可用于关闭某些 crate 的日志)。 - 读取
RUST_LOG_ANSI,为true时启用颜色;未设置时自动检测(stdout 为 TTY 才启用)。同时控制 logger 与 CLI 彩色输出(owo-colors,如build的✔/✘)。 - 读取
RUST_LOG_FORMAT,为json时输出 JSON 日志。
- 读取
- 若子命令为
init,生成配置并返回(需要 logger,不需要配置)。 - 若设置了
-r/--root(或ROOT环境变量),set_root_dir创建并切换到该目录。 - 初始化 Config 和 Renderer(
thurgio_app::initialize:Source → AppState → 熔断器状态恢复)。 - 执行子命令(
build/check/diff/upgrade/serve)。
不带子命令直接运行 thurgio 只执行初始化(加载配置)后退出。
build
alias: b
执行完整的 更新 → 渲染 → 写盘 管线,生成所有 provider(主输出 + 具名输出)与 snippet 的输出文件。
执行流程
- 更新订阅源 — 按 TTL 判断每个 provider/snippet 是否过期:未过期直接复用内存缓存;过期或
--force时重新获取;获取失败时回退到数据库中的 last-known-good 缓存(配合熔断器,见常见问题)。 - 解析与规则管线 — 解析代理列表并应用
pre_rename → filter → rename(详见配置指南)。 - 渲染 — 构建渲染上下文(
config/p/headers/version),provider 阶段渲染主输出与具名输出,与 snippet 阶段并发执行。 - 写盘 — 渲染结果经暂存目录原子发布到各输出路径。
- 发事件 — 构建完成发出
ProviderUpdated/SnippetUpdated/BuildCompleted事件:触发 SSE 通知(/app/read/events)与 webhook(设置THURGIO_WEBHOOK_URL时 POST JSON)。
成功输出 ✔ Build complete,失败输出 ✘ Build failed(颜色遵循 RUST_LOG_ANSI / TTY 检测)。
参数
| 参数 | 别名 | 环境变量 | 默认 | 说明 |
|---|---|---|---|---|
--force | -f | FORCE | false | 忽略缓存状态,强制重新获取所有订阅源 |
--watch | -w | WATCH | false | 构建一次后进入监听模式,文件变更自动重建 |
--notify | -N | NOTIFY | false | 构建完成时通过 notify-send 发送系统通知(Linux 桌面) |
监听模式(--watch)
thurgio build --watch
- 监听当前工作目录(递归)。
- 变更事件经 500ms 防抖合并后触发重建。
- 忽略构建自身的写入(输出目录与 SQLite 数据库文件),避免自触发循环。
- 命中配置声明文件(
thurgio.yaml、provider.yaml、params.yaml、rename.yaml、filter.yaml、snippet.yaml)时先全量重载配置再重建;重载失败保留旧配置仅记日志。其余路径(模板等)直接重跑更新 + 渲染管线。
按 Ctrl+C 停止。
示例
# 常规构建(TTL 内复用缓存)
thurgio build
# 强制刷新所有订阅源
thurgio build --force
# 构建 + 监听变更 + 完成时系统通知
thurgio build --watch --notify
Serve
alias: s
运行 HTTP 服务器。
参数
| 参数 | 别名 | 环境变量 | 说明 |
|---|---|---|---|
--address | -a | ADDRESS | 监听地址;未指定时使用配置 setting.address(默认 0.0.0.0) |
--port | -p | PORT | 监听端口;未指定时使用配置 setting.port(默认 10100)。端口为 0 时使用随机端口 |
--dev | -d | DEV | 开发模式:每次渲染前重新加载模板 |
--idle | -i | IDLE | 空闲模式:IDLE_TIMEOUT(默认 30s)内无请求则退出程序(配合 systemd Restart=always 按需拉起) |
--evict | -e | EVICT | 空闲时清空数据缓存(默认开启;--evict=false 关闭):EVICT_TIMEOUT(默认 10s)无请求先清缓存,进程继续存活 |
--tls-only | -s | TLS_ONLY | 仅提供 HTTPS(必须同时提供证书与私钥,否则报错) |
--tls-port | -t | TLS_PORT | TLS 端口,默认等于 --port |
--tls-cert | -c | TLS_CERT | TLS 证书文件 |
--tls-cert-key | -k | TLS_CERT_KEY | TLS 私钥文件 |
--open | -O | OPEN | 启动时用 xdg-open 打开浏览器 |
提供证书与私钥时,TLS 服务器与明文服务器可并行启动(--tls-only 则只启动 TLS)。同时提供明文与 HTTPS 时必须用 --tls-port 指定与 --port 不同的端口。
若配置文件来自本地文件,serve 会监听该配置文件本身(无防抖):thurgio.yaml 变更时全量重载配置并链式触发重建(成功才重建,失败保留旧配置仅记日志)。模板与 sibling YAML 的变更不在 serve 的监听范围内——需要模板热更新请用 --dev 模式(每次渲染前重载模板)或 thurgio build --watch。
app(公开端点)
/:Hello world!/v: 版本字符串(get_version():NAME VERSION (GIT_BUILD))/health: JSON 健康状态/ready: 就绪探针/metrics: Prometheus 指标/favicon.svg: 编译期嵌入的站点图标,亮色方案(assets/favicon.svg,image/svg+xml)/favicon-dark.svg: 站点图标,暗色方案(image/svg+xml)/icon-app.svg: 渐变 App 图标(Dashboard 头部 brand 标记,image/svg+xml)/app/login: 登录页(GET)与令牌校验(POST JSON{"token": ...})
app_th(受保护端点)
令牌通过 ?token= 查询参数或 Authorization: Bearer 请求头传递,按作用域分组(Read/Admin)。
Read 作用域:
/app/read/l?name=: 更新数据后渲染 provider(name为必填查询参数;支持请求级规则覆盖与tpl=autoUA 自适应,见 API 参考)。/app/read/r?name=: 更新数据后渲染 snippet(name为必填查询参数)。/app/read/ui: 后台更新,渲染ui.tpl(上下文含当前令牌scope)。/app/read/c: 渲染完整 Tera 上下文为 JSON(含scope,剔除config内proxies/content)。/app/read/events: SSE 构建通知(build_complete,15s keep-alive)。/app/read/health: 订阅健康度聚合(熔断器状态/失败计数/最后更新时间,只读)。/app/read/subs: 订阅商店聚合清单(provider 带解析后节点数与流量元数据,snippet 在后)。/app/read/meta?name=: 单个 provider 的流量元数据(解析subscription-userinfo头)。/app/read/history?name=&limit=: provider 快照时间序列(节点数 + 流量元数据)。/app/read/history/diff?name=&from=&to=: 对比时间序列中两个快照。/app/read/latency?name=: 逐代理 TCP 连接延迟测试(按需、不缓存)。/app/read/udp?name=: 逐代理 UDP 生效标志(静态能力报告)。/app/read/geo?name=: 入口落地检测(DNS 解析 + GeoIP 查询)。/app/read/unlock?services=: 流媒体解锁探测(netflix/youtube/disney)。
Admin 作用域:
/app/admin/s: 通过prov/url请求头更新提供者来源并强制刷新。/app/admin/p: params,请求携带的 queries 会深度合并覆盖当前内存中的 params。/app/admin/adhoc: 创建临时 provider/snippet(不写入配置,缓存持久化到 DB)。/app/admin/write: 执行完整构建(更新 + 渲染 + 写盘)。/app/admin/refresh: 无视缓存强制更新 providers/snippets(不写盘)。/app/admin/reload: 重载所有 templates。/app/admin/save: 持久化所有响应到数据库。/app/admin/dev?value=: 设置 dev 状态(true/false)。/app/admin/gist: 把配置声明文件备份为 GitHub 私密 Gist。/app/admin/shorten?url=: 经配置的短链接服务缩短 URL。
query
当携带参数请求接口时,比如 example.com/app/read/l?name=example&udp=true&port=9090,除 token 与 name 外的 queries 会被转换为 YAML 值(true → 布尔、9090 → 数字),作为 hp 注入模板——请求参数与配置参数分离:p 仅来自配置(params.yaml + /app/admin/p 热更新),hp 携带本次请求的查询参数。点分键(如 dns.enabled)在 hp 中写入嵌套结构(hp.dns.enabled)。单次请求参数超过 100 个会被拒绝。配置中的 params 可在模板中用 config.params 访问。
headers
访问 l / r 时:
- 转发提供者响应中的
subscription-userinfo头(snippet 转发请求头中的同名头)。 - 携带
d=config.yaml查询参数时,返回Content-Disposition: attachment; filename="config.yaml"下载头。
init
alias: i
在目标目录生成一份最小可用的配置脚手架。init 只需要 logger,不需要已有配置,因此可以在任意空目录中直接运行。
参数
| 参数 | 默认值 | 说明 |
|---|---|---|
-d, --dir <DIR> | 当前目录 | 目标目录(不存在时自动创建,支持嵌套路径) |
-f, --force | false | 覆盖已存在的文件 |
生成的文件
<dir>/
├── thurgio.yaml # 主配置(setting 节)
├── provider.yaml # provider 定义
├── filter.yaml # 过滤规则
├── rename.yaml # 重命名规则
├── params.yaml # 模板参数
├── snippet.yaml # snippet 定义
└── tpls/
├── base.tpl # 最小示例模板(provider 渲染入口)
└── ui.tpl # Dashboard 页面模板(serve 自带 UI 依赖它,勿删除)
注意:
- 不会生成
ruleset.yaml——它是唯一“缺失合法“的 sibling 配置文件,需要时手动创建即可(见配置参考)。 - 任一目标文件已存在且未指定
--force时报错退出;使用--force会用内置模板覆盖所有文件。 - 生成的
provider.yaml中带有一个指向本地示例的 provider 条目,可直接作为起点修改。
示例
# 在当前目录初始化
thurgio init
# 在指定目录初始化(目录不存在时自动创建)
thurgio init --dir ~/thurgio-config
# 已有文件时强制重新生成(覆盖为全新脚手架)
thurgio init --force
check
alias: c
校验配置而不构建:解析完整配置(thurgio.yaml + 全部 sibling YAML)后打印摘要与逐项检查结果。不访问网络、不写任何文件,适合放在 CI 或 cron 中做配置回归检查。
参数
无专属参数。支持全局参数 -c/--config 与 -r/--root。
输出内容
✅ Configuration is valid
Summary:
Providers: 2
Snippets: 1
Outputs: 3
Tokens: 2 configured
Address: 0.0.0.0
Port: 10100
Ttl: provider=600s, snippet=86400s
Provider 'example': OK (https://example.com/sub)
Provider 'local': OK (file: prov/example.yaml)
| 检查项 | 说明 |
|---|---|
| Summary | provider / snippet / 具名输出数量、令牌总数、监听地址/端口、TTL |
| Provider 来源 | URL 源检查 http(s):// 前缀格式;本地文件源检查文件是否存在 |
| 规则引用 | provider 的 pre_rename / filter / rename 规则名是否在 filter.yaml / rename.yaml 中定义 |
| 输出目录 | 所有配置输出路径的父目录是否存在 |
WARNING 类型
解析期未知规则名会被静默忽略(不参与管线),check 把它们以 WARNING 形式报告出来:
Provider 'example' filter rule 'us': WARNING (not defined in filter.yaml)
Provider 'example' pre_rename rule 'typo_name': WARNING (not defined in rename.yaml)
Provider 'local': WARNING (file not found: prov/example.yaml)
Output directory 'output/proxies.yaml': WARNING (parent directory does not exist)
- URL 格式异常 — 来源不是
http:///https://开头却被当作 URL 解析。 - 文件不存在 — 本地文件源指向的路径不存在。
- 规则未定义 — 引用的规则名在对应表中不存在(最常见的静默失效来源)。
- 输出父目录不存在 — 构建时写入会失败。
WARNING 不影响退出码(配置仍视为有效),但通常意味着运行时行为与预期不符。
示例
# 校验当前目录配置
thurgio check
# 校验指定配置
thurgio -c /etc/thurgio/thurgio.yaml check
diff
alias: d
两种对比模式:对当前配置的输出文件做文本级 diff(file,默认),或对两个订阅源做节点级 diff(nodes)。
diff file(默认模式)
thurgio diff [file] [NAME]
bare thurgio diff 等价于 thurgio diff file。
执行过程:
- 读取
setting.output中配置的所有输出文件的当前内容(不存在的文件视为空内容)。 - 执行一次强制构建(等价
build --force,会真实访问订阅源)。 - 逐文件打印新旧内容的行级差异:
-删除行、+新增行、空格前缀为未变更行。
| 参数 | 说明 |
|---|---|
NAME | 可选——只对比路径中包含该子串的输出文件 |
# 对比所有输出
thurgio diff
# 只对比路径含 example 的输出
thurgio diff file example
NAME匹配的是输出路径子串,不是 provider 名;但输出路径通常包含 item 名,按名过滤在多数场景下等效。
diff nodes
thurgio diff nodes <from> <to>
节点级对比两个订阅源:
http(s)://开头视为 URL,其余视为本地文件路径。- 格式自动检测(Clash / V2RayN / SIP002 等),不套用规则管线——对比的是原始节点。
- 不可解析的条目被丢弃并记 debug 日志。
输出分组
1 added, 0 removed, 1 changed, 1 renamed, 1 unchanged
+ HK-01 (trojan 9.9.9.9:443)
~ US-01: ss 1.2.3.4:8388 → ss 1.2.3.4:9000
↻ TW-01 → TW-02 (ss 6.6.6.6:8388)
| 分组 | 标记 | 颜色 | 判定 |
|---|---|---|---|
| added | + | 绿 | 仅新源中存在 |
| removed | - | 红 | 仅旧源中存在 |
| changed | ~ | 黄 | 同名但指纹不同 |
| renamed | ↻ | 黄 | 指纹相同但名称不同 |
| unchanged | — | — | 完全一致 |
指纹 = 去掉 name 字段后的全部序列化字段(type/server/port/密码等),任一变化即视为 changed。全部无变化时整行绿色输出 N unchanged。颜色遵循 RUST_LOG_ANSI / NO_COLOR。
示例
# 对比升级订阅前后的节点变化
thurgio diff nodes prov/old.yaml https://example.com/sub
# 对比两个本地文件
thurgio diff nodes prov/a.yaml prov/b.yaml
config
alias: cfg
备份 / 回滚配置声明文件。不加载配置、不访问网络、不碰数据库——即使当前配置已经改坏也能运行,是“改配置前留后路“的安全网。
备份范围
| 内容 | 说明 |
|---|---|
thurgio.yaml | 主配置 |
provider.yaml / params.yaml / rename.yaml / filter.yaml / snippet.yaml | 5 个 sibling 声明文件 |
tpls/ | 模板目录(递归复制;符号链接与特殊文件跳过) |
只复制实际存在的文件(缺失的声明文件跳过,部分配置按现状备份);ruleset.yaml 不在备份清单中。
backup
thurgio config backup [-d, --dir <DIR>]
| 参数 | 默认值 | 说明 |
|---|---|---|
-d, --dir | <root>/backups/<YYYYMMDD-HHMMSS> | 备份目标目录 |
- 目录名为本地时间戳;同一秒内多次备份自动追加
-1、-2后缀。 - root 中找不到任何声明文件时报错并清理空目录。
- 成功输出
✔ Backup created: <路径>。
restore
thurgio config restore <target>
| 参数 | 说明 |
|---|---|
target | 备份名(backups/ 下的子目录名)、绝对路径或相对 root 的路径(含路径分隔符时按路径解析) |
执行过程:
- 校验目标存在且包含至少一个声明文件(否则报错,不触碰当前状态)。
- 先把当前状态自动备份到
backups/<时间戳>(保险)——当前状态为空则跳过。 - 覆盖写回声明文件与
tpls/:只恢复备份中包含的内容,不删除 root 中的其他文件。
安全护栏:拒绝把 root 目录自身当作备份源(防止自我复制循环)。
成功输出 ✔ Restored from '<target>' (current state backed up at <路径>)。
list
thurgio config list
列出 backups/ 下所有备份目录(名称 + 本地时间修改时间),最新在前:
Available backups in /root/path/backups:
20260814-153000 2026-08-14 15:30:00
20260814-120000 2026-08-14 12:00:00
示例
# 改配置前备份
thurgio config backup
# 改坏了,回滚
thurgio config list
thurgio config restore 20260814-153000
upgrade
alias: u
检查 GitHub Releases 是否有新版本。只检查并提示,不会自动下载或安装。
参数
| 参数 | 别名 | 默认 | 说明 |
|---|---|---|---|
--yes | -y | false | 有新版本时额外打印发布页下载链接 |
行为
- 查询
https://api.github.com/repos/xylylab/thurgio/releases/latest(10 秒超时)。 - 以 semver 比较当前版本与最新 tag(忽略
v前缀)。
有新版本时:
Update available: v0.18.0 → v0.19.0
Release: https://github.com/xylylab/thurgio/releases/tag/v0.19.0
加 --yes 再打印一次 Download the latest release from: 与链接(便于脚本抓取)。已是最新时输出:
Already up to date (v0.18.0)
升级安装方式见部署指南。
Generate
alias: g
生成 shell 补全脚本。
参数
-s, --shell <shell>: 目标 shell,默认bash。支持 bash/zsh/fish 等(clap_complete::Shell)。环境变量SHELL。-o, --output <output>: 输出文件路径;不指定时输出到 stdout。环境变量OUTPUT。
示例
# Bash
thurgio generate --shell bash > /etc/bash_completion.d/thurgio
# Zsh
thurgio generate --shell zsh > /usr/local/share/zsh/site-functions/_thurgio
# Fish
thurgio generate --shell fish > ~/.config/fish/completions/thurgio.fish
# 直接写入文件
thurgio generate -s fish -o ~/.config/fish/completions/thurgio.fish
version
alias: V
打印版本信息。与 generate 一样不需要 logger 与配置,可在任何目录直接运行。
参数
| 参数 | 别名 | 说明 |
|---|---|---|
--json | -j | 以 JSON 输出 |
示例
$ thurgio version
thurgio 0.18.0
$ thurgio version --json
{"name":"thurgio","version":"0.18.0"}
服务器端对应 GET /v 端点(含 git 快照后缀时两者一致),见 API 参考。
Config
首先, 我们需要一个配置文件 thurgio.yaml。setting 顶级节包含服务器设置。
tokens
API 令牌,按作用域分组。至少配置一个令牌(否则校验失败)。
setting:
tokens:
admin:
- "admin-token" # Admin 作用域:可访问全部 /app/ 端点
- "${THURGIO_ADMIN_TOKEN}" # 环境变量引用(可选)
read:
- "read-token" # Read 作用域:仅可访问只读渲染端点
令牌通过 ?token= 查询参数或 Authorization: Bearer 请求头传递。
环境变量引用
token 值可写成 ${NAME} 形式,解析期从进程环境读取并替换(自托管免明文存 token)。仅当整个值恰好为 ${NAME} 时展开(如 prefix-${NAME}、${NAME}suffix 均保留字面);引用的环境变量未定义时配置解析报错。仅 token 值支持展开,其余配置字段不展开。
address
服务监听地址,默认 0.0.0.0。serve 启动时可用 -a/--address 或 ADDRESS 环境变量覆盖。
port
服务监听端口,默认 10100。serve 启动时可用 -p/--port 或 PORT 环境变量覆盖。端口为 0 时使用随机端口。
udp
对于节点的 udp 字段处理逻辑。
None: 不处理False: 设为 falseTry: 如果代理类型支持 udp 则启用(默认)True: 设为 true
mode
emoji 模式(全局)。
Disable: 不添加 emoji 前缀Enable: 检测到国家/地区时添加旗帜 emoji 前缀(默认)Hide: 去除代理名称中已有的 emoji 前缀
output
渲染结果的默认输出路径,按渲染目标 + 模板名分组(键为 provider/snippet,每个桶内再按模板名映射到目录列表):
setting:
output:
provider:
"clash/base.tpl":
- "prod/list/"
snippet:
"rule.tpl":
- "prod/rules/"
item 默认输出写入 {dir}/{key}.{ext}(key = item 名,ext 来自 ext 字段或参数 p.ext),目录取 item 自己 target 桶 + 自己模板名对应条目;未配置则无默认输出。
旧版的扁平目录列表写法(如
provider: ["prod/"])已不再支持,解析会直接报错(ADR-080)。
ttl
缓存有效期(秒)。
provider: 最小60,默认600(10 分钟)snippet: 最小300,默认86400(24 小时)
缓存未过期时不会重新获取。
concurrency_limit
并发任务限制(渲染/持久化等任务的并发数),最小 1,默认 10。可用环境变量 THURGIO_CONCURRENCY_LIMIT 覆盖。更新管道的抓取阶段为无界并发(每个 item 同时启动,ADR-073),不受此限制约束。
cors_origins
允许的 CORS 来源列表,默认 ["*"]。生产环境建议限制为具体域名;来源列表每请求从 live config 读取,配置热重载后立即生效。
adhoc
临时 provider/snippet(/app/admin/adhoc 端点)的默认行为:
setting:
adhoc:
tpl:
provider: "tpls/adhoc_nodelist.tpl" # 临时 provider 缺省模板
snippet: "tpls/adhoc_snippet.tpl" # 临时 snippet 缺省模板
max_items: 100 # 数量上限,超出淘汰最旧创建者
ttl_seconds: 86400 # 清理 TTL:自最后访问起算(秒)
| 字段 | 默认值 | 说明 |
|---|---|---|
tpl.provider / tpl.snippet | 无 | 请求未传 tpl 参数时的回退模板;两者都缺且请求也未指定时 → 400 |
max_items | 100 | 临时资源数量上限(内存与 DB 一致清理) |
ttl_seconds | 86400 | 滑动 TTL——自最后访问时间起算 |
example
thurgio.yaml
setting:
tokens:
admin:
- token
# address: 0.0.0.0 # $ADDRESS
# port: 10100 # $PORT
# udp: Try
# mode: Enable
# output:
# provider:
# "base.tpl":
# - output/example.yaml
# ttl:
# provider: 600
# snippet: 86400
# concurrency_limit: 10
# cors_origins:
# - "*"
Provider
provider 是代理订阅实体:从 source 获取 → 解析 → pre_rename → filter → rename → 按 template 渲染 → 写入 filename。
字段总览
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 否 | 显示名,未指定时用键名 |
type | string | 是 | 输入配置格式,见下表 |
source | string | 是 | URL(http(s):// 开头)或本地文件路径 |
template | string | 是 | 主输出模板(相对模板目录) |
filename | string 列表 | 否 | 渲染结果写入的文件列表 |
pre_rename | 规则名列表 | 否 | 解析后、过滤前应用(引用 rename.yaml) |
filter | 规则名列表 | 否 | 过滤阶段应用(引用 filter.yaml) |
rename | 规则名列表 | 否 | 过滤后应用(引用 rename.yaml),应用前后均检查重名 |
emoji | bool | true | 是否执行 emoji 逻辑 |
default_output | bool | 回退 p.default_output | 是否同时写默认路径文件 |
ext | string | 回退 p.ext | 默认输出文件的扩展名 |
outputs | mapping | 否 | 具名输出,见下文 |
p | 任意 YAML | {} | 自定义参数,模板中以 p.* 访问 |
type
输入配置格式:
clash— Clash YAMLmeta— Clash.Meta YAMLv2rayn— V2RayN 分享链接(Base64 JSON)sip002— SIP002 URI(逐行)surge— Surge 配置[Proxy]段qx/loon/singbox/sip008/ssdtelegram— TG 类 HTTP/SOCKS5 代理链接(tg://http/tg://socks/t.me/*)
auto不是合法的 providertype——它只用于渲染侧tpl=auto的 UA 自适应模板选择;输入格式必须显式指定。
source
untagged 枚举,纯字符串:
"https://..."或"http://..."— 远程 URL- 其余 — 本地文件路径(相对配置文件所在目录解析)
outputs(具名输出)
同一份代理列表经不同模板投影为多种格式。每个输出可独立指定:
example:
type: clash
source: "prov/example.yaml"
template: clash_list.tpl # 主输出
filename:
- prod/list/example.yaml
outputs:
base64_sub: # 具名输出 1
template: base64_sub.tpl
filename:
- prod/example_base64.txt
plain_list: # 具名输出 2
template: plain_list.tpl
filename:
- prod/example_plain.txt
p:
note: "human-readable" # 每个输出有自己的 p
- 输出渲染上下文与主输出一致:
l绑定所属 provider。 - 输出名跨所有 provider 全局唯一——重名在构建期报配置错误。
- 每个输出的
default_output/ext未设置时回退到自己的p同名键。
default_output 与 ext
默认输出路径 = <目录>/<item 名>.<ext>,其中目录取 setting.output 中“该 item 自己的 target 桶 + 自己模板名“对应条目(详见 setting.output)。未配置对应目录时不写默认输出。
example
provider.yaml
example:
name: example
type: clash
pre_rename:
- us
filter:
- us
rename:
- rm_brackets
source: "prov/example.yaml"
emoji: true
template: clash_list.tpl
filename:
- prod/list/example.yaml
p:
one: one
two: two
Filter
过滤规则(声明式语法)。在 filter.yaml 中定义,被 provider 的 filter 列表、请求级规则覆盖(/app/read/l)与模板 filter filter(rules= kwarg)按键名引用,依次应用。
每条规则值为三种形状之一:
- 裸字符串简写:
contain匹配任一|分隔关键字,忽略大小写(最常用) - 单个匹配器 mapping
- 匹配器 mapping 列表:顺序应用,代理必须命中全部(AND)
匹配器
| 键 | 值 | 说明 |
|---|---|---|
contain | 字符串或字符串数组 | 名称包含任一关键字(数组 = OR,等价 ` |
start | 字符串 | 名称以关键字开头 |
end | 字符串 | 名称以关键字结尾 |
type | 字符串 | 按代理协议类型过滤(ss, vmess, trojan 等) |
regex | 字符串 | 按正则匹配代理名 |
共享选项
| 键 | 缺省 | 说明 |
|---|---|---|
ignore_case | true | 忽略大小写 |
negate | false | 反转:命中则剔除 |
限制:列表(AND)仅支持 contain/start/end 匹配器;type/regex 只能单条使用。每个 mapping 必须恰好一个匹配器键。
example
filter.yaml
# 简写:保留名称包含任一关键字的代理(忽略大小写)
us: "美国|United States|American"
# 排除(negate)
rm_netease:
- contain: [netease, unblock, music]
negate: true
# 名称开头
hk_prefix:
- start: HK
# 类型过滤
vmess_only:
- type: vmess
# 正则
custom:
- regex: '\W+(US|us|United States)\W+'
# 多条规则顺序应用(AND)
strict:
- contain: [美国]
- start: HK
Rename
重命名规则,基于正则替换(regex::replace_all,支持 $1 反向引用)。在 rename.yaml 中定义,被 provider 的 pre_rename / rename 列表与模板 rename 过滤器(rules= kwarg)按键名引用。
规则形状
每条规则是一个二元素列表 [正则, 替换串],对代理名称执行全局替换:
us:
- "(.*)"
- "🇺🇸 $1"
- 正则语法为 Rust
regexcrate:支持\d、(?:...)、命名组(?<name>...)等,不支持回溯(lookahead/lookbehind 不可用)。 - 替换串中
$1/${name}引用捕获组;字面$写$$。
应用位置
| 阶段 | 配置键 | 时机 |
|---|---|---|
| 解析后 | pre_rename | 过滤前改名——先规范名称再筛选 |
| 过滤后 | rename | 过滤后改名——只对保留节点生效 |
两个阶段各自按列表顺序依次应用;应用后检查重复名称,重名报错。
示例
# rename.yaml
# 全体加国旗前缀
us:
- "(.*)"
- "🇺🇸 $1"
# 去掉括号前的 0:"香港 01(2)" → "香港 01 2"
rm_brackets:
- "0(\\d)"
- " $1"
# 房号规范化:"108-204" → "108/204"
room_number:
- "(\\d+)-(\\d+)"
- "$1-$2"
# 删掉倍率后缀
strip_rate:
- "\\s*[xX]\\d+(\\.\\d+)?$"
- ""
# 多条规则组合:先删信息素后加地区前缀
clean_and_tag:
- "剩余.*|到期.*|官网.*"
- ""
注意
- 未定义的规则名被静默忽略(不参与管线);用
thurgio check可发现这类悬空引用。 - 模板内可动态追加应用:
{{ l.proxies | rename(rules="rm_brackets") }}。 - YAML 中写正则时注意转义:
\d在双引号字符串中要写成"\\d"。
Snippet
snippet 是不依赖 provider 的独立渲染单元:从 source 获取内容 → 用 template 渲染(上下文绑定 s.response.content)→ 写入 filename。典型用途:规则集聚合、静态片段、模板化配置文件。
字段总览
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 否 | 显示名,未指定时用键名 |
template | string | 是 | 渲染模板(相对模板目录 THURGIO_TPL_DIR,默认 tpls/) |
source | string | 否* | URL(http(s):// 开头)或本地文件路径;内容进入 s.response.content |
filename | string 列表 | 否 | 渲染结果写入的文件列表 |
default_output | bool | 回退 p.default_output | 是否同时写默认路径文件 |
ext | string | 回退 p.ext | 默认输出文件的扩展名 |
p | 任意 YAML | {} | 自定义参数,模板中以 s.p.* 或 config.snippet.<key>.p 访问 |
* 无 source 的 snippet 也可用——模板仅依赖 config/p 等全局上下文时可以省略。
source
untagged 枚举,纯字符串:
"https://..."或"http://..."— 远程 URL- 其余 — 本地文件路径(相对配置文件所在目录解析)
与 provider 的区别
- 无解析与规则管线:内容原样作为文本进入模板,不做代理解析、不过滤/改名。
- 上下文不同:provider 绑定
l.proxies,snippet 绑定s.response.content。 - TTL 更长:默认缓存 24 小时(provider 默认 10 分钟),见 setting.ttl。
- snippet 的
p在其他 provider 模板中可经config.snippet.<key>.p读取——这是给其他模板传参数的常用手法(如clash.tpl遍历 snippet 读p.clash_url)。
example
snippet.yaml
example:
name: example
template: snippet.tpl
source: "rules/China.list"
filename:
- prod/list/example.yaml
p:
one: one
two: two
Ruleset
可选 sibling 文件 ruleset.yaml(唯一缺失合法的 sibling——其余 5 个缺失即报错),按名称定义命名规则集,供模板 ruleset 过滤器经 _rules.ruleset 上下文面消费(ADR-086/088)。
条目形状
每条目值为两种形状之一:
- 行列表(本地形态)——规则行数组
- URL 字符串(远程形态)——
http(s)://开头;其他字符串在解析期报错
# ruleset.yaml
ads:
- DOMAIN-SUFFIX,ads.example.com
- DOMAIN-KEYWORD,tracker
cnlist: https://example.com/cn.list
远程条目语义
- 由更新管线定时拉取并物化进
_rules.ruleset面。 - TTL 24 小时内命中缓存不重拉;URL 变更或强制刷新时重拉。
- 拉取失败回退上次成功文本并递增
ruleset_fetch_failed_total计数器。 - 同名冲突时本地声明优先。
模板消费:ruleset 过滤器
{{ l.proxies | ruleset(name="ads") }} {# → Clash 规则行 #}
{{ l.proxies | ruleset(name="ads", format="surge") }} {# → Surge 规则行 #}
{{ l.proxies | ruleset(name="ads", format="clash-classical") }} {# → Clash classical rule-provider 文档(payload: 序列)#}
| 参数 | 缺省 | 说明 |
|---|---|---|
name | 必填 | 规则集名(过滤器输入值被忽略) |
format | clash | clash / surge / clash-classical |
policy | 🚀 Node Select | Surge 格式的路由策略后缀 |
text | 无 | 内联规则行文本,覆盖其他一切来源 |
来源优先级:内联 text > _rules.ruleset 命名表(本文件)> 内置 assets/rulesets/*.list 资产。未定义的规则名会报渲染错误(与 filter/rename 的静默忽略不同)。
Params
全局自定义参数,供模板通过 p 面访问。在 params.yaml 中定义,也可放在 thurgio.yaml 的 params 顶级节(两者合并)。
值可以是任意 YAML 结构(标量、映射、列表),模板中按路径访问:
# params.yaml
mixed-port: true
dns:
enable: true
mode: fake-ip
format: json
test:
interval: 60
url: https://cp.cloudflare.com/generate_204
mixed-port: {{ p["mixed-port"] }}
dns-mode: {{ p.dns.mode }}
interval: {{ p.test.interval }}
参数面:p 与 hp 的分离
模板中有两个独立的参数面(ADR-081):
| 面 | 来源 | 可变性 |
|---|---|---|
p.* | 配置:params.yaml + /app/admin/p 端点热更新 | 进程生命周期内持久 |
hp.* | 本次 HTTP 请求的查询参数(YAML 解析、点分键嵌套) | 单次请求 |
HTTP 请求的查询参数不会覆盖 p 中的同名值——订阅方无法篡改你的配置参数;需要请求级差异时在模板中显式消费 hp。
热更新
Admin 令牌请求 /app/admin/p?key=value 会把查询参数深度合并进内存中的 params(点分键写入嵌套结构),立即对后续渲染生效,不写回配置文件:
curl "http://localhost:10100/app/admin/p?test.interval=120&token=ADMIN_TOKEN"
example
见上方 YAML 示例;完整参数面语义(解析规则、100 个上限等)见 API 参考。