Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

详细说明请参阅快速开始指南


链接


致谢

快速开始

从零开始运行 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/

从预编译二进制安装

  1. 从 Releases 页面下载适用于你操作系统/架构的压缩包。
  2. 解压并将 thurgio 二进制文件放到 $PATH 中的某个目录(例如 /usr/local/bin)。

验证

thurgio --version

快速初始化

使用一条命令生成最小可用配置:

thurgio init

这会在当前目录创建以下文件:

  • thurgio.yaml — 主配置(setting
  • provider.yaml / filter.yaml / rename.yaml / params.yaml / snippet.yaml — 各配置节(均可留空,按需填充)
  • tpls/base.tpltpls/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.snippetconfig.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 个 cratethurgio-bench 为仅开发用的基准 crate,fuzz 为 workspace 成员,共 23 crates/* + fuzz),分布在 5 层中,具有严格的依赖方向。上层依赖于下层的抽象;下层从不依赖上层。

关于详细的接口合约和 ADR,请参阅 design/architecture-detailed.mddesign/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>FieldsThurgioError + ErrorKindUdpUpdateState)与包身份标识(identity 模块:NAMEVERSIONGIT_BUILDget_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)、来源抽象(SourceFile/Url)、AppHttpClient trait。
  • thurgio-event — 进程内部事件总线(EventBusDomainEventEventHandlerEnvelope)。使用 tokio::sync::broadcast,慢订阅者会丢失事件。
  • thurgio-test-utils — 集成测试工具(测试应用工厂、测试配置构建器)。

领域层

  • thurgio-domain — 核心业务类型:Provider/RawProviderSnippetOutput/RawOutput(provider 具名输出),以及更新生命周期(UpdateStateCache/Lifecycle/Parsable trait,proxies_version 版本化 + cached_json 缓存)。
  • thurgio-proxy — 25 种代理协议类型(SS、VMess、VLESS、Trojan、Hysteria(2)、TUIC、AnyTLS、ShadowTLS、Juicity、Naive、Ssh、Mieru 等),支持解析(ConfTypeclash/meta/v2rayn/sip002/surge/qx/loon/singbox/sip008/ssd/telegram)和序列化;ProxyFormat<F> trait(marker V2rayN/Sip002/Surge/Qx/Loon)提供格式解析分发,formats.rs 能力表为单一事实源。全部协议无条件编译。
  • thurgio-formats — 共享行解析辅助(Surge/QX/Loon 行解析抽离,供 proxy 与 renderer 复用)。
  • thurgio-proxy-singbox — Sing-box 特定的代理类型转换(CoreSingBoxToSingBox trait,SProxyTy::supports 单一能力源)。
  • thurgio-filter — 代理过滤(Filter/RawFilter/Mode)、重命名(Rename)、emoji 模式(EmojiMap,aho-corasick)与规则集(RuleSet/RuleFormatassets/rulesets/*.list 内置表)。
  • thurgio-script — 内联 JS 引擎(ScriptEngine::transform,boa_engine,js 过滤器薄包装)。
  • thurgio-renderer-api — 轻量级 API 合约:RenderTarget/Render trait + 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 在 server ua.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 — 轻量纯配置类型(SettingTtlTokenScopeTokensEmojiModeAdhocTplSetting),零领域/代理/过滤依赖。

数据访问层

  • 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_contextbuild_render_context/build_base_context,ADR-057)为渲染装配单一入口。AppConfigAr<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/versionspawn_config_watcher 统一监听 thurgio.yaml + 6 siblings + tpls/)。依赖 thurgio-app 外观 + thurgio-proxy::diff(节点级 diff)。
  • thurgio-server — 基于 Axum 的 HTTP 服务器(build_router 导出,12 read + 10 admin 端点,tpl=auto UA 自适应,SSE 事件流,限流/CORS/指标/认证中间件,静态资源 include_str! 嵌入)。

统一错误类型

ThurgioError(位于 thurgio-types)使用 thiserror 派生枚举,配套 ErrorKind 分类(Copy,用于状态码映射与重试决策):

ErrorKind含义HTTP可重试
Network网络请求失败(reqwest::Error502
Timeout请求超时502
Persistence数据库错误500
Parse数据解析失败422
Config配置错误422
Render模板渲染失败500
NotFound资源未找到404
Validation字段验证失败422
Unauthorized认证失败401
CircuitOpen熔断器打开503
Canceled操作取消499
Internal内部错误(anyhow::Error500

trace_id 通过外部包装 TracedError 携带,不嵌入 ThurgioError。详见 design/error-handling.md

事件系统

事件总线实现解耦通信:

  • 7 种事件RebuildRequestedProviderUpdatedSnippetUpdatedBuildCompletedRendererReloadedConfigReloaded(配置全量重解析并原子替换,ADR-048)、ServerEvent
  • 3 个默认订阅者AutoBuildHandler(RebuildRequested → 强制重建,仅重跑更新+渲染管线、不重载配置)、LoggingHandler(所有事件 → INFO/WARN)、WebhookHandler(BuildCompleted → 向 THURGIO_WEBHOOK_URL POST JSON,未设置时为空操作)

管道抽象

更新与渲染管道由阶段(stage)构成:

  • 更新管道thurgio-app-update):UpdateItemsStage(providers)→ UpdateItemsStage(snippets)
  • 渲染管道thurgio-app-build):providers(主输出 + 具名输出)与 snippets 两个阶段并发执行(共享不可变上下文,无共享可变状态)

特性门控

thurgio-proxy 不再按协议门控(25 种协议全部无条件编译),full/filters 拆分已移除(ADR-064),thurgio-domainupdate/render-impl/db-impl 门控亦移除:

特性说明
test-utils测试示例构造器(开发/测试用,仅 thurgio-proxy/thurgio-request 定义,thurgio-test-utils 消费 16 例)

thurgio-cli 另有两个互斥的 TLS 后端特性:rustls-tls(默认)与 native-tls,通过 thurgio-appthurgio-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

TraitCrate用途
Stored, EntityStore, SqliteEntityStorethurgio-db存储模型关联 / 类型化实体存取
KvStorethurgio-db键值持久化(SQLite)
Proxy, ConfType, ProxyFormatthurgio-proxy代理类型定义与格式解析分发(marker V2rayN/Sip002/Surge
CoreSingBox, ToSingBoxthurgio-proxy-singboxSing-box 转换
EventHandler, EventBusthurgio-event事件订阅与分发
Render, RenderTargetthurgio-renderer-api渲染数据合约(无 Tera 依赖)
Renderable, Render, AppRenderEnginethurgio-renderer模板渲染执行

设计原则

  1. 单一职责 — 每个 crate 有一个清晰的用途。
  2. 依赖反转 — 上层依赖于抽象。
  3. 关注点分离 — 各层干净分离。
  4. 外观模式 — 接口层 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):

类型描述
clashClash YAML 格式
metaClash.Meta YAML 格式
v2raynV2RayN 分享链接格式(Base64 JSON 数组)
sip002SIP002 URI 格式(逐行 ss:// 等)
sip008SIP008 JSON 订阅格式(Shadowsocks Android,version+servers
surgeSurge 配置格式(解析 [Proxy] 段)
qxQuantumult X 节点行(shadowsocks=/vmess= 等 7 种)
loonLoon 节点行(10 种)
ssdSSD 订阅格式(ssd:// Base64 JSON 文档)
singboxsing-box 配置 JSON(解析 outbounds 数组,基础字段,含 wireguard/naive/ssh
telegramTG 类 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字符串代理类型(ssvmess 等)
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.proxiesprovider、具名输出代理对象列表
l.nameprovider、具名输出当前 provider 的名称
l.renameprovider、具名输出当前 provider 的重命名规则(键名)
l.filterprovider、具名输出当前 provider 的过滤规则(键名)
s.response.contentsnippet当前 snippet 的源内容(原样文本);adhoc snippet 绑定为 content
p.*全部来自配置的自定义参数(params.yaml + /app/admin/p 热更新)
hp.*全部本次 HTTP 请求的查询参数(与 p 分离;无请求参数时为 {}
config全部完整配置对象(config.snippetconfig.paramsconfig.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;MATCHfinal
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=...)按配置的过滤规则动态过滤代理(rulesfilter.yaml 中的规则名,逗号分隔或数组,_rules.filter 表)
rename(rules=...)按配置的重命名规则动态改名(rulesrename.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.yamlprovider合并空
params.yamlparams合并空
rename.yamlrename报错(必须存在空或有内容)
filter.yamlfilter报错
snippet.yamlsnippet合并空
ruleset.yamlruleset合并空(唯一缺失合法的 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当前目录
FORCEbuild 强制更新(-f/--forcefalse
WATCHbuild 监听模式(-w/--watchfalse
NOTIFYbuild 完成时发送系统通知(-N/--notifyfalse
SHELLgenerate 补全目标 shell(-s/--shellbash
OUTPUTgenerate 输出文件(-o/--outputstdout
ADDRESSserve 监听地址(-a/--address配置 setting.address(默认 0.0.0.0
PORTserve 监听端口(-p/--port配置 setting.port(默认 10100
DEVserve 开发模式(-d/--devfalse
IDLEserve 空闲模式(-i/--idle):无请求时自动退出false
EVICTserve 空闲时清空数据缓存(-e/--evicttrue
TLS_ONLYserve 仅提供 HTTPS(-s/--tls-onlyfalse
TLS_PORTserve TLS 端口(-t/--tls-portPORT
TLS_CERTTLS 证书路径(-c/--tls-cert
TLS_CERT_KEYTLS 私钥路径(-k/--tls-cert-key
OPENserve 启动时打开浏览器(-O/--openfalse
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_ALPHAbacklog EMA 平滑系数300
THURGIO_WEBHOOK_URL构建完成通知 webhook(POST JSON)无(禁用)
EVICT_TIMEOUT空闲后清空数据缓存的等待时间(秒,--evict 开启时)10
IDLE_TIMEOUT空闲模式退出等待时间(秒,--idle 开启时)30
DATABASE_URL数据库文件路径thurgio.db
REQUEST_TIMEOUTHTTP 客户端请求超时(秒)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=debuginfo
RUST_LOG_ANSI是否启用日志颜色(true/false自动检测(stdout 为 TTY)
RUST_LOG_FORMAT日志格式(json 启用 JSON 输出)文本
RUST_INVALID_TLS跳过 TLS 证书校验(仅测试用)false
OTEL_EXPORTER_OTLP_ENDPOINTOTLP 导出端点(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.yamloutputssnippet.yaml 条目已引用它们,thurgio build 一次产出所有格式。

模板适用输出格式说明
base.tplproviderClash YAML最小模板(thurgio init 生成),json_str 逐节点输出
clash.tplproviderClash YAML紧凑 JSON 节点 + test 块 + rule-providers(遍历 config.snippetp.clash_url
clash_groups.tplproviderClash YAMLp.groups 规则名动态生成 proxy-groupsfilter(rules=...) 实时筛选成员)
clash_list.tplproviderClash YAMLclash.tpl 变体:头部注释打印 l.rename / l.filter
surge.tplproviderSurge 配置手写 [Proxy] 段(for 循环 + 字段展开,教学向)
surge_proxy.tplproviderSurge 配置surge_proxy 过滤器版 [Proxy] 段({{ l.proxies | surge_proxy() }}
surge_full.tplproviderSurge 完整配置[General] / [Proxy] / [Proxy Group] / [Rule] 四段齐全,可直接订阅导入(surge_proxy 节点行 + select/url-test 组 + p.rules 规则聚合,首行 #!MANAGED-CONFIG
v2rayn.tplprovider纯文本每行一个 vmess:// 分享链接(v2rayn 过滤器)
sip002.tplprovider纯文本每行一个 ss:// 分享链接(sip002 过滤器)
sip008.tplproviderJSONSS Android 官方订阅:version + servers 数组(sip008 过滤器,仅 ss 节点)
ssd.tplprovider纯文本ssd:// 订阅:UrlSafe Base64 JSON(ssd 过滤器,仅 ss 节点)
shadowrocket.tplprovider纯文本每行一个分享链接(shadowrocket 过滤器,复用 sip002/v2rayn 转换,wg 跳过)
base64_sub.tplproviderBase64机场式订阅:v2rayn + sip002 链接整体 base64(mode="encode") 编码
plain_list.tplprovider纯文本人类可读节点清单:名称 | 类型 | 服务器:端口(for 循环 + 字段访问)
singbox.tplprovidersing-box JSON完整 sing-box 配置:sibox 转 outbounds + direct/block
singbox_rules.tplsnippetsing-box JSONclash 规则文本 → sing-box rule 数组(clashbox 过滤器),与 singbox.tpl 组合成完整配置
qx.tplproviderQX 节点行每行一个 QX 节点(qx 过滤器,ss/ssr/vmess/vless/trojan/http/socks5)
qx_config.tplproviderQX 完整配置完整 Quantumult X 配置:[general]/[dns]/[server_local]/[policy]qx_config 过滤器)
loon.tplproviderLoon 节点行每行一个 Loon 节点(loon 过滤器,10 种类型)
loon_config.tplproviderLoon 完整配置完整 Loon 配置:[General]/[Proxy]/[Proxy Group]/[Rule]loon_config 过滤器)
mellow.tplproviderMellow JSONMellow/V2Ray-core JSON:socks inbound + per-proxy outbounds(mellow 过滤器,ss/vmess/vless/trojan/http/socks5 + TCP/WS/TLS)
mixed.tplprovider混合分享链接混合单链接列表:逐节点 V2RayN 优先、SIP002 回退,mixed 过滤器
rules_aggregate.tplsnippetQuantumult X 规则集多规则集聚合:拼接 config.snippet 中各规则集内容 → uniq 去重 → sort_domain_ip 排序 → quantumult 输出(s.p.rules 指定要聚合的 snippet 名,s.p.policy 指定策略);换 clash / surge 过滤器即得对应格式规则列表
snippet.tplsnippet原样原样输出 snippet 内容(s.response.content
rules_pipeline.tplprovider教学演示模板内 filter → rename → v2rayn/sip002/cla 过滤器链
date.tpl片段文本{% include %} 引入的生成时间注释
ui.tplHTMLDashboard 页面(服务器自带,勿删除)
auto/*.tplproviderUA 自适应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.yamltemplate: 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.yamloutputs 中挂载(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.yamlclash_list.tplClash
prod/example.yamlclash.tplClash
prod/example_groups.yamlclash_groups.tplClash(动态 proxy-groups)
prod/example_surge.txtsurge.tplSurge(手写 [Proxy] 段)
prod/example_surge_proxy.txtsurge_proxy.tplSurge(过滤器版 [Proxy] 段)
prod/list/example_v2rayn.txt / prod/example_v2rayn.txtv2rayn.tpl分享链接
prod/list/example_sip002.txt / prod/example_sip002.txtsip002.tpl分享链接
prod/example_sip008.jsonsip008.tplSS Android JSON 订阅
prod/example_ssd.txtssd.tplssd:// 订阅
prod/example_shadowrocket.txtshadowrocket.tplShadowrocket 分享链接
prod/example_base64.txtbase64_sub.tplBase64 订阅
prod/example_plain.txtplain_list.tpl纯文本节点清单
prod/example_singbox.jsonsingbox.tplsing-box 完整配置
prod/example_singbox_rules.jsonsingbox_rules.tplsing-box 规则数组
prod/example_china.listsnippet.tpl原样规则集
prod/example_quantumult.txtrules_aggregate.tplQuantumult X 规则集

挂载新模板:在 provider.yamloutputs(或 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_configcheck_url=, dns=, group={{ l.proxies | qx_config(group="Proxy") }} — 完整 QX 配置四段
loon{{ l.proxies | loon() }} — Loon 节点行(10 种类型)
loon_configdns=, group={{ l.proxies | loon_config(group="Proxy") }} — 完整 Loon 配置四段
mellowport=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)
clameta=true{{ l.proxies | cla(meta=true) | json_str() }} — 过滤出 clash 兼容节点
proxy_suffixsuffix="..."{{ l.proxies | proxy_suffix(suffix="HK") }} — 给节点名加后缀
jssrc="..."{{ l.proxies | js(src="return $input.map(p=>...)") }} — 内联 JS 变换(boa_engine)
sort_nodesby=name/region/latency, order=asc/desc, latencies={{ l.proxies | sort_nodes(by="latency", latencies=map) }}
clash_groupsgroups="a,b"{{ l.proxies | clash_groups() }} — Clash/Mihomo 策略组
surge_groups{{ l.proxies | surge_groups() }} — Surge 策略组

规则过滤器(模板内动态应用 filter.yaml / rename.yaml)

过滤器参数示例
filterrules="a,b"(或数组){{ l.proxies | filter(rules="hk") | v2rayn() }}
renamerules="a,b"(或数组){{ l.proxies | rename(rules="rm_brackets") | v2rayn() }}

未定义的规则名静默忽略;过滤器可链式组合,顺序即应用顺序。

规则文本过滤器(snippet 内容 / 规则集)

过滤器参数示例
clashpolicy=..., symbol=...{{ s.response.content | clash(policy="Proxy") }} — 规则文本 → clash 规则列表
surgepolicy=..., symbol=...{{ s.response.content | surge(policy="Proxy") }}
quantumultpolicy=..., symbol=...{{ s.response.content | quantumult(policy="Proxy") }} — 规则文本 → Quantumult X 规则集(host/host-suffix/host-keyword/ip-cidr/ip6-cidr/geoip/user-agent/final;无对应类型的规则丢弃)
clashboxpolicy=..., premium=..., meta=..., pretty=true{{ s.response.content | clashbox(policy="proxy") }} — clash 规则文本 → sing-box rule 对象
no_resolvevalue=true/false{{ s.response.content | no_resolve(value=true) }}
sort_domain_ip域名/IP 规则排序

通用过滤器

过滤器参数示例
base64mode="encode"/"decode"{{ lines | base64(mode="encode") }}
json_strpretty=false{{ i | json_str(pretty=false) }}
yaml_str{{ l.proxies | yaml_str() }}
indentwidth=2{{ body | indent(width=2) }}
uniq按行去重
clearnum=3, n=2压缩连续空行
slugify / urlencodeseparator="-"字符串处理

函数

函数参数示例
nowformat="[year]-[month]-[day]...", tz="+08:00", utc=true, timestamp=true{{ now() }}{{ now(timestamp=true) }}
get_envname="VAR", default="..."{{ get_env(name="HOME", default="") }}

渲染上下文速查

变量可用范围说明
l.proxiesprovider代理对象数组(每个元素有 name / type / server / port 等字段)
l.name / l.rename / l.filterprovider名称 / 配置层规则名
s.response.contentsnippetsnippet 源内容(原样文本)
p.*全部自定义参数(params.yaml + /app/admin/p 热更新)
hp.*全部本次 HTTP 请求的查询参数(与 p 分离,ADR-081)
config全部完整配置对象(config.snippetconfig.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-androidAndroid 设备(不是服务器)thurgio-aarch64-linux-android
aarch64-unknown-linux-muslARM64 Linux 服务器(如树莓派)自行构建:just build-arm-linux

Release 目前只附带 x86_64-unknown-linux-muslaarch64-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 serveserve 在前台运行(无 daemon 模式),配合 Type=simple 由 systemd 直接管理进程;Restart=always + RestartSec=5s 在崩溃或空闲退出(--idle)时自动拉起,StartLimitIntervalSec/StartLimitBurst 防 crash 循环。
  • -r /var/lib/thurgio 会把工作目录切换到数据根目录:thurgio.dbDATABASE_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
  • 硬化:NoNewPrivilegesProtectSystem=strict(仅 ReadWritePaths=/var/lib/thurgio 可写)、ProtectHomePrivateTmp/PrivateDevicesRestrictSUIDSGIDRestrictAddressFamiliesUMask=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.tomlfixtures/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 秒用 busybox wget 探测公开端点 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/amd64linux/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-linuxx86_64-unknown-linux-musl
Windowsjust build-winx86_64-pc-windows-gnu
Androidjust build-androidaarch64-linux-android
Linux ARMjust build-arm-linuxaarch64-unknown-linux-musl

CI(.github/workflows/ci.yml)会在推送时构建 x86_64-unknown-linux-muslaarch64-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 模式时必须为 alwayson-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.yamlfilter.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.yamlsetting.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/jsonapplication/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"
}

字段说明

字段类型描述
statusstring始终为 "healthy"
versionstring名称 + 版本号 + git 快照
databasestring始终为 "connected"
memory_usage_mbintegerRSS 内存(MB,来自 /proc/self/statusVmRSS
cpu_coresinteger可用并行度
timestampstringRFC 3339 时间戳(UTC)

GET /ready

就绪探针——检查 SQLite 数据库是否可读。

响应 200 OK(数据库可读):

{
  "status": "ready",
  "database": "connected"
}

响应 503 Service Unavailable(数据库不可读):

{
  "status": "not_ready",
  "database": "disconnected"
}

GET /metrics

Prometheus 格式的指标数据,包含请求指标、熔断器快照、渲染线程池指标(renderer_active_threadsrenderer_backlog_smoothedrenderer_threads_spawned_totalrenderer_threads_exited_totalrenderer_slots_total)与内存占用。

响应200 OK

纯文本 Prometheus 暴露格式。

GET /favicon.svg / GET /favicon-dark.svg

编译期嵌入的站点图标(crates/thurgio-server/assets/favicon.svgfavicon-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 检查并更新数据。

查询参数

参数类型描述
namestring必填——要渲染的 provider 名称(缺失或为空 → 400)
dstring可选——设置 Content-Disposition: attachment; filename="..."(为空时使用 name
tokenstring认证令牌(也可通过 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 上下文(configphpheadersversion 等),同样注入 scope 变量;config 内的 proxies/content 字段已剔除(dashboard 只消费资源键名)。

响应200 OK,内容类型为 application/json

构建通知(SSE)

GET /app/read/events

Server-Sent Events 流。每次构建完成推送 build_complete 事件,每 15 秒发送一次 keep-alive 心跳。

响应200 OKtext/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 头。

查询参数

参数类型描述
namestring必填——provider 名

响应200 OK

{
  "name": "example",
  "upload": 1073741824,
  "download": 10737418240,
  "total": 107374182400,
  "used": 11811160064,
  "expire": 1893456000,
  "expire_iso": "2030-01-01T00:00:00Z"
}
字段类型描述
usedinteger | null已用流量(upload + download),两者均未知时为 null
expire_isostring | 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 返回空列表。

查询参数

参数类型描述
namestring必填——provider 名
limitinteger可选——只保留最近 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 连接延迟测试。结果按次计算、永不缓存(延迟是时效性数据)。代理列表来自已解析的代理库(与渲染缓存同源),不重新解析原始内容。

查询参数

参数类型缺省描述
namestring必填——provider 名
timeout_msinteger3000单次探测超时(钳制到 [100, 10000]
retryinteger1每个代理重试次数(钳制到 [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 查询国家/地区。

查询参数

参数类型缺省描述
namestring必填——provider 名
timeout_msinteger5000单主机解析超时(钳制到 [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}

探测流媒体服务在当前出口网络的可用性与地区。

查询参数

参数类型缺省描述
servicescsv全部三项要探测的服务:netflixyoutubedisney(别名 disneyplus);未知值 → 400
timeout_msinteger5000单服务探测超时(钳制到 [100, 10000]
proxyURL本次请求使用的一次性上游代理(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(缺少 provurl 头);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}

启用或禁用开发模式(每次渲染前重新加载模板)。

查询参数

参数类型描述
valueboolean必填——truefalse(缺失或非法值 → 400)

响应:设置后的当前开发模式值的字符串——"true""false"

创建临时 Provider/Snippet

GET /app/admin/adhoc

创建一次性 provider/snippet(不写入配置文件),立即渲染返回;同名临时资源可经 /app/read/l/app/read/r 后续引用,且对配置中的同名正式项呈遮蔽效果。缓存持久化到 DB,重启后命中缓存即恢复;按 adhoc.max_items/adhoc.ttl_seconds 自动清理。

查询参数

参数类型必填描述
typestringprovider / snippet
urlstring数据源 URL
namestring名称,缺省自动生成 adhoc-<8 位 hex>
formatstringclash/meta/v2rayn/sip002/surge/auto,默认 auto(仅 provider 生效)
tplstring模板名;缺省回退 setting.adhoc.tpl.provider/tpl.snippet
pre_rename / filter / renamestring逗号分隔的规则名,按顺序应用(未知规则名静默忽略)
forcebooleantrue 忽略内存 + DB 缓存强制重新获取

响应:渲染后的内容,内容类型 text/plain,并携带 Content-Dispositiond 参数)与抓取响应的 subscription-userinfo 头。

备份配置到 GitHub Gist

GET /app/admin/gist?token={gh_pat}&description={text}

把进程工作目录(即 -r/--root 切换后的目录)下的声明配置 YAML 备份为 GitHub 私密 Gist。

查询参数

参数类型描述
tokenstring可选——GitHub PAT;缺省时依次回退 config params 的 gist.token、环境变量 GITHUB_TOKEN
descriptionstring可选——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}"

查询参数

参数类型描述
urlstring必填——长链接,必须以 http:// / https:// 开头(否则 → 400
servicestring可选——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 TypePOST/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 . buildthurgio build -r . 会报错)。

子命令总览

命令别名说明文档
buildb更新 + 渲染 + 写盘;--watch 监听重建build
serves启动 HTTP/TLS API 服务器serve
initi生成最小可用配置脚手架init
checkc校验配置并打印摘要与逐项检查结果check
diffd输出文件 diff 或订阅节点级 diffdiff
configcfg配置声明文件备份 / 回滚 / 列表config
upgradeu检查 GitHub Releases 新版本upgrade
generateg生成 shell 补全脚本generate
versionV打印版本信息version

不带子命令直接运行 thurgio 只加载配置后退出(可用于验证解析是否成功)。

命令的初始化需求

各命令对环境的依赖不同(排错时有用):

阶段命令
不需要 logger/配置generate, version
只需要 loggerinit
只需要 root 目录config(备份/回滚在坏配置下也能运行)
完整初始化(配置 + 数据库 + 渲染器 + 熔断器恢复)build, check, diff, serve, upgrade

启动流程细节见 app

环境变量

所有带环境变量的参数遵循同一优先级:命令行 > 环境变量 > 默认值/配置值。完整清单见配置指南

App

启动流程(thurgio_cli::cli::init):

  1. 若子命令为 generateversion,直接执行并返回(不需要 logger)。
  2. 初始化 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 日志。
  3. 若子命令为 init,生成配置并返回(需要 logger,不需要配置)。
  4. 若设置了 -r/--root(或 ROOT 环境变量),set_root_dir 创建并切换到该目录。
  5. 初始化 Config 和 Renderer(thurgio_app::initialize:Source → AppState → 熔断器状态恢复)。
  6. 执行子命令(build/check/diff/upgrade/serve)。

不带子命令直接运行 thurgio 只执行初始化(加载配置)后退出。

build

alias: b

执行完整的 更新 → 渲染 → 写盘 管线,生成所有 provider(主输出 + 具名输出)与 snippet 的输出文件。

执行流程

  1. 更新订阅源 — 按 TTL 判断每个 provider/snippet 是否过期:未过期直接复用内存缓存;过期或 --force 时重新获取;获取失败时回退到数据库中的 last-known-good 缓存(配合熔断器,见常见问题)。
  2. 解析与规则管线 — 解析代理列表并应用 pre_rename → filter → rename(详见配置指南)。
  3. 渲染 — 构建渲染上下文(config / p / headers / version),provider 阶段渲染主输出与具名输出,与 snippet 阶段并发执行。
  4. 写盘 — 渲染结果经暂存目录原子发布到各输出路径。
  5. 发事件 — 构建完成发出 ProviderUpdated / SnippetUpdated / BuildCompleted 事件:触发 SSE 通知(/app/read/events)与 webhook(设置 THURGIO_WEBHOOK_URL 时 POST JSON)。

成功输出 ✔ Build complete,失败输出 ✘ Build failed(颜色遵循 RUST_LOG_ANSI / TTY 检测)。

参数

参数别名环境变量默认说明
--force-fFORCEfalse忽略缓存状态,强制重新获取所有订阅源
--watch-wWATCHfalse构建一次后进入监听模式,文件变更自动重建
--notify-NNOTIFYfalse构建完成时通过 notify-send 发送系统通知(Linux 桌面)

监听模式(--watch

thurgio build --watch
  • 监听当前工作目录(递归)。
  • 变更事件经 500ms 防抖合并后触发重建。
  • 忽略构建自身的写入(输出目录与 SQLite 数据库文件),避免自触发循环。
  • 命中配置声明文件(thurgio.yamlprovider.yamlparams.yamlrename.yamlfilter.yamlsnippet.yaml)时先全量重载配置再重建;重载失败保留旧配置仅记日志。其余路径(模板等)直接重跑更新 + 渲染管线。

Ctrl+C 停止。

示例

# 常规构建(TTL 内复用缓存)
thurgio build

# 强制刷新所有订阅源
thurgio build --force

# 构建 + 监听变更 + 完成时系统通知
thurgio build --watch --notify

Serve

alias: s

运行 HTTP 服务器。

参数

参数别名环境变量说明
--address-aADDRESS监听地址;未指定时使用配置 setting.address(默认 0.0.0.0
--port-pPORT监听端口;未指定时使用配置 setting.port(默认 10100)。端口为 0 时使用随机端口
--dev-dDEV开发模式:每次渲染前重新加载模板
--idle-iIDLE空闲模式:IDLE_TIMEOUT(默认 30s)内无请求则退出程序(配合 systemd Restart=always 按需拉起)
--evict-eEVICT空闲时清空数据缓存(默认开启;--evict=false 关闭):EVICT_TIMEOUT(默认 10s)无请求先清缓存,进程继续存活
--tls-only-sTLS_ONLY仅提供 HTTPS(必须同时提供证书与私钥,否则报错)
--tls-port-tTLS_PORTTLS 端口,默认等于 --port
--tls-cert-cTLS_CERTTLS 证书文件
--tls-cert-key-kTLS_CERT_KEYTLS 私钥文件
--open-OOPEN启动时用 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.svgimage/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=auto UA 自适应,见 API 参考)。
  • /app/read/r?name=: 更新数据后渲染 snippet(name 为必填查询参数)。
  • /app/read/ui: 后台更新,渲染 ui.tpl(上下文含当前令牌 scope)。
  • /app/read/c: 渲染完整 Tera 上下文为 JSON(含 scope,剔除 configproxies/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,除 tokenname 外的 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, --forcefalse覆盖已存在的文件

生成的文件

<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)
检查项说明
Summaryprovider / 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

执行过程:

  1. 读取 setting.output 中配置的所有输出文件的当前内容(不存在的文件视为空内容)。
  2. 执行一次强制构建(等价 build --force,会真实访问订阅源)。
  3. 逐文件打印新旧内容的行级差异:- 删除行、+ 新增行、空格前缀为未变更行。
参数说明
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.yaml5 个 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 的路径(含路径分隔符时按路径解析)

执行过程:

  1. 校验目标存在且包含至少一个声明文件(否则报错,不触碰当前状态)。
  2. 先把当前状态自动备份backups/<时间戳>(保险)——当前状态为空则跳过。
  3. 覆盖写回声明文件与 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-yfalse有新版本时额外打印发布页下载链接

行为

  • 查询 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.yamlsetting 顶级节包含服务器设置。

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.0serve 启动时可用 -a/--addressADDRESS 环境变量覆盖。

port

服务监听端口,默认 10100serve 启动时可用 -p/--portPORT 环境变量覆盖。端口为 0 时使用随机端口。

udp

对于节点的 udp 字段处理逻辑。

  • None: 不处理
  • False: 设为 false
  • Try: 如果代理类型支持 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_items100临时资源数量上限(内存与 DB 一致清理)
ttl_seconds86400滑动 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_renamefilterrename → 按 template 渲染 → 写入 filename

字段总览

字段类型必填说明
namestring显示名,未指定时用键名
typestring输入配置格式,见下表
sourcestringURL(http(s):// 开头)或本地文件路径
templatestring主输出模板(相对模板目录)
filenamestring 列表渲染结果写入的文件列表
pre_rename规则名列表解析后、过滤前应用(引用 rename.yaml
filter规则名列表过滤阶段应用(引用 filter.yaml
rename规则名列表过滤后应用(引用 rename.yaml),应用前后均检查重名
emojibooltrue是否执行 emoji 逻辑
default_outputbool回退 p.default_output是否同时写默认路径文件
extstring回退 p.ext默认输出文件的扩展名
outputsmapping具名输出,见下文
p任意 YAML{}自定义参数,模板中以 p.* 访问

type

输入配置格式:

  • clash — Clash YAML
  • meta — Clash.Meta YAML
  • v2rayn — V2RayN 分享链接(Base64 JSON)
  • sip002 — SIP002 URI(逐行)
  • surge — Surge 配置 [Proxy]
  • qx / loon / singbox / sip008 / ssd
  • telegram — TG 类 HTTP/SOCKS5 代理链接(tg://http / tg://socks / t.me/*

auto 不是合法的 provider type——它只用于渲染侧 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)按键名引用,依次应用。

每条规则值为三种形状之一:

  1. 裸字符串简写contain 匹配任一 | 分隔关键字,忽略大小写(最常用)
  2. 单个匹配器 mapping
  3. 匹配器 mapping 列表:顺序应用,代理必须命中全部(AND)

匹配器

说明
contain字符串或字符串数组名称包含任一关键字(数组 = OR,等价 `
start字符串名称以关键字开头
end字符串名称以关键字结尾
type字符串按代理协议类型过滤(ss, vmess, trojan 等)
regex字符串按正则匹配代理名

共享选项

缺省说明
ignore_casetrue忽略大小写
negatefalse反转:命中则剔除

限制:列表(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 regex crate:支持 \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。典型用途:规则集聚合、静态片段、模板化配置文件。

字段总览

字段类型必填说明
namestring显示名,未指定时用键名
templatestring渲染模板(相对模板目录 THURGIO_TPL_DIR,默认 tpls/
sourcestring否*URL(http(s):// 开头)或本地文件路径;内容进入 s.response.content
filenamestring 列表渲染结果写入的文件列表
default_outputbool回退 p.default_output是否同时写默认路径文件
extstring回退 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)。

条目形状

每条目值为两种形状之一:

  1. 行列表(本地形态)——规则行数组
  2. 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必填规则集名(过滤器输入值被忽略)
formatclashclash / surge / clash-classical
policy🚀 Node SelectSurge 格式的路由策略后缀
text内联规则行文本,覆盖其他一切来源

来源优先级:内联 text > _rules.ruleset 命名表(本文件)> 内置 assets/rulesets/*.list 资产。未定义的规则名会报渲染错误(与 filter/rename 的静默忽略不同)。

Params

全局自定义参数,供模板通过 p 面访问。在 params.yaml 中定义,也可放在 thurgio.yamlparams 顶级节(两者合并)。

值可以是任意 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 参考