thunder-id/thunderid · 上手攻略
- 仓库:thunder-id/thunderid
- 链接:https://github.com/thunder-id/thunderid
- 分类:agent / identity / IAM / 去中心化身份
- 作者:spark
- 更新:2026-10-28
版本锚点:最新 release v1.0.2(发布于 2026-10-27),Apache-2.0,主语言 Go。仓库 snapshot(2026-10-28):595 stars、403 forks、1034 open issues。属 LF Decentralized Trust(Linux Foundation)项目,官网 thunderid.dev。
一、是什么
ThunderID 是一个轻量级开源 IAM(身份与访问管理)栈,定位明确:为人、AI Agent 和机器三类主体提供可组合的身份流程。
它有四个设计目标,README 原文列出:
- Agent 原生身份:把 AI Agent 当一等身份管理,支持委派权限、同意感知的访问、可追溯性,并支持向 Agent 签发可验证凭证(VC)。目标是让 Agent 能通过接口安全、可编程地使用 IAM 能力。
- 后量子安全(post-quantum-safe by design):算法、密钥类型、签名方式、令牌保护机制都可演进,内建对后量子安全算法和混合过渡方案的支持,覆盖密钥管理、凭证签发、断言和服务间通信。
- 去中心化身份:补上"依赖方(relying party)"的落地缺口,让服务提供方能在真实应用里消费、验证并信任去中心化身份——涉及 DID、可验证凭证、数字钱包、信任注册表和 issuer-verifier-holder 模型。
- 轻量运行时 + GitOps:容器化轻量运行时,本地机房和云上都能跑;身份流程、策略、配置全部声明式,适合自动化、版本化和 GitOps。
架构上是控制面(编写配置)+ 数据面(运行认证)分离。
功能面覆盖:身份管理(分层组织单元 OU 与群组)、标准协议(OAuth 2.1、OpenID Connect 含 PAR 与 PKCE;OpenID4VCI 签发 / OpenID4VP 验证;WebAuthn / passkeys;Google、Microsoft、GitHub 及任意 OIDC/SAML 的 IdP 联邦)、去中心化身份(从可配置模板向用户钱包签发 VC、按 presentation definition 与 trust anchor 验证)、用户旅程(login / registration / recovery 以 journey 定义,20+ 内置执行器:密码、passkey、OTP、社交登录、同意等)、授权(分层资源与派生权限、跨用户/Agent/应用的 RBAC、带用户审阅的同意管理)、开发者体验(Console UI、REST API、SDK,以及供 AI Agent 管理 IAM 的 MCP server)。
二、解决什么问题
传统 IAM 栈(Keycloak、Auth0、Okta、Zitadel 等)诞生于"人用应用"时代。当 AI Agent 开始代替人调用 API、访问用户数据、代表用户发起操作时,这套栈有四个具体缺口:
- Agent 没有身份主体。传统 RBAC 里主体是 user/service account,没有"这个 Agent 代表谁、被授权了什么、用户是否同意"的表达方式。
- 委派权限(on-behalf-of)流程重。Agent 用用户权限跑,需要完整实现 OBO token 交换、自定义代码、额外的授权服务器。
- 去中心化身份落地断层。DID/VC 规范很多,但依赖方接入的实际工程路径模糊。
- 配置与策略难以版本化。多数 IAM 的控制台配置存在数据库里,没有 GitOps 友好性。
ThunderID 的答案是一套把 Agent 当一等公民 + 全声明式 YAML 资源定义 + MCP server 暴露 IAM 能力的栈。它由 LF Decentralized Trust 治理,技术章程在 thunder-id/governance 仓库,有 OIDC Conformance 测试工作流。
诚实边界:这是一个 research / 早期 1.x 阶段的项目。README 自身没有给出生产就绪声明或 SLA;open_issues 数量(1034)相对 stars(595)偏高,GitHub topics 里还挂着 hacktoberfest。企业生产使用前应自行评估。
三、快速安装
默认监听 8090 端口,Console 在 https://localhost:8090/console。
方式 A:Docker Compose(最快)
仓库自带 install/quick-start/docker-compose.yml,一条命令起全套(数据库初始化 → setup → server):
docker compose -f oci://ghcr.io/thunder-id/thunderid-quick-start:latest up
成功后访问 https://localhost:8090/console。
该 compose 定义了三个服务:thunderid-db-init(从镜像拷贝数据库 schema 到卷)、thunderid-setup(跑一次 ./setup.sh,生成 TLS/JWT 签名/AES 密钥与 Direct Auth Secret)、thunderid(挂载 thunderid-db / thunderid-certs / thunderid-secrets 三个卷启动服务)。
方式 B:单容器 Docker
# Step 1: 拉镜像(生产请固定 tag,别用 latest)
docker pull ghcr.io/thunder-id/thunderid:<version>
# Step 2: 一次性 setup —— 生成密钥材料并初始化配置与数据库
docker run -it --rm \
-v thunderid-data:/opt/thunderid/database \
-v thunderid-certs:/opt/thunderid/config/certs \
-v thunderid-secrets:/opt/thunderid/config/secrets \
ghcr.io/thunder-id/thunderid:<version> \
./setup.sh
# Step 3: 后台启动
docker run -d \
--name thunderid \
--restart unless-stopped \
-p 8090:8090 \
-v thunderid-data:/opt/thunderid/database \
-v thunderid-certs:/opt/thunderid/config/certs \
-v thunderid-secrets:/opt/thunderid/config/secrets \
ghcr.io/thunder-id/thunderid:<version>
docker logs -f thunderid
关键点:setup.sh 会生成这套部署独有的 TLS、JWT 签名和加密密钥。服务器在没有这些密钥时不会启动,所以 config/certs 和 config/secrets 两个卷必须挂载,且必须先跑 setup。
方式 C:Release Artifact(本地跑)
unzip thunderid-<version>-<os>-<arch>.zip
cd thunderid-<version>-<os>-<arch>/
./setup.sh # Linux/macOS;Windows 用 .\setup.ps1
./start.sh # Linux/macOS;Windows 用 .\start.ps1
开发/本地场景可以把 seed + serve 合成一步(幂等,可重复跑):
./start.sh --bootstrap-and-serve --admin-password <your-admin-password>
注意:--bootstrap-and-serve 不会像 setup 那样自动生成管理员密码。你必须用 --admin-password 或 ADMIN_PASSWORD 环境变量提供,两者都没有则启动失败。--admin-username 可选,默认 admin。分阶段部署(初始化作为一次性 Job)则应保持 setup 和 start 分离。
方式 D:Helm(Kubernetes)
需要先装 PostgreSQL(官方示例用 Bitnami chart 建 4 个库:configdb、runtime_transient、entitydb、runtime_persistent),再:
helm install thunderid oci://ghcr.io/thunder-id/helm-charts/thunderid \
--devel \
--set configuration.database.config.postgres.hostname=postgresql \
--set configuration.database.config.postgres.sslmode=disable \
--set configuration.database.runtime_transient.postgres.hostname=postgresql \
--set configuration.database.runtime_transient.postgres.sslmode=disable \
--set configuration.database.entity.postgres.hostname=postgresql \
--set configuration.database.entity.postgres.sslmode=disable \
--set configuration.database.runtime_persistent.postgres.hostname=postgresql \
--set configuration.database.runtime_persistent.postgres.sslmode=disable \
--set configuration.server.publicUrl=https://localhost:8090 \
--set configuration.gateClient.hostname=localhost \
--set configuration.gateClient.port=8090
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=thunderid --timeout=120s
kubectl port-forward svc/thunderid-service 8090:8090
未配置密码时,setup Job 会生成随机管理员密码并存在 thunderid-admin-credentials Secret:
kubectl get secret thunderid-admin-credentials -o jsonpath="{.data.admin-password}" | base64 -d; echo
数据库选择
- 嵌入式 SQLite(默认):开箱即用,无需外部依赖。挂
-v thunderid-data:/opt/thunderid/database持久化。 - 外部 PostgreSQL:
cd install/local-development && docker compose up -d起一个配套实例(需 Docker Compose 2.0+)。
四、核心用法
核心概念模型
理解 ThunderID 就三样东西:Resource server(资源服务器)、Resource(资源)、Role(角色)。权限用 资源标识:动作 形式表达,权限分隔符在资源服务器上配置(文档示例用 :)。
用 Agent 自己的身份调 API
这是 ThunderID 最独特的部分。流程是:client_credentials 换 token → introspect 验证 → 检查 scope。
import base64, os
from urllib.parse import quote_plus
import requests
BASE_URL = os.environ["THUNDERID_BASE_URL"].rstrip("/")
CLIENT_ID = os.environ["AGENT_CLIENT_ID"]
CLIENT_SECRET = os.environ["AGENT_SECRET"]
RESOURCE = os.environ["COURSE_RESOURCE"] # 资源服务器的 Identifier
def post(path, data):
creds = f"{quote_plus(CLIENT_ID)}:{quote_plus(CLIENT_SECRET)}"
basic = base64.b64encode(creds.encode()).decode()
r = requests.post(f"{BASE_URL}{path}", data=data,
headers={"Authorization": f"Basic {basic}"},
verify=os.environ.get("THUNDERID_CA_BUNDLE") or True,
timeout=30, allow_redirects=False)
if r.status_code != 200:
raise RuntimeError(f"{path} returned HTTP {r.status_code}")
return r.json()
def validate_token(token: str, permission: str) -> dict:
identity = post("/oauth2/introspect", {"token": token})
audiences = identity.get("aud", [])
if isinstance(audiences, str):
audiences = [audiences]
if (identity.get("active") is not True
or identity.get("client_id") != CLIENT_ID
or not identity.get("sub")
or RESOURCE not in audiences
or permission not in identity.get("scope", "").split()):
raise PermissionError(f"The token is not valid for {permission} on {RESOURCE}.")
return identity
def read_courses(token: str) -> dict:
identity = validate_token(token, "courses:read")
print("Verified agent:", identity["sub"])
return {"courses": [{"id": "CS101", "name": "Introduction to Computer Science"}]}
token = post("/oauth2/token", {
"grant_type": "client_credentials", "resource": RESOURCE, "scope": "courses:read",
})["access_token"]
read_courses(token)
接入 LangChain / Google ADK / CrewAI / Vercel AI SDK
官方提供 docs/content/getting-started/connect-your-agent/ 下的快速上手文档,覆盖 LangChain、Google ADK、CrewAI、Vercel AI SDK 四种框架。Agent 集成代码的核心就在上面的 client_credentials + introspect 模式:Agent 拿自己的 token,工具函数里 introspect 校验 active / client_id / sub / aud / scope,再决定是否返回数据。
THUNDERID_BASE_URL=https://localhost:8090
AGENT_CLIENT_ID=replace-with-your-client-id
AGENT_SECRET=replace-with-your-client-secret
COURSE_RESOURCE=https://courses.example.com
THUNDERID_CA_BUNDLE=./thunderid.pem
本地自签证书的导出方式:
openssl s_client -connect localhost:8090 -servername localhost </dev/null 2>/dev/null | openssl x509 -out thunderid.pem
Python 侧依赖示例:langchain==1.4.2、langchain-google-genai==4.4.0、requests==2.34.2、python-dotenv==1.2.3(Python 3.12 或 Node.js 24)。
委派模式(On-Behalf-Of)
开启委派后,Agent 使用用户的权限而不是自己的。需要在 Console 里:
- Agents → 对应 Agent → Advanced → 打开 Delegated mode;保持 Require PKCE 开启,Require pushed authorization requests 关闭;添加回调地址如
http://localhost:6274/callback。 - Flows → 从 Basic 模板创建登录流,在 Authorization 与 Auth Assertion Generator 之间插入 User Consent 组件,把 Allow 和 Deny 两个响应都连回 User Consent,打开 Fail flow when user denies consent。
- 在 Agent 的 Flows 标签页把该流设为它的 Sign-in flow。
接入应用(用户身份)
Console 里建 Application → 配置 OAuth 设置、redirect URI 和访问策略;用 Flows 可视化设计器搭登录流,加 MFA、passkey 或社交登录不用改应用代码。官方提供 B2C 指南,及 connect-your-application 下的 Android / Browser / Express / Flutter / iOS / Next.js / Node / Nuxt 示例(每个有 redirect-based 与 embedded 两种 prompt)。
MCP 与 AI Agent 管理 IAM
ThunderID 自带 MCP server,让 AI Agent 管理和查询 IAM。官方另有 Securing MCP Guide(在 use-cases/ai-agents/mcp-authorization),即"用 ThunderID 保护 MCP 服务"的对称场景。
五、典型适用场景
- 给 AI Agent 加一层受控身份:Agent 用
client_credentials拿自己的 token,权限由 RBAC + scope 精确约束,不共享人类账号密钥。 - Agent 代表用户操作(OBO):用户登录并同意后,Agent 以用户权限执行操作——客服助手、代办 Agent、自动化工作流。
- MCP 服务的统一鉴权:把 MCP server 接到 ThunderID,避免每个 MCP 服务各写一套 token 校验。
- B2C 登录与旅程编排:需要 passkey / OTP / 社交登录 / MFA 组合,且希望改登录流程不动应用代码的团队。
- 去中心化身份落地:需要向用户钱包签发 VC、按 presentation definition 验证、并把结果串进传统登录旅程的场景。
- GitOps 化的身份配置:所有实体都有 YAML 资源定义,身份配置可入 Git、可评审、可版本化。
- 后量子迁移准备:需要 crypto-agile 底座、且不想等到 2030 才开始改造密钥体系。
六、坑与注意
坑 1:没跑 setup.sh 就启动,服务器直接拒绝启动
现象:拉了镜像直接 docker run,服务起不来。
影响:新手的第一个卡点,报错不会明说是缺密钥。
修复:先跑一次 ./setup.sh(挂 config/certs 和 config/secrets 两卷),后续启动必须挂同一对卷,否则读不到生成的密钥。"没密钥就不启动"是设计。
坑 2:--bootstrap-and-serve 不会生成管理员密码
现象:想一步启动,结果启动失败。
影响:误以为是环境问题。
修复:必须显式给 --admin-password 或 ADMIN_PASSWORD 环境变量,否则启动失败。--admin-username 默认 admin。
坑 3:生产用 latest tag,密钥和数据库版本跟着漂
现象:镜像和 compose 里都是 :latest。
影响:上游发新版后密钥格式、schema 与运行版本不匹配,可能起不来;也破坏可复现性。
修复:官方明确说"Pin to a specific release tag for reproducible deployments"。固定 :v1.0.2 这类 tag,并把 db-init 与镜像一起锁。
坑 4:setup 和 start 容器间数据卷丢失
现象:setup 用 --rm 跑完,起新容器没挂 thunderid-data。
影响:SQLite 回到初始状态,Console 里的用户、资源、Agent 全没了。
修复:三个卷都要挂:thunderid-data(数据库)、thunderid-certs(TLS/JWT/加密密钥)、thunderid-secrets(Direct Auth Secret)。照抄 compose 即可。
坑 5:本地自签证书导致客户端 SSL 校验失败
现象:requests.post 抛证书验证错误。
影响:本地开发直接卡住。
修复:导出公钥链到 thunderid.pem 并设 THUNDERID_CA_BUNDLE=./thunderid.pem,或从安装目录拷 config/certs/server.cert 重命名。公网可信证书则省略该变量——别在生产用 verify=False 绕过。
坑 6:Helm 部署后忘了创建 4 个 PostgreSQL 库
现象:Pod 一直 Pending 或启动即崩,日志是连不上数据库。
影响:官方要建 configdb、runtime_transient、entitydb、runtime_persistent 四个库,还要逐个导入 dbscripts/$db/postgres.sql 的 schema。
修复:按 Helm 章节完整执行(Bitnami 建库 → kubectl exec 导入四个 schema → 删 dbinit Pod),再装 chart。
坑 7:Agent 权限配在错误的资源服务器上,scope 校验永远失败
现象:token 拿到了但 introspect 校验不过,报 "The token is not valid for ... on ..."。
影响:容易误判为 ThunderID 的 bug。
修复:资源服务器的 Identifier 才是 audience。文档示例里 COURSE_RESOURCE=https://courses.example.com 只是命名空间,不需要真部署服务在该 URL。另注意 scope 用 " ".split() 精确匹配,且 aud 可能是数组或字符串两种形态。
坑 8:委派模式下忘了开 "Fail flow when user denies consent"
现象:用户拒绝同意后,流程仍然往下走。 影响:本该被阻断的 Agent 越权操作生效——这是安全语义错误。 修复:把 User Consent 组件的 Allow 与 Deny 都连回 User Consent,并显式打开 "Fail flow when user denies consent"。
坑 9:把"轻量运行时"当成企业级 IAM 的替代品
现象:看到 "post-quantum-ready"、"LF Decentralized Trust" 就认为可直接上生产。 影响:1034 open issues vs 595 stars 的比例本身说明社区处于早期;README 未给生产 SLA。 修复:先用官方 try-it-out 验证能力覆盖度,再评估生产加固。官方另给了 Docker Production 建议(反代、资源限制、健康检查、密钥管理)。
坑 10:YAML 声明式配置与 Console UI 双通道可能冲突
现象:一边改 Console、一边没改 Git 里的 YAML,下次同步时被覆盖(反之亦然)。 影响:配置漂移,权限行为不可预期。 修复:定一个单一事实来源。官方强调 YAML + immutable runtime 就是为 GitOps 设计的,把 Console 当调试界面而非配置源。
七、与同类对比
| 维度 | ThunderID | Keycloak | Zitadel | Auth0 / Okta |
|---|---|---|---|---|
| Agent 作为一等身份 | ✅ 核心设计目标 | ❌(需绕道) | ⚠️ 部分 | ❌(商业限制) |
| 委派 / OBO | ✅ 内建 + 同意流程 | ⚠️ 需自建 | ⚠️ 有限 | ⚠️ 有限 |
| 去中心化身份(DID/VC) | ✅ OpenID4VCI/4VP + 钱包 | ❌ | ⚠️ | ❌ |
| 后量子 crypto-agile | ✅ 设计内建 | ❌ | ❌ | ❌ |
| GitOps 声明式配置 | ✅ 全实体 YAML | ⚠️ 有 operator | ⚠️ | ❌(控制台为主) |
| MCP server 暴露 IAM | ✅ | ❌ | ❌ | ❌ |
| 身份旅程可视化编排 | ✅ Flow designer + 20+ 执行器 | ⚠️ | ⚠️ | ✅(主打) |
| 部署形态 | 单二进制 / Docker / Helm | Java + Quarkus | Go 单二进制 | 纯 SaaS |
| 许可 | Apache-2.0 | Apache-2.0 | Apache-2.0 | 商业 |
| 成熟度 | 1.x 早期 | 成熟 | 中 | 成熟(但闭源/成本高) |
和 Keycloak:Keycloak 是 Java/Quarkus 的重型全家桶,扩展靠 SPI 插件;ThunderID 走 Go + 声明式 YAML + Agent 原生,路线完全不同。Keycloak 强在身份联邦成熟度与生态,ThunderID 强在 Agent 与 DID 原生支持。
和 Zitadel:Zitadel 同样 Go 写、偏云原生,也有事件溯源架构,是最接近的参照物。差别在于 ThunderID 把 Agent 身份、委派同意链和 post-quantum 作为一级设计目标。
和 Auth0 / Okta:闭源 SaaS,成熟稳定、托管省心,但无自托管选项,Data Resident / Agent 身份这类需求要靠商业合同。ThunderID 反过来:完全自托管 + Apache-2.0 + LF 治理,代价是自扛运维升级。
和 Spiffe / SPIFFE:Spiffe 解决工作负载身份(服务间 mTLS),不涉及终端用户登录、不做 OAuth/OIDC 消费侧流程、不管 Agent 委派。两者互补:SPIFFE 管"服务是谁",ThunderID 管"人和 Agent 是谁、能做什么"。
八、一句话推荐
如果你的产品正让 AI Agent 代表用户调用 API 或 MCP、需要"用户登录并同意 → Agent 拿受限权限执行"这条委派链,同时又想要去中心化凭证和 GitOps 化配置,ThunderID 是少见的把 Agent 当一等公民的免费 Apache-2.0 自托管 IAM,值得花半天跑通 try-it-out;但它只有 1.x、生产就绪度需自评,成熟场景仍应先看 Keycloak / Zitadel / 商业 SaaS。
本文的不确定处(诚实标注):
- ⚠️ 文中所有命令、YAML 键名、端点路径取自 v1.0.2 发布(2026-10-27)前后的 main 分支文档(docs/content/getting-started/get-thunderid.mdx、docs/content/deployment/deployment-paths/docker.mdx、install/quick-start/docker-compose.yml、docs/content/getting-started/connect-your-agent/langchain.mdx),未实际执行(本次为只读研究,不 clone、不跑代码)。
- ⚠️ 官方文档大量使用 {{productSlug}} / <ProductName /> 模板占位符,本文按 README 与仓库实际内容替换为 thunderid;占位符与实际变量名是否完全一致未逐一验证,Compose 服务名等细节可能随版本变动。
- ⚠️ Helm 章节中 helm install 的 --devel 参数按官方文档原样抄录,其在非开发场景的实际语义未核实。
- ⚠️ "post-quantum-safe by design" 是 README 的设计目标声明,本文未核查仓库中哪些算法已实际落地、哪些只是 crypto-agile 抽象层——落地程度待核。
- ⚠️ "20+ 内置执行器"、"595 stars / 403 forks / 1034 open issues" 为 2026-10-28 经 GitHub API 读取的快照,随时间变动。
- ⚠️ 未核查 docs/content/deployment/deployment-paths/{kubernetes,openchoreo}.mdx、docs/content/use-cases/** 的具体内容(仅确认存在),可能含本文未覆盖的部署与用例细节。
- ⚠️ 官方文档站 thunderid.dev 与 GitHub README 的版本号未交叉核对,站点可能处于 next / v1.0.x 双版本并行状态。