Onyx Terraform Provider 使用指南:通过 Onyx 管理 API 声明式管理 LLM Provider、API Key 与工作区配置
【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer
本文以terraform-provider-onyx为核心,介绍如何使用 Terraform 通过 Onyx 管理 API 对 Onyx(AI 聊天平台)应用层配置进行声明式管理——涵盖 Provider 认证配置、可管理的资源与数据源、密钥安全实践(write-only 参数)以及已知 API 限制。读完本文,你将掌握从零接入 Onyx Provider、编写首个可落地的 Terraform 配置,以及安全托管 API Key 与 LLM Provider 的完整方案。
Provider 定位:管理"运行在 Onyx 内部"的配置
terraform-provider-onyx是 Onyx 官方 Terraform Provider,其目标是在 Terraform 中以声明式方式管理Onyx 应用配置:LLM Provider、部署默认模型、API Key、工作区设置、Embedding Provider 等,全部经由 Onyx 管理 API 完成读写。官方文档的描述非常精炼:
Manage Onyx application configuration (LLM providers, API keys, workspace settings, ...) declaratively via the Onyx admin API.
需要特别区分的是:仓库 deployment/terraform 目录用于部署 Onyx 运行所需的基础设施(EKS、RDS 等),而本 Provider 配置的是某个 Onyx 部署内部运行的东西。两者互补但职责完全不同——前者回答"Onyx 跑在哪里",后者回答"Onyx 里配了什么"。
Provider 源码位于 terraform-provider-onyx 目录,核心入口是 internal/provider/provider.go,基于 HashiCorp 的terraform-plugin-framework框架实现。
Provider 认证与三项核心配置参数
Provider 的全部配置集中在endpoint、api_key、api_prefix三个参数上,均可在.tf文件中声明,也可通过环境变量注入。依据 index.md 的 Schema 定义,参数说明如下:
| 参数 | 类型 | 说明 | 环境变量 |
|---|---|---|---|
endpoint | String | Onyx 服务器源地址(origin),例如https://cloud.onyx.app或http://localhost:3000 | ONYX_SERVER_URL |
api_key | String, Sensitive | 位于种子Admin组的 Onyx API Key(on_...),或不受限的个人访问令牌(onyx_pat_...) | ONYX_API_KEY |
api_prefix | String | API 挂载的路径前缀,默认/api(Web 代理);直连后端(如http://localhost:8080)时设为"" | ONYX_API_PREFIX |
最小可用配置
terraform { required_providers { onyx = { source = "onyx-dot-app/onyx" } } } variable "onyx_api_key" { type = string sensitive = true } # Credentials can also come from ONYX_SERVER_URL / ONYX_API_KEY env vars. provider "onyx" { endpoint = "https://onyx.internal.example.com" api_key = var.onyx_api_key # an API key in the Admin group ("on_...") }其中api_prefix不写时默认取/api。当你直连后端服务(http://localhost:8080)时需显式置空,例如本地开发或接测试套件时。
配置优先级与校验逻辑
从源码看,provider.go 的Configure阶段对环境变量与属性做了明确的优先级合并:
endpoint:先取ONYX_SERVER_URL,若配置中显式设置了endpoint则以配置为准;api_key:先取ONYX_API_KEY,配置中的api_key优先;api_prefix:默认/api,ONYX_API_PREFIX可覆盖,配置中的api_prefix优先级最高。
若endpoint或api_key最终为空,Provider 会直接报错并给出创建密钥的指引。同时,Provider 拒绝接收"未知(unknown)"的配置值——当endpoint/api_key/api_prefix派生自尚未 apply 的资源时,会在 plan 阶段直接报错,而不是静默当作空值处理。
客户端实现见 internal/client/client.go:请求会同时携带Authorization与X-Onyx-Authorization两个 Bearer 头(后者优先被服务端检查,可穿透会消费Authorization头的代理),并以terraform-provider-onyx/<version>作为 User-Agent;对 429 限流一律重试,而 POST 等可能已产生副作用的请求遇到传输错误则不重放。
认证方式与 API Key 的获取
Provider 需要一个位于种子Admin组的 API Key(或无组限制的 PAT)。获取方式有两种:
- 管理面板:在 Onyx 管理后台的Settings -> Service Accounts中创建;
- 命令行:调用管理 API 手工铸造,注意必须传入 Admin 组 id——不带组的 Key 没有任何管理权限:
curl -X POST https://your-onyx/api/admin/api-key \ -H "Cookie: fastapiusersauth=<admin session>" \ -H "Content-Type: application/json" \ -d '{"name": "terraform", "group_ids": [<admin group id>]}'仓库提供了自动化脚本 examples/bootstrap/mint_api_key.sh,完整执行"注册 → 登录 → 解析 Admin 组 → 铸造 Key"的流程,适合 CI 或脚本化首次部署。它强制要求ONYX_ADMIN_EMAIL与ONYX_ADMIN_PASSWORD而不提供默认值:在无用户的部署上,脚本会注册该账号,且第一个注册的用户自动成为管理员——若给默认密码,等于在任何可达部署上悄悄创建一个已知口令的管理员。
值得注意的两点:
- API Key 的有效性与部署的人类用户
AUTH_TYPE(basic/OIDC/SAML/cloud)无关; - 在 Onyx Cloud 上,租户信息内嵌于 Key 本身。
资源与数据源总览
Provider 注册了 13 个资源与 4 个数据源(见 provider.go 的Resources/DataSources方法)。依据 README.md 汇总如下:
| 名称 | 管理内容 | Import id |
|---|---|---|
onyx_api_key | API Key(/admin/api-key) | 数字 id |
onyx_llm_provider | LLM Provider 及其模型列表(/admin/llm/provider) | 数字 id |
onyx_llm_provider_default | 部署默认(及视觉)模型——单例 | default |
onyx_settings | 工作区设置——单例,部分托管 | settings |
onyx_embedding_provider | 云端 Embedding Provider 凭据 | provider 类型(如openai) |
onyx_credential | 连接器凭据(/manage/credential) | 数字 id |
onyx_connector | 连接器定义(/manage/admin/connector) | 数字 id |
onyx_cc_pair | 连接器-凭据配对(/manage/connector/.../credential/...) | 数字 id |
onyx_document_set | 文档集(/manage/admin/document-set) | 数字 id |
onyx_custom_tool | 自定义动作(/admin/tool/custom) | 数字 id |
onyx_agent | Agent / 助手(/persona) | 数字 id |
onyx_mcp_server | Onyx 连接的 MCP 服务器(/admin/mcp) | 数字 id |
onyx_user_group | 用户组:成员、管理者、权限授予(仅企业版) | 数字 id |
data.onyx_llm_providers | 只读:Provider 列表 + 默认模型 | — |
data.onyx_embedding_providers | 只读:Embedding Provider 列表 | — |
data.onyx_settings | 只读:当前设置(含许可证tier) | — |
data.onyx_connectors | 只读:连接器列表 | — |
每个资源的生成文档位于 terraform-provider-onyx/docs 下的data-sources/与资源对应页面。
实战:一个完整的"第一天"配置
examples/bootstrap/ 提供了一个可运行的 day-one 配置:创建一个聊天模型、索引一个公开文档站点、将结果归入文档集,并添加一个从该文档集作答的 Agent,全部工作在 Community Edition 上可用(onyx_user_group是唯一例外,需开启企业特性)。
terraform { required_version = ">= 1.5" required_providers { onyx = { source = "onyx-dot-app/onyx" } } } # Reads ONYX_SERVER_URL and ONYX_API_KEY when the variables are unset. provider "onyx" { endpoint = var.onyx_server_url api_key = var.onyx_api_key }工作区与聊天模型
# 仅管理此处设置的属性。销毁该资源不会重置已写入的设置。 resource "onyx_settings" "workspace" { company_name = var.company_name } resource "onyx_llm_provider" "openai" { name = "openai" provider_type = "openai" api_key = var.openai_api_key # 完整的启用模型集合:任何被省略的模型都会在 apply 时被移除。 model_configurations = [ { name = "gpt-5" }, { name = "gpt-5-mini" }, ] } # 引用 provider id 也保证了销毁顺序:默认模型先于持有它的 provider 被释放。 resource "onyx_llm_provider_default" "this" { provider_id = onyx_llm_provider.openai.id model_name = "gpt-5" }onyx_settings是"部分托管"的单例——从源码 settings_resource.go 可见,未在配置中设置的属性在服务端保持不动,从配置中移除某属性意味着"停止管理"而非"重置"。可写属性包括company_name、maximum_chat_retention_days、anonymous_user_enabled、invite_only_enabled、deep_research_enabled、multi_model_chat_enabled、search_ui_enabled、query_history_type、user_knowledge_enabled、disable_default_assistant、craft_default_enabled等;只读属性则包括application_status、tier、ee_features_enabled、seat_count、used_seats等(部分每次读取时从后端环境变量覆盖)。
知识库:连接器、凭据与配对
# Web 连接器读取公开页面,因此其凭据不含任何秘密。 resource "onyx_credential" "web" { source = "web" name = "public-web" credential_json = jsonencode({}) } resource "onyx_connector" "docs" { name = "docs-site" source = "web" input_type = "load_state" # 每天重新索引一次。 refresh_freq = 24 * 60 * 60 connector_specific_config = jsonencode({ base_url = var.docs_base_url web_connector_type = "recursive" }) } # 配对才是真正执行索引的对象,同时承载其产出文档的访问控制。 resource "onyx_cc_pair" "docs" { name = "docs-site" connector_id = onyx_connector.docs.id credential_id = onyx_credential.web.id access_type = "public" } resource "onyx_document_set" "docs" { name = "docs" description = "Public product documentation" cc_pair_ids = [onyx_cc_pair.docs.id] }注意access_type与groups放在onyx_cc_pair上——Onyx 在凭据被关联时应用访问控制,连接器端点本身虽然要求access_type字段却会忽略它。
Agent 与企业版用户组
resource "onyx_agent" "docs" { name = "Docs" description = "Answers product questions from the documentation" system_prompt = <<-EOT You answer questions from the product documentation. If the documentation does not cover the question, say so. EOT document_set_ids = [onyx_document_set.docs.id] starter_messages = [ { name = "Getting started" message = "How do I get started?" }, ] } # 用户组需要企业版。Community Edition 上请保持关闭:Onyx 会拒绝这些路由。 resource "onyx_user_group" "platform" { count = var.enable_enterprise_features ? 1 : 0 name = "Platform" # 权限使用 Onyx 自己的令牌,而非枚举名。 permissions = [ "manage:connectors", "manage:document_sets", ] }应用与清理
cp terraform.tfvars.example terraform.tfvars # 编辑 terraform.tfvars terraform init terraform plan terraform apply凭据也可以走环境变量,从而避免写入terraform.tfvars:
export ONYX_SERVER_URL=http://localhost:8080 export ONYX_API_KEY="$(ONYX_ADMIN_EMAIL=admin@example.com \ ONYX_ADMIN_PASSWORD='...' ./mint_api_key.sh)" export TF_VAR_openai_api_key="sk-..."terraform destroy会销毁配对并后台移除其索引的文档;onyx_settings是例外——销毁只停止托管设置,不会重置它们。索引在 apply 后后台执行,Agent 需等待首个索引周期完成才能基于站点作答,可在管理后台Connectors下观察进度。
密钥安全:write-only 参数与轮换
每个 Provider 接收的密钥都有两种形态(见 README.md):
- 普通属性:值写入 Terraform state,任何能读到 state 文件的人都能读到密钥;
_wo孪生属性:Terraform 的 write-only 参数,值会从 plan 与 state 中剥离,只存在于你的配置文件中。
| 资源 | 存入 state | Write-only |
|---|---|---|
onyx_llm_provider | api_key、custom_config | api_key_wo、custom_config_wo |
onyx_embedding_provider | api_key | api_key_wo |
onyx_credential | credential_json | credential_json_wo |
onyx_mcp_server | api_token、admin_credentials | api_token_wo、admin_credentials_wo |
onyx_custom_tool | custom_headers | custom_headers_wo |
两者只能二选一设置,不能同时给出。onyx_credential因载荷必填,必须且只能提供其一。示例:
resource "onyx_llm_provider" "openai" { name = "openai" provider_type = "openai" api_key_wo = var.openai_api_key api_key_wo_version = 1 model_configurations = [{ name = "gpt-5-mini" }] }Write-only 参数要求Terraform 1.11 或更高,旧版 CLI 会拒绝包含它的配置。
三个无法使用 write-only 的例外
onyx_api_key.api_key:这是 Onyx 铸造的 Key 而非你提供的,Terraform 只能通过 state 交回生成值,须将 state 文件视同持有该 Key;onyx_mcp_server.auth_template_headers:该属性是 computed(Onyx 为共享令牌自行写入模板),Terraform 不允许一个参数既是 computed 又是 write-only,其占位符值来自admin_credentials_wo;- Provider 自身的
api_key:Provider 配置本就不会写入 state,建议直接用ONYX_API_KEY环境变量提供,而非写进.tf文件。
轮换 write-only 密钥
Terraform 从不存储的值也无法做 diff,因此仅修改api_key_wo不会触发任何计划。每个孪生属性配有一个_wo_version计数器:把它加一,产生的 diff 会让下一次 apply 发送当前密钥。
注意不要用密钥派生计数器(如md5(var.token))——计数器会存进 state,不能反推出密钥。计数器只决定何时触发 apply;由于 Onyx 更新时替换所有字段,Provider 每次 apply 都会发送当前密钥。
两个需要知晓的行为
onyx_custom_tool停止刷新其请求头:Onyx 原样返回 action headers(而非掩码),所以custom_headers能正常刷新、面板外的改动会出现在terraform plan中;但对custom_headers_wo做不到这一点(刷新会把密钥写进 state),因此面板中改动的 header 在下次 apply 覆盖前不会被报告——这是 write-only 形式唯一的让步。- 导入多一次 apply:导入读取的是服务端现状,若 Onyx 未掩码返回密钥,它会进入存储属性;第一次 apply 使用孪生属性时将其从 state 清除,资源随即转入 write-only 路径。
已知限制:API 设计使然,需要绕行而非等待
这些限制源于 Onyx API 本身的行为,属于需要设计规避的特性而非等待修复的 bug。Fixed upstream表示后端已改进,但修复尚未进入已发布的 Onyx,因此 Provider 保留绕行方案,限制仍然适用。完整清单见 README.md 的Known limitations章节,以下为要点:
密钥
- 秘密漂移不可检测:API 读取时对密钥做掩码,面板轮换对
terraform plan不可见;配置值是权威,下次 apply 会重新断言。
设置与部署默认值
onyx_settings与onyx_llm_provider_default并非真正删除:Onyx 没有重置设置 API,也没有取消文本/视觉默认值的 API,销毁只会把它们从 state 移除;聊天命名默认值有 unset API,会被真正清空。
LLM 与 Embedding Provider
onyx_embedding_provider更新会替换全部字段,配置中必须保留api_key(或api_key_wo),否则更新会清空已存密钥;当前生效的 Embedding Provider 不可删除。model_configurations是"权威记录":被省略的模型会在服务端移除,移除当前部署默认模型会失败——先改指onyx_llm_provider_default(引用顺序已正确处理)。- 模型列表读取的是 API 的显示视图:隐藏过时与重复模型,写操作无法保留未返回的行;管理面板行为一致。Fixed upstream:upsert 已支持
keep_existing_models。
凭据与连接器
onyx_credential载荷永不被读回:API 总是掩码载荷;admin_public、curator_public、groups无更新端点,会强制替换。- 私有凭据可能看起来像被删除:
admin_public = false的凭据对创建者以外的管理员隐藏,与删除不可区分,Terraform 会删了重建。托管凭据请保持默认admin_public = true。 onyx_connector不拥有访问控制:access_type与groups在配对(cc_pair)上设置;未设置prune_freq时首次更新会变成 7 天,Provider 随后将其作为权威值。
Agent 与动作
- 删除 Agent 留下墓碑:行被标记删除,名称仍被占用,同名重建会复活原 Agent,销毁-重建返回原 id 而非新 id。
- 已删除的 Agent 返回 400 而非 404,Provider 依据 Agent 列表确认存在性。Fixed upstream:路由现返回类型化的
PERSONA_NOT_FOUND。 onyx_agent并非拥有全部字段:附加的文件夹与文档省略即清空、显式 null 则被拒绝,Provider 读取后写回,存在一个往返窗口可能回滚期间的并发附加。Fixed upstream:两个字段已可空。另外search_start_date只写不读,头像不受管理。display_priority在 upsert 上仅创建时生效,后续写忽略,变更需第二次调用显示优先级端点。- 两个内置动作对 API 隐藏:
OktaProfileTool与MemoryTool不在 Agent 快照中,持有它们的 Agent 报告的tool_ids少于写入值且永不收敛。 - 删除自定义动作会将其从每个使用它的 Agent(包括 Terraform 未管理的)上摘除,且无任何报错或警告。
用户组(企业版)
onyx_user_group仅企业版存在路由,Community Edition 上所有调用返回 404。- 用户组不管理"组能看见什么":连接器、文档集、Agent、LLM Provider、MCP 服务器与凭据各自携带自己的
groups;组暴露只读的cc_pair_ids、document_set_ids、agent_ids,两侧不会争夺同一条边。 - 移除成员可能覆盖并发的连接器共享;组同步期间 Onyx 拒绝成员变更/重命名/删除,而新组从"同步中"开始,Provider 会在每次操作前等待;同步中的组在成员与删除路由上返回 404 而非冲突,因此 404 不代表组已消失。Fixed upstream:门禁现抛出
RESOURCE_SYNCING冲突。 - 权限使用 Onyx 的 wire 令牌(如
manage:connectors)而非枚举名;只有可切换的权限可设置,basic、admin、craft_sandbox、manage:skills及隐含读令牌由 Onyx 管理。 - 种子默认组(
Admin、Basic)只持有成员:管理名册可行,重命名、删除、权限与隐身变更会被拒绝。 - Onyx 拒绝会让某人脱离所有组的移除,销毁组同样受检——若某成员只有这一个组,销毁会失败;管理员权限存续、经理自移除、权限放大均有防护。
MCP 服务器
- 只管理无需交互登录的服务器:
NONE与API_TOKEN可用;OAUTH、PT_OAUTH需要浏览器往返,plan 阶段即被拒绝。 - 服务器暴露哪些工具不受管理:Onyx 通过调用服务器来学习工具,拒绝命名未见过工具的选择。
- 省略的
description会被清空而非保留;groups、users同理,从配置移除即清空服务端(含管理面板添加的条目)。 - 从管理面板添加但从未配置的服务器导入为空字符串:
auth_type、transport未设置,首次 plan 后移入 schema 默认值,一次 apply 收敛。 - 服务器 URL 不能指向 Onyx 主机:SSRF 防护在所有保护级别都拒绝
localhost与链路本地地址。
本地开发、测试与文档生成
开发需要 Go(见 go.mod)与 Terraform CLI:
go build ./... # 构建 go test ./... # 单元测试(无需 Onyx)使用本地构建
在~/.terraformrc中写dev_overrides指向本地二进制:
provider_installation { dev_overrides { "onyx-dot-app/onyx" = "/path/to/onyx/terraform-provider-onyx" } direct {} }然后go build,在任意使用该 Provider 的配置中直接terraform plan/apply(跳过terraform init)。
验收测试
验收测试针对真实 Onyx 部署执行完整 CRUD 周期(会创建/销毁 Provider 与 Key,并短暂修改工作区设置,请使用开发部署):
TF_ACC=1 ONYX_TF_ACC_SERVER_URL=http://localhost:8080 go test ./internal/provider/ -vONYX_TF_ACC_API_PREFIX默认为""(直连后端),走 Web 服务器时设为/api;- 认证:设置
ONYX_TF_ACC_API_KEY使用现有管理 Key,或让测试装置以ONYX_TF_ACC_ADMIN_EMAIL/ONYX_TF_ACC_ADMIN_PASSWORD登录自助引导(默认admin_user@example.com/TestPassword123!;全新部署上第一个注册用户自动成为管理员)。
未设置TF_ACC时这些测试自动跳过,因此go test ./...在无 Onyx 环境下保持绿色——CI 的pr-golang-tests.yml即如此,不跑验收套件;pr-terraform-provider-tests.yml是会跑验收套件的通道,它用 docker compose 拉起 api_server 与 background,并让套件跑两遍(自引导 Key 与先用mint_api_key.sh铸造的 Key)。onyx_cc_pair、onyx_document_set、onyx_user_group测试还需要 Celery 与 beat(用户组测试尤其依赖 beat 的check-for-vespa-sync每 20 秒清除同步状态,否则每次重命名/成员变更/销毁都会等到超时);配对测试特意使用mock_connector源,以覆盖全生命周期而无需真实数据源。
文档生成
docs/是生成产物——编辑 schema 的MarkdownDescription与examples/后执行:
go generate . # 运行 tfplugindocs;需要 terraform 在 PATH 上小结
terraform-provider-onyx将 Onyx 应用层配置完整地带入 Terraform 的声明式工作流:通过endpoint/api_key/api_prefix三个参数完成认证接入,13 个资源与 4 个数据源覆盖 API Key、LLM Provider、默认模型、工作区设置、连接器与凭据、文档集、Agent、MCP 服务器乃至企业版用户组;write-only 孪生属性与_wo_version计数器为密钥安全与轮换提供了 state 之外的路径。理解其"API 设计使然"的已知限制(秘密漂移不可检测、部分资源不可真删、删除行为与 404 语义等),即可在真实部署中做出正确的建模与绕行设计。
【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考