参考

API 参考

太一只暴露一份 ConnectRPC API。同一份契约同时服务内置管理 UI(HTTP 上的 JSON)、tiyi CLI(HTTP 上的 JSON)与节点流(gRPC 双向)—— UI 能做的事,脚本同样能做。共 23 个服务、238 个 RPC,另有一小组非 RPC 的 HTTP 端点。

传输

每个过程都是对 /{package}.{Service}/{Method}POST,例如 POST /tiyi.v1.SiteService/ListSites。同一批路由上支持三种线上协议:

协议Content-Type适用场景
Connect(一元)application/jsoncurl、脚本、CI —— 无需帧封装,也无需生成代码。
Connect(流式)application/connect+json非 gRPC 客户端调用服务端流与双向流。
gRPC / gRPC-Webapplication/grpc生成的 gRPC 客户端;节点流。

API 同时监听管理地址(--addr,默认 0.0.0.0:8080)与本地 Unix socket(--admin-socket,默认 /run/tiyi/admin.sock)。

鉴权

三种鉴权面,按部署上下文选用:

鉴权面机制使用方
本地管理 socket admin.sock 上的 OS 文件权限 与服务端同主机的 tiyi CLI。请求以合成的超级管理员身份运行 —— 这正是无凭据也能重置密码的原因。
HS256 JWT HTTP 上的 Authorization: Bearer <jwt> 管理 UI、远程 CLI、任何从主机外驱动 API 的客户端。由 AuthService.Login 签发,AuthService.Refresh 续期。
节点流 token 一次性入网 token → 长期流 token AgentStreamService.Connect 上的节点。token 由 AgentService.IssueEnrollmentToken 签发。

登录

$ curl -sS http://tiyi:8080/tiyi.v1.AuthService/Login \
    -H "Content-Type: application/json" \
    -d '{"username":"admin","password":"..."}'
{
  "accessToken": "<jwt>",
  "expiresAt": "2026-07-26T21:13:41Z",
  "user": {
    "id": "<uuid>",
    "tenantId": "<uuid>",
    "username": "admin",
    "roles": ["Administrator"],
    "accessCodes": ["site:read", "site:write", "..."]
  }
}

三个容易踩的点:

默认有效期:access token 8 小时(auth.access_token_ttl),refresh token 7 天(auth.refresh_token_ttl)。配置了 LDAP 或 RADIUS 时,同一个 Login 调用会按顺序回退尝试。

本地管理 socket

$ curl -sS --unix-socket /run/tiyi/admin.sock \
    http://tiyi.local/tiyi.v1.SystemService/GetSystemSettings \
    -H "Content-Type: application/json" -d '{}'

http://tiyi.local 只是占位符 —— 真正决定去向的是 socket 路径。socket 默认权限 0600--admin-socket-mode 0660 --admin-socket-group tiyi-admin 可以共享给一个 Unix 组。能打开该 socket 就等于拥有完全控制权,请把该组视同 root。

CLI

$ export TIYI_API=https://tiyi.example.com   # 不设置则走本地 socket
$ export TIYI_TOKEN="$TOKEN"
$ tiyi site list

命令级参数 --api--token--admin-socket 会覆盖环境变量。

请求约定

revision 与乐观并发

带版本的资源都有 revision,每次写入自增。提交时带上你读到的 revision;若已被他人抢先写入,调用会返回 failed_precondition: revision conflict。重新读取、重放改动、再重试即可。省略 revision 则跳过该检查。

路由与站点级策略覆盖有各自独立的 RPC(UpdateSiteRoutingUpsertSitePolicyOverride),正是为了避免不理解路由的客户端用一次普通 UpdateSite 把它们抹掉。

分页

有界列表(站点、用户、证书、策略)用偏移分页:{"page":{"page":1,"pageSize":50}},页码从 1 开始,默认每页 50,上限 200。日志与事件查询用游标:把上一次的 nextCursor 传回,直到 hasMore 为 false。需要直接跳页时用 offset 代替 cursor,上限 10 万行,超出应改为收窄时间范围。

23 个服务

服务范围
AuthService登录、登出、刷新、当前用户、access code、修改密码。
SystemService健康、设置、Dashboard 汇总、声明式 apply、CRS/GeoIP/二进制发布、升级批次。
MenuService按权限过滤的管理界面导航。
UserService用户 CRUD、角色分配、锁定/解锁、密码重置。
RoleService角色 CRUD 与权限目录。
SiteService站点 CRUD、启用/停用、编译配置预览、路径路由、站点级策略覆盖。
UpstreamService上游池 CRUD;被引用时拒绝删除。
CertService上传、ACME 签发/续期、下载、DNS provider CRUD。
PolicyServiceWAF 策略 CRUD、分层更新、版本快照与回滚、SecLang 预览、测试台。
RuleOverrideService单条 CRS 规则的行为与范围化覆盖生命周期。
CustomRuleService自定义 SecLang 与可视化规则的生命周期、排序、模板。
IpListService可复用 IP 数据、分层绑定、CSV 导入导出、查询与优先级。
RateLimitService端点与客户端范围的限速资源。
CrsServiceCRS 目录浏览、导入、排除包安装与挂载。
AgentService节点 CRUD、入网 token、安装脚本、命令、配置 bundle、指标采样。
AgentGroupService基于标签与显式列表的节点分组及匹配预览。
AgentStreamService带鉴权的节点双向会话流。
EvidenceUploadService请求证据上传独占一条流,避免大请求体阻塞控制面流量。
TrustService客户端 IP 信任配置(租户 + 站点覆盖)、CDN 快照、explain。
AlertService告警规则与通道 CRUD、ack/resolve、静默、备注、通道测试发送。
LogService安全/访问/错误事件查询、保留证据、导出、日志策略、实时 tail。
AuditService防篡改审计查询、链状态与校验。
AIService可选且默认关闭的建议、Copilot 分析与 Chat。

逐 RPC 的完整参考 —— 每个请求/响应字段以及所需权限 —— 由 schema 自动生成,随源码树发布在 docs/api/,protobuf 定义在 proto/tiyi/v1/

错误

错误返回标准码、给人看的 message,有时还带类型化 details。请基于 code(以及 details[].debug.reason)分支,不要基于 message

{
  "code": "unauthenticated",
  "message": "invalid session",
  "details": [{ "type": "google.rpc.ErrorInfo",
               "debug": { "reason": "TOKEN_INVALID", "domain": "tiyi.io" } }]
}
HTTP常见原因可重试?
invalid_argument400参数被验证器拒绝。否 —— 先改请求。
failed_precondition400revision 冲突、资源被引用或其他状态保护。重新读取后重试。
unauthenticated401token 缺失、格式错误或过期。刷新后重试一次。
permission_denied403会话有效但缺少所需权限;也用于 ACCOUNT_LOCKED否。
not_found404ID 不存在或已软删除。否。
already_exists409违反唯一性约束。否。
resource_exhausted429写入准入队列已满。是 —— 遵守 Retry-After
unimplemented501schema 中已声明但尚未接线。否。
unavailable503SQLite 繁忙或依赖不可用。是 —— 退避重试。
deadline_exceeded504服务端超时。收窄查询范围。
internal500未处理的服务端故障。否 —— 请反馈。

已知的 ErrorInfo reason:BAD_CREDENTIALSTOKEN_INVALIDACCOUNT_LOCKED

写入准入

写类 RPC 会经过写入准入闸门排队,避免并发写入压垮内嵌 SQLite。队列满时返回 resource_exhausted,并带上以秒为单位的 Retry-After 头。读取从不排队。批量导入应限制写并发并遵守该响应头。

RBAC

每个 RPC 声明所需权限 —— site:readpolicy:writelog:exportsystem:apply 等,共 57 项。服务端将 JWT subject 的角色与权限表比对;持有 tiyi:superadmin 的角色通过所有检查。内置角色只有 Administrator 且拥有全部权限,因此请用 tiyi role 或角色页面创建最小权限角色,并用 RoleService.ListPermissions 查看实时目录,而不是依赖固定清单。

七个过程无需会话即可访问:SystemService.HealthAuthService.Login / Refresh / LogoutAgentService.EnrollAgentStreamService.ConnectEvidenceUploadService.Upload。后三者改由入网 token 或流 token 把关。

流式 RPC

流式调用可走 gRPC,也可走 Connect 流式协议。使用 Connect 时设置 Content-Type: application/connect+json,并给每条消息加 5 字节信封:1 字节标志位 + 大端 uint32 长度。流失败时 HTTP 状态码仍是 200 —— 真正的状态在结尾帧(标志位 0x02)里,只看 HTTP 码的客户端会漏掉错误。

断线请退避重连。这些流都不会重放历史,断连期间遗漏的数据需要另行查询补齐。

非 RPC 的 HTTP 端点

端点监听面鉴权用途
GET /healthz两者存活/就绪探针:健康 200,否则 503,两种情况都返回 JSON。
GET /download/tiyi两者下发当前二进制,供尚无凭据的新节点自举。
/api/v1/telemetry/*两者JWT + telemetry:read精确计数:qpsseriestopkapitree 以及 API 资产处置路由。
GET /metrics仅 socketsocket 权限观测管线的 Prometheus/OpenMetrics 抓取端点。
GET /debug/*仅 socketsocket 权限logsink、告警、AI、许可的诊断计数器。非稳定契约。

管理监听同时提供管理 UI,未知路径会回落到 SPA 并返回 HTTP 200 —— 所以对「仅 socket」路径 curl 成功,拿到的其实是 HTML 而非数据。请检查 Content-Type

生成客户端

把代码生成器指向源码树中的 proto/tiyi/v1。已验证的组合:connect-go(CLI 使用)、connect-es / @connectrpc/connect-web(管理 UI 使用),以及任意标准 gRPC 客户端(适合流式为主的集成)。由于一元 Connect 就是 HTTP 上的普通 JSON,一个简单的 fetchrequests 封装同样够用。

包名是 tiyi.v1;字段号永不复用,删除的字段保留为 reserved,因此旧客户端可以继续解析新响应。新的可选字段与新 RPC 在小版本中引入 —— 请把未知响应字段当作可忽略。

API 契约

本文档覆盖面向运维的 API 表面,并应与已安装二进制保持一致 —— 不一致请提 issue。