首页
/ Corsair 集成指南:从零到生产环境的用户应用连接方案

Corsair 集成指南:从零到生产环境的用户应用连接方案

2026-09-14 10:39:26作者:董灵辛Dennis

导读

Corsair 是当前仓库(corsa/corsair)中的核心 TypeScript 集成框架,它帮助你的应用通过 OAuth 或 API Key 连接 GitHub、Slack、Linear、Stripe、Gmail 等数百个外部服务,并把连接后的凭证加密存储在你自己的数据库中,供 Agent、UI、Webhook、知识库等消费。本文以仓库内 skills/corsair/SKILL.md 为骨架,完整覆盖从规划、安装、配置、路由挂载、账户连接、API 调用、Webhook、工作流、MCP 接入、权限审批到生产发布的端到端流程,并结合 packages/corsair 源码说明底层实现原理,读完后你可以把任意 TypeScript 应用(Next.js、Express、Hono、SvelteKit、Astro、Nuxt、Remix 乃至 Bun/Deno/Workers)接入 Corsair 并跑通真实数据调用。


一、Corsair 的核心模型与两个贯穿原则

1.1 先理解四个术语

在整个集成过程中,你会反复遇到四个术语,它们构成了 Corsair 的领域模型:

  • tenant(租户):应用的某个最终用户。多租户模式下(multiTenancy: true),所有连接、凭证、数据都按租户隔离。
  • plugin(插件):一个外部服务,如 githubslacklinear。插件包按 @corsair-dev/<service> 作用域发布。
  • delivery URL(投递地址):Hub 向你的应用回传结果(OAuth token、Webhook、连接状态等)的地址。它不需要写进配置文件——首次请求时自动向 Hub 注册,仪表盘头部的小圆点变绿即表示投递已打通。
  • Hub:Corsair 托管的控制面,负责托管 OAuth 授权页、环境管理与密钥发放;它不存储任何凭证数据。

1.2 贯穿始终的两条规则

SKILL.md 明确了两条规则,它们是集成是否成功的关键:

  1. 动手接线前先读真实文档:仓库根目录的 docs/docs.json 聚合了全部文档索引,其中的 docs/hub/overview.mdxdocs/hub/setup.mdx 等页面描述了 Hub 模式的核心概念,动手前应先通读这些内容。
  2. 服务跑起来不算完成,真实 API 调用返回数据才算完成。这条规则贯穿全文:连接账户、跑通调用、验证端到端,缺一不可。

1.3 架构前提:Corsair 需要一个服务端

/api/corsair 路由持有签名密钥并接收 Hub 的服务端到服务端投递,因此 Corsair 需要服务端,不能跑在纯客户端 SPA 上。如果你用的是 Angular、Vue 或纯前端 Svelte 且没有后端,需要把该路由挂载到 SSR 服务、Express 或 Hono 上;如果后端不使用 JavaScript,则可以跳过 SDK,改用 Hub REST API(对应源码 packages/corsair/hub/route-handlers.ts)。


二、Phase 1:写代码前的规划

2.1 学习模型

首先阅读文档索引,重点先看介绍与 Hub 页面,建立对数据流(应用 ↔ Hub ↔ 外部服务)的整体认知。

2.2 扫描项目,只问空白

SKILL 建议先探测项目现状,能检测到的直接预填,只对信息缺口提问:

检测项 信息来源
框架 next.config.*svelte.config.*nuxt.config.*astro.config.*、Express/Hono 入口、package.json 依赖
是否已装 Corsair 存在 corsair 依赖、已有 /api/corsair 路由、存在 corsair.ts、或已有 CORSAIR_* 环境变量 → 应重配置而非重新脚手架
包管理器 lockfile(pnpm-lock.yaml / package-lock.json / yarn.lock
数据库 已有的 DB 句柄或 ORM;否则最快路径是用 better-sqlite3 走 SQLite

2.3 敲定两个决定整体形态的选择

SKILL 强调这两个问题要用对话式讨论,而不是非黑即白的 yes/no:

  • 连接谁的账号? 只是应用自己的工具账号,还是最终用户的账号?用户账号意味着多租户(multiTenancy: true),这是最常见的情况,不确定时默认倾向多租户。
  • 用哪个框架? 它决定了路由的挂载方式(见第五章的适配器表)。

2.4 先确认计划再动代码

在改动任何文件前,先把整体方案(框架、数据库、租户模型、要接的插件)总结出来并征得确认。


三、Phase 2:实施总览

SKILL 给出三条路径的实施决策树:

New / empty app?  ── install → corsair.ts → /api/corsair route → keys in .env
                                → start server (dot goes green) → connect an account → real API call
Adding to an app? ── detect stack → install → add route → keys → connect the services named
Already wired?    ── reconcile corsair.ts → add the new plugins/features → re-check keys
  • 全新/空应用:安装 → 写 corsair.ts → 挂 /api/corsair 路由 → 配置 .env 密钥 → 启动服务(圆点变绿)→ 连接账户 → 真实 API 调用。
  • 已有应用接入:探测技术栈 → 安装 → 加路由 → 配密钥 → 连接指定服务。
  • 已接入再扩展:核对既有 corsair.ts → 新增插件/功能 → 复查密钥。

四、安装与密钥环境

4.1 安装

npm install corsair @corsair-dev/github

多个服务一次安装:

npm install corsair @corsair-dev/slack @corsair-dev/linear

插件包统一以 @corsair-dev/<service> 作用域命名,完整服务目录可查看 docs/integrations 相关文档 与仓库中各插件包(如 packages/githubpackages/slack)。集成哪个服务不确定时应当提问确认,不要凭仓库名猜测。

4.2 生成 KEK(密钥加密密钥)

KEK 是信封加密(envelope encryption)的根密钥:它包裹每个连接独立的 DEK(数据加密密钥),DEK 再加密每条凭证。KEK 一旦丢失,所有已存储的凭证都无法恢复,必须妥善保管:

openssl rand -base64 32

在源码中,KEK 的加解密逻辑集中在 packages/corsair/core/auth/encryption.ts,密钥管理见 packages/corsair/core/auth/key-manager.ts;缺少 KEK 时会抛出 CorsairKekMissingError(定义于 packages/corsair/core/auth/errors/kek-missing.ts),从源码结构看,这是初始化时的第一道守卫。

4.3 获取密钥并写入 .env

Corsair 需要三个东西:开发 API Key、签名密钥(signing secret)和 KEK。API Key 的前缀(ck_dev_ck_prod_)告诉 SDK 当前处于哪个环境。

最快路径:生成一个带项目名的登录链接,用户用 Google 登录后,工作区和项目自动创建,onboarding 界面直接展示密钥:

https://hub.corsair.dev/login?title=My%20App

偏好仪表盘的话,在 https://hub.corsair.dev/dashboard 创建项目,从 Keys 标签页复制同样的密钥。

CORSAIR_DEV_API_KEY=ck_dev_...
CORSAIR_DEV_SIGNING_SECRET=...
CORSAIR_KEK=...

生产环境改用 CORSAIR_PROD_API_KEYCORSAIR_PROD_SIGNING_SECRETCORSAIR_KEK 保持一致。绝不记录或提交这些密钥到日志、版本库


五、编写 corsair.ts 并理解每个配置项

5.1 最小可用配置

import "dotenv/config";
import { createCorsair } from "corsair";
import { github } from "@corsair-dev/github";

export const corsair = createCorsair({
  kek: process.env.CORSAIR_KEK!,
  database: db, // the app's own DB handle; Corsair persists here, Hub stores nothing
  hub: {
    projectApiKey: (process.env.CORSAIR_PROD_API_KEY ?? process.env.CORSAIR_DEV_API_KEY)!,
    signingSecret: (process.env.CORSAIR_PROD_SIGNING_SECRET ?? process.env.CORSAIR_DEV_SIGNING_SECRET)!,
  },
  plugins: [github()],
  multiTenancy: true, // from the "whose accounts?" choice
});

仓库内的最小示例见 demo/minimal/corsair.ts:它用 slack()linear()resend() 三个插件演示了 withTenant 租户隔离与全类型推断的 API/DB 调用。

5.2 字段速查表

字段 作用
kek 信封密钥。包裹每个连接的 DEK,DEK 加密每条凭证。
database 应用的数据库句柄。Corsair 会创建五张表:corsair_integrationscorsair_accountscorsair_entitiescorsair_eventscorsair_permissions
hub { projectApiKey, signingSecret },Hub 模式配置。
plugins 配置好的插件工厂,例如 github({ authType: "managed" })
multiTenancy true 时所有数据按最终用户隔离;false 用于应用自己的工具账号。
permissions 可选的审批门控,见 docs/concepts/permissions.mdx

5.3 源码佐证:五张表与工厂函数

五张表的类型定义在 packages/corsair/db/kysely/database.tsCorsairKyselyDatabase),其中 corsair_accountsdek 字段直接对应 SKILL 中"每个连接独立 DEK"的设计。createCorsair 工厂的重载实现于 packages/corsair/core/index.ts:传入 multiTenancy: true 时返回带 withTenant() 方法的 CorsairTenantWrapper;不传时返回直接可用的 CorsairSingleTenantClient,且两者都暴露 keys(集成级密钥管理)与 manage(管理命名空间)属性。

关于 Hub 配置,packages/corsair/hub/config.tsnormalizeHubConfig 会校验 projectApiKeysigningSecret 非空,缺失即抛 HubCredentialsMissingError;同时 ck_dev_ 前缀的开发密钥默认自动开启本地隧道(tunnel: false 可显式关闭),对应 maybeStartTunnelsuperviseTunnel 的逻辑——这解释了为什么 SKILL 说开发环境无需配置投递 URL。

5.4 数据库接入形式

database 字段接受 pg 的 Poolbetter-sqlite3 实例、postgres.jsSql 或 Kysely 实例(见 packages/corsair/db/kysely/database.tsCorsairDatabaseInput)。demo 中的 PostgreSQL 示例见 demo/minimal/db.ts


六、添加 /api/corsair 路由(各框架适配器)

每个框架都有各自的适配器包装共享处理器(对应源码 packages/corsair/core/management/adapters 下的 express.ts、fastify.ts、hono.ts、next.ts 等):

框架 路由写法
Next.js(App Router),文件 app/api/corsair/[[...path]]/route.ts export const { GET, POST, OPTIONS } = toNextJsHandler(corsair, { basePath: "/api/corsair" });
Express app.use("/api/corsair", toExpressHandler(corsair, { basePath: "/api/corsair" }));不要挂载 body parser:全局 express.json() 若出现在该路由之前会重新序列化 body,破坏 Hub 的签名校验。如果有全局 parser,应将 express.raw({ type: "application/json" }) 限定到本路由、全局 parser 注册在其后,或捕获 req.rawBody
Hono app.all("/api/corsair/*", toHonoHandler(corsair, { basePath: "/api/corsair" }));
SvelteKit / Astro export const { GET, POST, OPTIONS } = toSvelteKitHandler(corsair, { basePath: "/api/corsair" });。Astro 用同形状的 toAstroHandler
Remix / React Router export const { loader, action } = toRemixHandler(corsair, { basePath: "/api/corsair" });
Nuxt / Nitro export default toNuxtHandler(corsair, { basePath: "/api/corsair" });。尽早挂载,置于任何 body parser 之前。
Web 运行时(Bun、Deno、Workers) toWebHandler(corsair, { basePath: "/api/corsair" }),返回 (Request) => Promise<Response>

各框架的完整细节见 docs/adapters/handlers.mdx。测试方面,仓库提供了 packages/corsair/tests/framework-body-fidelity.test.tspackages/corsair/tests/framework-realserver.test.ts,专门验证各框架下 body 保真与真实服务端行为——这正是 SKILL 反复强调"body parser 顺序破坏签名"的测试依据。

启动服务后,首次请求会自动向 Hub 注册投递 URL,仪表盘圆点变绿即表示接线已生效。投递 URL 的自动解析逻辑在 packages/corsair/hub/resolve-delivery-url.ts:优先取 CORSAIR_DELIVERY_URL 环境变量,否则自动推导 http://localhost:{PORT}/api/corsair;它不会从入站请求头读取投递地址。


七、连接账户(Connect Link)

7.1 服务端铸造连接链接

在服务端铸造 connect link,让用户跳转完成授权。tenantId 必须来自会话(session),绝不能来自用户输入

const { connectUrl } = await corsair.manage.connect.createLink({
  plugin: "github",
  tenantId: session.userId,
});
// redirect the user to connectUrl

corsair.manage 命名空间对应源码 packages/corsair/core/management/index.tsbuildManagementNamespace,而 createLink 等核心操作定义在 packages/corsair/core/management/operations.ts。从源码看,createConnectLink 会根据是否配置 hub 分流:Hub 模式走 createHubConnectSession(托管 OAuth 会话,见 packages/corsair/hub/connect.ts),非 Hub 模式走 createManualConnectLink(需要 manual: { baseUrl, redirectUri } 配置并校验 OAuth 客户端凭证)。

7.2 React 客户端

React 中可用 createCorsairReactClient({ baseURL }) 及其 useCreateConnectLinkuseConnectionStatus hooks,详见 docs/adapters/react.mdx,对应实现位于 packages/corsair/client/react

用户授权发生在 Hub 托管页面上,授权完成后 token 加密落入你自己的数据库(Hub 不存储凭证)。


八、调用 API 与读取落库数据

8.1 直接调用

// call a service
await corsair.slack.api.messages.post({ channel, text });

// read auto-persisted responses back out of the app's DB
await corsair.github.db.repositories.search({});

8.2 多租户隔离

多租户应用通过租户作用域调用,所有请求与查询都限定在该租户:

const tenant = corsair.withTenant("user_123");
await tenant.github.api.repositories.list({});
await tenant.github.db.repositories.search({}); // only this tenant's rows

withTenant 的返回包装器在 packages/corsair/core/index.ts 中实现,它是多租户模式下的统一入口。demo 示例 demo/minimal/corsair.ts 展示了完全类型推断的调用体验:tenant.slack.api.messages.post(...) 的返回值(如 message.tsmessage.channel)以及 tenant.linear.db.issues.list() 的行结构在编辑器中都能获得自动补全。

8.3 用 CLI 探索可用操作

需要查看当前实例支持哪些 API/DB 操作时,安装 Corsair CLI 并使用内省(introspection)命令:

npm install @corsair-dev/cli

npm corsair list # for api operations

npm corsair list --type=db # for db operations

npm corsair schema <endpoint> # to get input / output schema of any operation

CLI 实现位于 packages/cli,内省能力由 packages/corsair/core/inspect 提供。


九、Providers:托管 vs 自带凭证(authType)

authType 是插件配置中的关键字段,共有三种取值:

authType 适用场景 配置示例
"managed" 最快路径。使用 Corsair 托管的 OAuth 应用,无需自行注册 provider github({ authType: "managed" })
"oauth_2" 用户自带 OAuth 应用 linear({ authType: "oauth_2", credentials: { clientId, clientSecret } })
"api_key" 静态 API Key 或 bot token slack({ authType: "api_key", credentials: { botToken } })

哪些服务支持自带 OAuth,见 docs/concepts/auth.mdxdocs/guides/plugin-credentials.mdx。从源码看,packages/corsair/core/management/operations.tsgetIntegrationCredState 会按 authType 校验凭证完整性:OAuth2 要求 client_id + client_secret 齐全(redirect_url 视为可选),其余类型要求所有必填字段齐全,未配置时 /plugins/connection-status 仍返回 200 而非报错。


十、Webhook 与触发器

10.1 一个端点处理一切

processWebhook(corsair, headers, body, { tenantId }) 会完成:识别 integration、事件与租户 → 校验签名 → 写入数据库 → 执行 hooks。按插件用 secret 启用,并用 webhookHooks 响应:

github({
  webhookSecret: process.env.GITHUB_WEBHOOK_SECRET,
  webhookHooks: {
    pullRequestOpened: {
      after: async (ctx, result) => {
        await corsair.withTenant(ctx.tenantId).slack.api.messages.post({ channel: "#eng", text: "PR opened" });
      },
    },
  },
});

核心处理器在 packages/corsair/async-core/webhook-handler.tsBaseWebhookHandler 提供 on/off/clearHandlers 事件注册),事件解析与租户匹配见 packages/corsair/core/webhookspackages/corsair/core/webhooks/tenant-match.ts。另外 packages/corsair/core/webhooks/tenant-links.ts 负责 OAuth 场景下把回调与租户关联。

10.2 租户路由与 before 钩子

  • 通过 webhook URL 上的查询参数路由租户:?tenantId=user_abc123
  • before 钩子可以改写参数,或返回 { continue: false } 跳过执行。

完整说明见 docs/guides/webhooks.mdx


十一、工作流(Workflows)

工作流是基于上述 webhook hooks 构建的事件驱动自动化:事件触发 → after 钩子执行 → 调用任意插件 API。它没有独立引擎,就是纯 TypeScript。对于重型或长耗时任务,应从钩子中卸载到任务队列(job queue),仓库为此提供了 docs/guides/inngest.mdxdocs/guides/temporal.mdxdocs/guides/trigger-dev.mdxdocs/guides/hatchet.mdx 四份对接指南。

运行记录相关的命名空间(corsair.runs)与工作流执行实现见 packages/corsair/core/runspackages/corsair/workflows/execute.ts


十二、通过 MCP 把 Corsair 暴露给 AI Agent

12.1 安装与三个核心工具

安装 @corsair-dev/mcp,它通过三个工具把 Corsair 的能力暴露给 Agent,无需手动接线 schema:list_operationsget_schemarun_script。MCP 包源码位于 packages/mcp,其 buildCorsairTools(见 packages/corsair/adapters.ts)会把 API 操作转换为框架无关的工具:操作路径(如 slack.api.channels.list)被自动清洗成符合 OpenAI function-calling 命名约束的工具名,输入 schema 由 Zod 推导,execute 按点分路径动态解析并调用。

12.2 Claude Code 的 stdio 服务器

Claude Code 使用 stdio 服务器(mcp-server.ts):

import "dotenv/config";
import { runStdioMcpServer } from "@corsair-dev/mcp";
import { corsair } from "./corsair";

runStdioMcpServer({ corsair }).catch((err) => {
  console.error("[corsair-mcp] Fatal:", err);
  process.exit(1);
});

.mcp.json 中注册后用 /mcp 确认。Vercel AI SDK、OpenAI Agents、Mastra、Cursor、Codex 各有独立适配器(见 packages/mcp/src/adapters 下的 vercel-ai.ts、openai-agents.ts、mastra.ts、claude.ts 等),完整说明见 docs/mcp-adapters/mcp-adapters.mdx


十三、权限与审批(Permissions)

对高风险操作做门控。根级配置:

permissions: { timeout: "30m", onTimeout: "deny", mode: "asynchronous" }

每个插件可设置 modeopen | cautious | strict | readonly)与按操作覆盖(overrides),例如:

"repositories.delete": "deny",
"releases.create": "require_approval"

被标记为 require_approval 的操作会向 corsair_permissions 表写入一条带 token 的记录;批准后 Agent 可重试该操作。实现见 packages/corsair/core/permissions(含 PermissionRequiredErrorrunReadonly 等导出,见 packages/corsair/core/index.ts 的 re-export),审批记录会附带审批 URL(enrichPermissionWithApprovalUrl)。完整语义见 docs/concepts/permissions.mdx


十四、走向生产(Go to production)

  1. 配置生产密钥:在部署环境中设置 CORSAIR_PROD_API_KEYck_prod_ 前缀)与 CORSAIR_PROD_SIGNING_SECRET。前面 corsair.ts 中的 CORSAIR_PROD_* ?? CORSAIR_DEV_* 写法会自动优先使用生产密钥,ck_prod_ 前缀把 SDK 切换到生产环境。
  2. 注册投递 URL:在仪表盘 Delivery URLs 标签页注册应用的公网 HTTPS 投递地址并激活生产环境。开发环境自动注册,生产环境是显式操作。相关说明见 docs/hub/environments.mdxdocs/hub/delivery-urls.mdx
  3. 部署

部署到 Vercel 时,可在 Hub 仪表盘连接 Vercel,把生产密钥直接推送到项目的环境变量,省去手动复制。


十五、端到端验证与安全检查

15.1 验证清单(缺一不可)

SKILL 明确:服务器能跑不代表任务完成,必须确认以下三项:

  • 仪表盘圆点变绿(投递 URL 已自动注册);
  • 账户能通过铸造的 connect link 完成连接;
  • 一次真实的 API 调用成功返回数据。

15.2 安全红线

  • 绝不记录或暴露签名密钥、KEK、租户 token 或插件凭证。
  • 投递 URL 来自应用自身配置,绝不从入站请求头读取;不要试图用请求头"修正"它。
  • tenantId 来自会话,绝不来自用户输入。
  • 当技术栈、插件、租户或数据库存在歧义时询问用户,不要凭仓库元数据猜测。

参考资源(仓库内对应文件)

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.21 K
2.81 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
945
1.86 K
docsdocs
暂无描述
Markdown
906
5.84 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
537
607
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
864
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.28 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.39 K
1.48 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
550
401
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.19 K
347