news 2026/9/12 9:53:22

AIRI 后端(AIRI Backend)本地部署与 Railway 生产部署完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AIRI 后端(AIRI Backend)本地部署与 Railway 生产部署完整指南

AIRI 后端(AIRI Backend)本地部署与 Railway 生产部署完整指南

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

本篇技术指南围绕 Project AIRI 的托管后端(hosted backend)展开,全面讲解server/目录的架构布局、本地一键启动流程(pnpm dev:backend+ Docker Compose 五服务栈)、以及基于 Railway 的生产部署方式(Resource API 与 Auth 双服务、Config File Path、健康检查与定向私有链接配置)。读完本文,你将掌握如何在本地跑起包含 PostgreSQL(vchord 向量扩展)、Redis、API、Auth 与 Caddy 网关的完整后端,并按照"服务到服务契约"在 Railway 上正确配置跨服务变量与迁移归属,避免常见的 JWT issuer 不匹配与迁移竞态问题。

后端在仓库中的位置与总体职责

AIRI 的托管后端源码集中在仓库的server/目录下。这里需要注意一个明确的边界:应用仓库只承载服务源码与数据库归属,生产部署拓扑(生产 Caddy 路由、OpenTelemetry Collector 配置、可观测性存储、Grafana 面板)存放在proj-airi/airi-railway,避免部署拓扑在应用仓库内重复维护(参见 server/README.md)。

从仓库根目录看,后端整体由以下部分组成:

  • server/apps/api:资源 API(Resource API),承载业务域、数据库迁移与 API 运行时;
  • server/apps/auth:独立的 Better Auth 与 OIDC 服务;
  • server/packages/auth-shared:Auth 归属的数据库 schema 与主体验证(principal)契约;
  • server/packages/server-sdk-shared:托管聊天 WebSocket 的 Eventa 契约;
  • server/dev/caddy:仅本地使用的公共边缘路由(为共享的 Auth/API 来源服务);
  • server/docker-compose.yaml:完整的本地 API + Auth + PostgreSQL + Redis + Caddy 栈。

本地运行:一条命令拉起完整后端

从仓库根目录执行:

pnpm dev:backend

该命令在根 package.json 中定义为:

"dev:backend": "docker compose -f server/docker-compose.yaml up --build"

它会读取 server/docker-compose.yaml,构建并启动全部服务,但只对外暴露 Caddy 网关http://localhost:6112,API 与 Auth 的容器端口保持在内部网络,不直接暴露给宿主机。

本地栈的五个服务

server/docker-compose.yaml 定义了完整的本地后端栈(compose 项目名为proj-airi-backend):

服务镜像 / 构建来源关键配置
dbghcr.io/tensorchord/vchord-postgres:pg18-v1.0.0端口127.0.0.1:5435:5432,挂载./apps/api/sql/init.sql/docker-entrypoint-initdb.d/init.sql,健康检查pg_isready
redisredis:7-alpine端口127.0.0.1:6379:6379,健康检查redis-cli ping
apiserver/apps/api/Dockerfile启动命令pnpm -F @proj-airi/api-server start,依赖 db/redis 健康后启动
authserver/apps/auth/Dockerfile启动命令pnpm -F @proj-airi/auth-server start,依赖 api 健康后启动
caddycaddy:2-alpine端口127.0.0.1:6112:3000,挂载./dev/caddy/Caddyfile

注意两个细节:

  1. 数据库使用 vchord PostgreSQL。初始化脚本 server/apps/api/sql/init.sql 的内容只有一行:

    CREATE EXTENSION vchord CASCADE;

    也就是说本地数据库首次初始化时会启用vchord向量扩展——这是 AIRI 后端数据库带向量检索能力的直接证据(vchord 为 pgvector 兼容的向量索引方案)。

  2. API 与 Auth 通过 env_file 读取可选环境文件./apps/api/.env./apps/api/.env.localrequired: false),并以environment块注入核心变量。本地栈中 API 的AUTH_SERVER_URL与 Auth 的PUBLIC_URL均被显式设为http://localhost:6112,与 Caddy 网关地址一致,保证本地 JWT issuer 校验一致。

本地 Caddy 路由规则

server/dev/caddy/Caddyfile 是本地公共边缘的唯一入口,规则清晰对应生产语义:

:3000 { route { @internal path /internal /internal/* respond @internal 404 @auth path /api/auth /api/auth/* /auth /auth/* /.well-known/oauth-authorization-server/api/auth reverse_proxy @auth auth:3000 reverse_proxy api:3000 } }

要点:

  • /internal/*在公共边缘直接 404 拒绝,保证内部 Auth→API 调用边界不暴露;
  • Auth 相关路径(/api/auth/*/auth/*以及 OAuth 授权服务器发现端点)反向代理到auth:3000
  • 其余请求全部转发到api:3000

按服务单独开发的命令

若需要源码级调试,可以跳过 compose 而分别启动两个服务。API 侧(见 server/apps/api/README.md):

pnpm -F @proj-airi/api-server dev pnpm -F @proj-airi/api-server typecheck pnpm -F @proj-airi/api-server exec vitest run pnpm -F @proj-airi/api-server build

Auth 侧(见 server/apps/auth/README.md):

pnpm -F @proj-airi/auth-server dev

Auth 服务从自身目录读取.env.localPUBLIC_URL是经 Caddy 对外呈现的公共 issuer 来源;RESOURCE_SERVER_URL是用于内部调用的私有资源 API 地址。

两个服务的职责边界

Resource API(@proj-airi/api-server

根据 server/apps/api/README.md,其职责包括:

  • Hono 业务 API 与 WebSocket 端点;
  • 角色(characters)、聊天(chats)、提供商(providers)、Flux、Stripe、模型路由与计费;
  • 共享数据库的 PostgreSQL 迁移所有权:Drizzle 在启动时读取检入(checked-in)的drizzle/journal 与 SQL 文件;
  • Redis 缓存、配置 KV 与跨实例 Pub/Sub;
  • 通过公共 JWKS 本地校验 Auth 签发的 OIDC JWT。

从源码布局看,API 路由覆盖了routes/下的chat-ws(v1/v2 两代 WebSocket 协议,含未认证对等方与 payload 限流)、openai/v1(OpenAI 兼容网关,含 billing、telemetry、traffic-control 中间件与 chat-completions/speech-catalog/speech-generation 操作)、stripe(checkout/webhook 与价格目录)、characterschatsprovidersvoice-packsfluxaudio-speech-wsaudio-transcription-stream等模块;services/domain/下则承载计费、llm-router(并发账本、配置加载、密钥轮换、错误映射)、llm-tracing、provider-catalog、user-deletion 等业务域。它同时提供/readyz健康检查与 OpenTelemetry 仪表(见src/otel/gauges/下的 db-pool、tts-pool、ws-online-users 等 gauge)。

Auth(@proj-airi/auth-server

根据 server/apps/auth/README.md,其职责包括:

  • Better Auth 会话、社交登录、magic-link、密码与 OIDC 流程;
  • /api/auth/*/auth/*与认证发现端点;
  • Auth 归属的 Redis 配置、事务邮件与认证遥测;
  • 删除业务数据前通过私有网络调用资源 API。

其代码刻意保持扁平,主要边界为:auth.ts(Better Auth 配置与身份生命周期钩子)、routes.ts(完整公共 Auth HTTP 面与请求认证)、server.ts(依赖组合、健康检查与进程生命周期)、resource-api.ts(唯一的 Auth→资源 API 私有边界)、rate-limit.tsotel.ts(跨路由运维策略)、email.tsoidc-jwt-bearer.ts(大型外部集成模块)。

明确不做的事:产品 API、计费、模型路由、聊天或 WebSocket 业务状态;导入server/apps/api的模块;在正常进程启动时运行共享数据库迁移历史。Auth 的表与主体验证契约全部来自@proj-airi/auth-shared,共享迁移文件由 API 启动时由 Drizzle 读取——迁移所有权始终在 API 侧。

Railway 生产部署

API 与 Auth 是同一仓库构建出的两个独立 Railway 长期运行服务。核心约束是:每个服务的 Root Directory 必须保持在仓库根目录,因为两份 Dockerfile 都需要从该构建上下文复制 workspace 清单与共享包。

两份 Dockerfile 的构建输入

API 的 server/apps/api/Dockerfile(生产用 Railway 专用版位于 server/apps/api/production/railway/Dockerfile,内容一致)基于node:24-alpine,启用 corepack,复制pnpm-lock.yamlpnpm-workspace.yamlpackage.jsontsconfig.jsonpatches/,再复制server/apps/apiserver/packages/auth-sharedserver/packages/server-sdk-shared,随后以--frozen-lockfile --ignore-scripts安装依赖,先后构建@proj-airi/server-sdk-shared@proj-airi/api-server,并以非 root 用户airi运行,EXPOSE 3000

Auth 的 server/apps/auth/Dockerfile 与之类似,但只复制server/apps/authserver/packages/auth-shared(它不消费 server-sdk-shared),安装命令带--filter @proj-airi/auth-server...过滤。

在 Railway 中显式配置 Config File Path

由于仓库根目录下没有默认的railway.toml,必须为每个服务显式指定 Config File Path:

服务Config File Path公共角色私有依赖
Resource API/server/apps/api/railway.toml产品与资源 APIAuth issuer 与 JWKS
Auth/server/apps/auth/railway.tomlBetter Auth 与 OIDC issuerResource API 的删除端点

server/apps/api/railway.toml 的实际内容:

[build] builder = "DOCKERFILE" dockerfilePath = "/server/apps/api/production/railway/Dockerfile" watchPatterns = [ "server/apps/api/**", "server/packages/auth-shared/**", "server/packages/server-sdk-shared/**", "package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", "tsconfig.json", "patches/**" ] [deploy] startCommand = "pnpm -F @proj-airi/api-server start" healthcheckPath = "/readyz" healthcheckTimeout = 100

server/apps/auth/railway.toml 与之对应,dockerfilePath 为/server/apps/auth/Dockerfile,watchPatterns 不包含server/packages/server-sdk-shared/**,启动命令为pnpm -F @proj-airi/auth-server start

每一份 config 都自持 Dockerfile、启动命令、/readyz健康检查与 watch 模式。只有当变更触及该服务本身、其复制的某个共享包、或复制的根构建输入时,该服务才会触发部署——例如只修改server/apps/api/**不会让 Auth 重新构建。

服务到服务的变量契约(关键)

共享数据库、Redis 与可观测性变量,应使用Railway 引用变量(reference variables)在两个服务间传递,而不是复制敏感值。两个方向的私有链接按如下配置:

消费方变量值来源用途
Resource APIAUTH_SERVER_URLAuth 的规范公共 issuer URLJWT issuer、audience 与公共 JWKS 身份
Resource APIAUTH_SERVER_INTERNAL_URLAuth 的 Railway 私有域名私有网络内拉取 JWKS;不会改变 issuer 校验
AuthPUBLIC_URLAuth 的规范公共 issuer URLBetter Auth 与 OIDC issuer URL;必须等于 API 的AUTH_SERVER_URL
AuthRESOURCE_SERVER_URLAPI 的 Railway 私有域名删除用户业务数据前的私有调用

两个最容易踩的坑

  1. issuer 必须严格一致PUBLIC_URLAUTH_SERVER_URL必须是同一个值。否则 API 校验 JWT 的 issuer/audience 会失败。AUTH_SERVER_INTERNAL_URL只是私有 JWKS 拉取通道,即便它指向私有域名,也不影响 issuer 与 audience 的校验逻辑(issuer 依旧取AUTH_SERVER_URL)。
  2. /internal/*必须保持私有:Auth 通过私有域名调用 API 的内部路径;公共路由(Caddy 边缘)必须拒绝/internal/*,且 API 服务不应有独立的公共入口(参见 server/apps/api/README.md 与 Caddyfile 中respond @internal 404的实现)。

代理信任与限流

只有在直接接收 Railway 代理流量的服务上才设置RATE_LIMIT_TRUSTED_PROXY=railway。该标志会告知限流中间件(server/apps/api/src/middlewares/rate-limit.ts与 Auth 的rate-limit.ts)信任 Railway 代理转发,从而正确解析客户端真实 IP;误设会导致任意请求伪造X-Forwarded-For绕过限流。

迁移归属与部署成功判据

  • API 是共享数据库迁移的唯一 owner:不要给 Auth 添加 Railway pre-deploy 迁移命令,也不要让 Auth 启动时执行共享迁移。共享迁移在 API 启动时由 Drizzle 读取drizzle/journal 与 SQL 文件执行(API 侧迁移文件位于 server/apps/api/drizzle/ 下,包含 0000~0022 共 23 个 SQL 迁移及对应 snapshot)。
  • 部署成功 ≠ 就绪:任一个服务部署后,Railway 必须从该服务的/readyz收到200。健康检查超时在 railway.toml 中配置为healthcheckTimeout = 100。只有当/readyz返回 200,才说明服务能连上其依赖(数据库、Redis、对端服务);仅凭"部署完成"不能证明服务可达。

包边界与契约的归属

AIRI 对后端包的放置有明确规则(见 server/README.md):

  • 前端应用保持在根apps/下;
  • 定义了资源 API 协议的托管后端包可以放在server/packages/下,即使前端消费其生成的契约(例如 server/packages/server-sdk-shared 承载托管聊天 WebSocket 的 Eventa 契约);
  • 跨运行时的服务器 SDK 与协议包仍放在根packages/下,因为 Web、Electron、插件与独立服务都要消费它们;
  • server/packages/auth-shared 是 Auth 归属的数据库 schema 与主体验证契约:Auth 表结构集中于此,API 与 Auth 均引用它,而二者互不 import 对方的应用模块(见 server/apps/api/README.md:no module under server/apps/auth is imported)。

这种"协议归服务、跨运行时归根包"的划分,配合 Railway 的 watchPatterns 精准控制构建触发范围,让 API 与 Auth 两个服务在共享同一仓库、同一数据库的前提下,仍能保持独立部署与演进。

生产可观测性拓扑的去向

生产环境的 Caddy 路由、OpenTelemetry Collector 配置、可观测性存储与 Grafana 面板不在本应用仓库内,而统一维护在proj-airi/airi-railway,以保持"应用仓库 = 代码与契约"、"部署仓库 = 拓扑与观测"的职责分离。应用仓库内只保留本地开发用的 Caddyfile 与 server/docker-compose.yaml,如需扩展本地可观测性,可围绕server/apps/api/src/otel/的仪表与脚本(server/apps/api/src/scripts/otel/下的 http-smoke、ws-smoke、smoke)自行接入。

小结

AIRI 后端的核心设计可以概括为三条主线:

  1. 本地一条命令可复现pnpm dev:backend拉起 vchord PostgreSQL + Redis + API + Auth + Caddy 完整栈,且只有 Caddy 网关暴露在localhost:6112
  2. 生产两服务强契约:API(资源域 + 迁移 owner)与 Auth(身份与 OIDC issuer)共享同一数据库,通过PUBLIC_URL == AUTH_SERVER_URLAUTH_SERVER_INTERNAL_URL(私有 JWKS)、RESOURCE_SERVER_URL(私有删除回调)四个变量 + 私有/internal/*边界完成安全互通;
  3. 部署粒度精细可控:每份 railway.toml 自持 Dockerfile、start command、/readyz与 watchPatterns,只有触及服务自身或其复制的共享包才触发部署,配合"部署成功需以/readyz200 为准"的判据,保障变更安全。

如需在 Railway 上部署,请严格按照"Root Directory 保持仓库根目录 + Config File Path 指向各自 railway.toml + 定向私有链接 + 迁移仅由 API 执行"的契约配置;任何对PUBLIC_URL/AUTH_SERVER_URL的改动,都需要同时同步到两个服务后一并部署验证。

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 9:52:19

ESP32蓝牙测距实战:从RSSI物理本质到工业级空间感知

1. 这不是“蓝牙通信”,是物理世界里的厘米级空间感知很多人第一次看到“ESP32 蓝牙 beacon 测距”这个标题,下意识会想:“不就是发个广播包,手机扫一下?跟WiFi信号强度测距差不多吧?”——我去年在做室内定…

作者头像 李华
网站建设 2026/9/12 9:51:55

OLAP系统高并发优化技术与实践

1. OLAP并发处理能力研究的背景与意义在大数据时代,企业每天产生的数据量呈指数级增长。根据行业统计,全球数据总量预计到2025年将达到175ZB,其中企业数据占比超过60%。面对如此庞大的数据规模,传统的OLTP(在线事务处理…

作者头像 李华
网站建设 2026/9/12 9:51:32

银杏叶提取物的生物活性成分与药理机制解析

1. 银杏叶提取物的生物活性成分解析银杏叶提取物(Ginkgo biloba extract, GBE)作为传统中药和现代植物药研究的典范,其核心活性成分主要包括两大类:黄酮类化合物(Flavonoids)和萜内酯类(Terpeno…

作者头像 李华
网站建设 2026/9/12 9:51:28

MMP-9调控类淋巴系统在帕金森病中的作用机制

1. 研究背景与意义 帕金森病(Parkinsons Disease, PD)作为第二大神经退行性疾病,其病理特征包括α-突触核蛋白异常聚集和中脑黑质多巴胺能神经元丢失。近年研究发现,类淋巴系统(Glymphatic System)功能障碍…

作者头像 李华
网站建设 2026/9/12 9:50:14

Kronos:面向K线预测的开源金融基础模型与最小运行指南

Kronos:面向K线预测的开源金融基础模型与最小运行指南 【免费下载链接】Kronos Kronos: A Foundation Model for the Language of Financial Markets 项目地址: https://gitcode.com/GitHub_Trending/kronos14/Kronos Kronos 是首个面向金融K线序列的开源基础…

作者头像 李华