---
url: /docs/agentbuff-stack/env.md
---
# 环境变量

可选[客服聊天](./support-chat)没有新的环境密钥：`support.websiteId` 是公开站点标识，放在产品配置；真实支持邮箱用 `metadata.supportEmail`。默认关闭，未知供应商、非法 ID 和开启后缺少邮件回退会被配置 / 构建检查拒绝。不要把 Crisp REST API 凭据放入前端配置。

## 文件与安全边界

根目录 `.env` 是仓库已有的共享默认值和占位符；**真实凭据只放被忽略的 `.env.local`**。从 `.env.example` 复制，不能把生产值提交回 `.env`。新站工厂不复制任何本地凭据、数据库或 Git 历史。

API 契约：`apps/api/lib/env.ts`。`local.ts`、`dev.ts` 和 `worker.ts` 都通过 `parseEnv` 检查基础字段及 OAuth / Stripe 组合；报错仅包含变量名和问题类别。Bun 本地加载环境文件，部署后的 Worker 从 Secrets 与绑定读取数据，不会读取电脑上的 `.env.local`。

## 核心与构建

| 变量 | 可见性 / 使用位置 | 要求 |
| --- | --- | --- |
| `ENVIRONMENT` | 服务端 | `development` / `staging` / `production` |
| `APP_NAME` | 非秘密品牌；Wrangler / API / 构建兼容 | 与 `websiteConfig.metadata.name` 同步；工作台实际品牌取配置 |
| `APP_ORIGIN` | 非秘密；API、OAuth、邮件链接 | 裸 origin；非开发环境必须 HTTPS |
| `PUBLIC_SITE_URL` | 公开；Astro 构建 | canonical、OG、RSS、sitemap；线上与 `APP_ORIGIN` 相同，本地可预览线上 canonical |
| `API_ORIGIN` | 本地开发 | Vite 代理目标；完整预览默认 `http://127.0.0.1:4600` |
| `PORT` | 本地开发 | API 端口，完整预览为 4600；更改需同步网关 |
| `DOCS_SITE_URL` | 公开；文档构建 | 可选文档域名；缺省文档 noindex 且不生成 sitemap |

`PUBLIC_*` 会进入公开网页。`VITE_APP_NAME`、`VITE_DEFAULT_MODE`、`VITE_SITE_LANGUAGE` 由工作台构建从网站配置派生，不需要手工配置。不要把私钥或账号级 Cloudflare token 放进这些变量。

## 数据库与绑定

| 变量 / 绑定 | 使用位置 | 要求 |
| --- | --- | --- |
| `DATABASE_URL` | 本地预览、Drizzle CLI | 含凭据，服务端保密；本地完整预览限制 loopback 数据库 |
| `STORAGE` | API Worker 资源绑定 | 私有 R2 对象；本地文件 API 与定时清理已验收，预览数据库需具备迁移 0002 / 0003；测试 / 正式环境已分别声明，资源创建与云端验收待做 |
| `TASK_QUEUE` | API Worker 资源绑定 | 队列对象，名称在 Wrangler 各环境分别声明；任务消费者与实际本地验收已接入，数据库需迁移至 0006 |
| `TASK_DEAD_QUEUE_NAME` | API Worker 服务端变量 | 必须与死信消费者队列名一致，用于区分业务 / 死信批次；开发运行时从 Wrangler 派生，生成器同步重命名；云端资源仍待验收 |
| `DISCORD_NOTIFICATION_WEBHOOK_URL` | API 秘密 | 可选站主频道地址，启用需明确起点与完整迁移 0021；仅允许标准 Discord 服务地址 |
| `LOCAL_OPERATIONS` | 仅本地运行时注入 | 站主消息捕获，开发缺失不回退外网；非开发环境不得携带 |
| `LOCAL_MAIL` | 仅本地运行时注入 | 邮件捕获绑定，不作为云端变量或公开配置 |
| `LOCAL_MARKETING` | 仅受控本地验收注入 | Contacts 测试绑定；开发环境缺少时明确失败，禁止回退到外部请求 |
| `HYPERDRIVE_CACHED` / `HYPERDRIVE_UNCACHED` | 生产 Worker 资源绑定 | 配置 ID 在 API Wrangler 中；数据库原始凭据保存在 Hyperdrive 服务 |
| `APP_SERVICE` / `API_SERVICE` / `ASSETS` | Web Worker 资源绑定 | 服务绑定与静态资产，不是环境变量字符串 |

可选[任务邮件](./task-notifications)复用现有邮件变量，不引入新密钥或新队列绑定；启用前完整升级 0022，重建 API / 工作台 / 公共页，并核对定时维护。`APP_NAME`、`APP_ORIGIN`、发信地址或密钥变化会停止旧通知重试，接收地址和内容不能由客户端指定。正常预览仍关闭，两张通知表尚未应用到主库。

可选[站主通知](./operator-notifications)使用独立服务端频道地址和 `notifications.operator` 起点 / 开关；不复用个人邮件选择。启用前完整升级 0021 并重建。开发环境始终捕获；主库未升级，默认关闭。

迁移 CLI 与生产 API 的连接路径不同。运行 `db:migrate` 前核实 CLI 的数据库目标；不因配置检查通过就自动执行生产迁移。

线上应用使用独立业务角色，迁移连接使用实际所有者。更换数据库密码时更新该环境两个 Hyperdrive 的源凭据，并核对已有连接池；改本机 `DATABASE_URL` 不会更新云端。授权脚本与隔离复验见[数据库角色与凭据](./database-roles)。

`bun dev` 与完整预览均从 `DATABASE_URL` 派生两个本地 Hyperdrive 绑定，不再使用旧的 `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_*` 覆盖。支持 loopback PostgreSQL 的 trust 认证；模拟器要求非空密码，因此空密码仅在内存中补本地占位值，不改环境文件或数据库用户。

资源对象类型集中在 `apps/api/lib/bindings.ts`，与 `parseEnv` 的普通字段分开。本地 R2 / 队列持久化目录为本项目 `.local/workerd/`，重启保留，生成站不复制或共享该目录。开发、测试、正式环境已分别声明存储、任务 / 死信队列与定时维护；云端资源尚未创建或验收。`bun deploy:check --target staging` 检查资源关系与占位配置，不能验证资源存在，见[部署与验收](./deployment)。

`bun dev` 监听 API、数据库模型、公共产品配置及已构建邮件模板，重新打包并重载 Worker。修改本地运行器本身、启动变量或邮件模板源码后，重启开发命令以重新加载运行器和构建邮件模板。完整预览使用构建产物，修改后按快速开始重建并重启。

## 认证与邮件

| 变量 | 规则 |
| --- | --- |
| `BETTER_AUTH_SECRET` | 至少 32 字符，每站每环境独立；非开发环境拒绝已知占位符 |
| `BETTER_AUTH_SECRETS` | 可选、按顺序排列的 `版本:密钥`；未设置时明确使用版本 0。切换当前密钥需重新登录，基础密钥仍影响邮件选择与营销链接，见[认证密钥轮换](./auth-key-rotation) |
| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | 两个一起设置或一起留空；仅服务端读取 |
| `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | 两个一起设置或一起留空；仅服务端读取 |
| `RESEND_API_KEY` | 线上邮件必须有值；`bun dev` / 完整本地预览由捕获器提供临时测试密钥，不会真实发送 |
| `RESEND_EMAIL_FROM` | 合法邮箱，线上使用已验证发信域；本地可用 `preview@example.test` |

本地完整预览会把邮件写到 `.local/outbox.jsonl`，里面有 OTP / 验证链接，应只留在本机。Google / GitHub 的 Client ID 不是前端直接用的登录密钥，本站仍统一由服务端管理。

## 营销联系人

`marketing.contactsSync` 默认关闭，独立于两个申请页面开关。开启时复用具备 Full access 的 `RESEND_API_KEY`，以下三项一起设置：

| 变量 | 规则 |
| --- | --- |
| `RESEND_MARKETING_WEBHOOK_SECRET` | 营销回调独立签名 secret，`whsec_` 后为供应商提供的 Base64 内容 |
| `RESEND_NEWSLETTER_TOPIC_ID` | 邮件订阅主题 UUID |
| `RESEND_WAITLIST_TOPIC_ID` | 候补名单主题 UUID；必须与邮件订阅不同 |

当前名单 API 需完整迁移至 0016，同步时两个供应商主题的默认订阅必须为 `opt_out`。配置检查不联网验证权限或默认订阅。主预览保持 0012 与关闭状态；`--contacts` 使用独立数据库、完整迁移和测试绑定。完整说明见[邮件订阅与候补名单](./marketing-subscriptions)。

## 支付

以下四项**全部设置或全部留空**。即使网站配置停用了支付，也不接受半组凭据，以便暴露部署错误。

| 变量 | 格式 / 职责 |
| --- | --- |
| `STRIPE_SECRET_KEY` | `sk_` 前缀，服务端调用 Stripe |
| `STRIPE_WEBHOOK_SECRET` | `whsec_` 前缀，验证 Better Auth 订阅 webhook |
| `STRIPE_CREDITS_WEBHOOK_SECRET` | 可选 `whsec_` 前缀，积分包独立回调；必须与订阅 endpoint secret 不同，启用积分包时必填 |
| `STRIPE_STARTER_PRICE_ID` | `price_` 前缀，对应 Starter 月度价格 |
| `STRIPE_PRO_PRICE_ID` | `price_` 前缀，对应 Pro 月度价格 |
| `STRIPE_PRO_ANNUAL_PRICE_ID` | 可选 `price_` 前缀，需前四项完整；独立的 Pro 年度价格，启用设置页年付选项 |

测试和生产使用各自的 Stripe 密钥、价格及 webhook secret，不混用。一次性积分包还需公开目录 `creditPacks`、独立回调 secret，并完整迁移至 0024、重建工作台 / API；当前仍要求前四项订阅凭据完整，不能仅配两个积分包变量。退款扣回、争议恢复和八连接并发已本地验收，真实沙箱待验收，见[支付教程](./billing-tasks)。当前没有 Creem、Waffo 运行时变量。

三个订阅 Price ID 必须不同，配置校验只报告字段要求，不输出实际 ID。月付商品需要一月一个周期，年付 Pro 需要一年一个周期；目前仅支持已启用、同测试 / 正式模式、固定正整数金额、按单位计价的 licensed 订阅商品，不支持多年月期、计量、阶梯价或一次性商品冒充订阅。创建 Checkout 前通过 SDK 核对实际收费项，配置存在不代表供应商商品已核实。

不配置年付 ID 时，设置页只提供月付；`config:check` 的 `StripeAnnual` 显示是否已配置。离线检查不连接 Stripe，实际收费金额和周期须在 Checkout 核对，真实付款仍需沙箱验收。

## 联盟营销

Rewardful 后台核查默认关闭。`REWARDFUL_API_SECRET` 与 `REWARDFUL_WEBHOOK_SECRET` 一起设置或一起留空，分别用于只读 REST API 与原始字节签名验证。开启还需 `sk_test_` / `sk_live_` 的 Stripe 私钥，以及已有 Stripe 四项完整凭据组；推广计划 UUID 和商户账户 ID 放在 `websiteConfig.affiliates`。这些服务密钥不放入前端配置。先完整升级至 0033 并重建 API，正常主预览不自动迁移。

浏览器追踪默认关闭；仅公开推荐页明确同意后加载 SDK，工作台仅读取本地候选并分别确认个人 / 团队绑定。`tracking.publicKey` 只能填写公开浏览器 key，不能填写服务密钥；200 回调只表示持久接收，供应商 `paid` 不证明银行到账。配置、有限恢复、退款 / 结算复核与受控本地验收见[联盟营销教程](./affiliates)。

## 统计

`PUBLIC_CF_ANALYTICS_TOKEN` 是 Web Analytics 的公开 beacon token。只有 `websiteConfig.analytics.provider === "cloudflare"` 才构建统计脚本；缺失 token 会使检查和 Astro 构建失败。它不是 Cloudflare API / 账号权限 token。私有工作台没有植入该脚本。

## 检查方式

```sh
bun run config:check
bun --env-file .env.staging.local scripts/config-check.ts --target staging
bun --env-file .env.production.local scripts/config-check.ts --target production
bun run config:check --json
```

命令读取当前本地准备的环境，`--target` 不会把开发变量自动转换成生产变量。会检查匹配的 `ENVIRONMENT`、必需字段、凭据组、网站范围、域名和 `.env.example` 字段覆盖。只输出变量名与模块状态，不输出值；错误返回非零退出码。

该命令不联网，不验证账户权限、价格真实性、邮件可送达性、Hyperdrive / R2 绑定或 Worker 部署。生产 Worker 请求入口也做环境校验，但它不能代替部署前的沙箱验收。

## 模板官网与预览端口

`WEBSITE_ORIGIN`、`WEBSITE_DOCS_URL`、`WEBSITE_DEMO_URL` 只作用于模板官网构建，均为公开地址，不是服务凭据。生成产品不需要这些变量。

官网域名已确认为 `stack.agentbuff.dev`，专用示例为 `.env.website.example`；当前只配置、暂不发布。公开教程和模板演示地址尚未确定，示例留空，正式构建会拒绝未补齐的地址。它不设置产品 `APP_ORIGIN`，也不要求先接入真实模型或产品数据库。

`preview.config.json` 是构建产物预览的端口来源。`bun preview:build` 和 `bun preview:start` 派生产品 origin 与内部端口；生成器的 `--port` 设置新项目入口。生产构建仍以实际公开域名配置为准。

## 可选上传验证

`PUBLIC_TURNSTILE_SITE_KEY` 是 widget 的公开键，由文件配置 API 提供给工作台；`TURNSTILE_SECRET_KEY` 只供 API 调用 Siteverify。两个值成对配置。`security.turnstile.enabled` 默认关闭；开启后缺配置会拒绝启动。已知官方测试键只允许 development。详细操作与验收范围见[执行限制与验证](./execution-security)。

营销确认邮件复用以上邮件变量；启用名单前完整迁移至 0023，重建公共页与 API。语言来自每代主动同意的记录，不能用环境变量或请求头改写重试语言，详见[邮件订阅](./marketing-subscriptions)。
