Skip to content

This document was written by AI and has been manually reviewed.

配置

站点配置存储在 D1 的 site_config 表中,可通过 Admin → Settings 在运行时编辑。更改任何配置项均无需重新部署。

敏感字段(验证码私钥,含全球与中国大陆两个 Turnstile 密钥、社交登录的 client_secret、SMTP/IMAP 密码、GitHub README PAT、Discord bot token 等)通过 Cloudflare Secrets Store 绑定 SECRETS_KEY 使用 AES-GCM 在数据库中加密存储。管理面板读取时透明解密,配置 API 永远不会暴露其明文。

通用

类型默认值说明
site_namestring"Prism"显示在浏览器标题和邮件中
site_descriptionstring"Federated identity platform"显示在登录页面
site_icon_urlstring?null网站图标 / Logo 的 URL
allow_registrationbooleantrue允许新用户自助注册
invite_onlybooleanfalse即使 allow_registration = true,也要求注册时携带邀请令牌
require_email_verificationbooleanfalse要求用户完成邮箱验证后才能登录
accent_colorstring"#0078d4"主题主色调(十六进制),驱动 FluentUI 主题
custom_cssstring""注入到每个页面的 <style>
disable_user_create_teambooleanfalse隐藏「新建团队」按钮 — 仅管理员可创建团队
disable_user_create_appbooleanfalse隐藏「新建应用」按钮 — 仅管理员可创建 OAuth 应用
allow_alt_email_loginbooleantrue允许使用任意已验证的次要邮箱登录,而不仅是主邮箱
initializedbooleanfalse首次初始化后设为 true,请勿手动修改

法律页面

管理员可在 管理 → 设置 → 法律条款 中发布两份文档:隐私政策与服务条款。每份文档 使用 Markdown 编写(渲染与净化方式与个人主页 README 相同),并显示在各自的公开页面, 无需登录即可访问:

文档页面API 端点
隐私政策/privacyGET /api/legal/privacy
服务条款/termsGET /api/legal/terms

与上面的设置不同,这两份文档存放在 site_config 中,而是位于专用的 legal_documents D1 表(slug、content、updated_atupdated_by)。政策文档体积较大, 而 site_config 几乎在每个请求中都会被整表读取;将文档移出可避免在热路径上加载它们。 每份文档上限为 256 KiB。

每份已发布文档的链接都会出现在每个页面(无论是否登录)的页脚。清空某份文档(保存为空) 会同时隐藏对应页面及其页脚链接。公开的 GET /api/site 响应不包含全文,只暴露 has_privacy_policy / has_terms_of_service 布尔值,供页脚决定渲染哪些链接;正文在读者 打开页面时按需获取,端点还会返回最后更新时间以显示「最后更新」信息。

会话与令牌

类型默认值说明
session_ttl_daysnumber30会话 JWT 有效期(天)。可在 users 表中按用户单独覆写(仅管理员可改)
access_token_ttl_minutesnumber60OAuth 访问令牌有效期(分钟)。同样支持按用户覆写
refresh_token_ttl_daysnumber30OAuth 刷新令牌有效期(天)。同样支持按用户覆写

机器人防护(验证码)

同一时刻只能启用一个 provider。注册、登录、改密、重发邮箱验证、以及管理员显式启用的流程都会触发验证码。

类型默认值说明
captcha_providerstring"none"none | turnstile | hcaptcha | recaptcha | pow
captcha_site_keystring""公开 site key。Turnstile 时为全球(region: "world")组件
captcha_secret_keystring""该密钥对应的服务端密钥(加密存储)
turnstile_endpoint_modestring"global"仅 Turnstile。选择分发组件的主机(见下)
turnstile_china_site_keystring""仅 Turnstile。region: "china" 组件的 site key(见下)
turnstile_china_secret_keystring""仅 Turnstile。该密钥对应的服务端密钥(加密存储)
pow_difficultynumber20工作量证明所需的前导零比特数(越高越难)

Turnstile:两个主机,两个组件

Cloudflare 通过全球主机(challenges.cloudflare.com)和中国大陆主机(challenges.cloudflare-cn.com)分发 Turnstile。两者并不通用,这决定了整个设置的形态:

  • 每个 Turnstile 组件都带有一个 regionworldchina),在创建时确定。
  • 组件只能在与其 region 匹配的主机上工作。把 region: "world" 的 site key 发往中国大陆主机会得到 HTTP 400,组件将其显示为 Error: 400020,被它保护的表单从此无法提交。
  • region 不可修改:API 会以 you cannot change region 拒绝更改。已有的全球密钥永远无法升级。
  • 创建 region: "china" 组件需要与 China Network 合同绑定的权限;没有该权限时 API 返回 not entitled to create widgets with this region。Cloudflare 的 China Network 文档也直接写明:“Turnstile is not available within Mainland China.”

因此使用中国大陆主机意味着同时更换 site key。这正是 turnstile_china_site_key / turnstile_china_secret_key 的用途:一个以 region: "china" 创建的第二组件,与 captcha_site_key / captcha_secret_key 中的全球密钥对并列。

turnstile_china_site_key 为空,下面所有模式的行为都等同于 global 即使在没有权限的情况下选择了偏向中国大陆的模式也不会出问题——访客只会看到全球组件。

turnstile_endpoint_mode

决定某个访客使用哪一组密钥对:

  • global——始终使用全球主机与密钥。
  • china——始终使用中国大陆主机与密钥。
  • client_language——浏览器语言为中文(zh*)时由浏览器选择中国大陆。
  • server_region——请求的边缘地理位置为 CN 时由服务端选择中国大陆。
  • client_region——浏览器时区属于中国大陆时区时由浏览器选择中国大陆。

由于该决定同时选中主机和密钥,客户端模式下两个 site key 都会下发给浏览器;site key 本就是公开的,因此不会泄露任何信息。令牌回传时会附带签发它的组件标识(captcha_variant),服务端据此使用对应的密钥校验。

校验。 配置的中国大陆密钥只有在实际可用后才会被采信:Prism 会向中国大陆主机为该密钥请求一次挑战,成功时在 KV_CACHE 中缓存 6 小时、失败时缓存 1 小时,失败则回退到 global。这能捕获填错字段的密钥、实际以 region: "world" 创建的组件,以及已失效的权限。探测在后台进行,因此缓存未命中时直接返回全球主机而不会等待。

为什么回退必须在服务端。 Turnstile 脚本在加载时只从自己的 <script> 标签读取一次挑战源,而能够覆盖它的 base-url 参数在生产版本中是关闭的。已经从错误主机加载的组件,不重新加载页面就无法改用另一个主机,因此这个选择必须在浏览器提交之前就是正确的。

Proof-of-work 不依赖任何第三方服务。pow/ 中的 Rust→WASM 求解器比 JS 兜底快约 10 倍。难度 20 时一般在 0.1–2 秒内完成。高于 24 可能在低端设备上超时。PoW 一次性使用,通过 pow_used 表防重放。

二次验证 / 步骤提升

类型默认值说明
sudo_mode_ttl_minutesnumber5用户成功完成一次步骤提升后,同一 (用户, 会话, 应用) 三元组下的后续挑战在该时长内可跳过 TOTP/Passkey 重新提示。0 表示完全禁用 sudo 模式
require_captcha_for_2fabooleanfalse站点全局:每次步骤提升确认都必须通过当前启用的验证码。应用也可针对单个挑战开启。captcha_provider = none 时此项无效

公开资料

用户和团队的公开资料默认全部关闭,必须显式启用。站点级默认值仅作用于用户尚未自定义的字段,绝不会把已设为私密的资料暗中转为公开。

用户资料默认值

类型默认值说明
enable_public_profilesbooleantrue主开关。false/u/:username/t/:id 一律返回 404
default_profile_show_display_namebooleantrue
default_profile_show_avatarbooleantrue
default_profile_show_emailbooleanfalse敏感信息 — 即使资料整体公开也默认不展示
default_profile_show_joined_atbooleantrue
default_profile_show_gpg_keysbooleantrue
default_profile_show_authorized_appsbooleanfalse暴露用户连接了哪些第三方服务 — 默认关闭
default_profile_show_owned_appsbooleantrue
default_profile_show_domainsbooleantrue
default_profile_show_joined_teamsbooleanfalse同时控制是否允许出现在任何团队的公开成员列表中
default_profile_show_readmebooleantrueREADME 本身是显式启用的(空内容即不展示);只有用户写过内容时该默认值才生效
profile_readme_max_bytesnumber65536README Markdown 源码的字节硬上限

团队资料默认值

类型默认值说明
default_team_profile_show_descriptionbooleantrue
default_team_profile_show_avatarbooleantrue
default_team_profile_show_ownerbooleanfalse默认关闭:否则会通过团队页暴露所有者的用户名
default_team_profile_show_member_countbooleantrue
default_team_profile_show_appsbooleantrue
default_team_profile_show_domainsbooleantrue
default_team_profile_show_membersbooleanfalse完整成员列表。是否真正展示某成员还要看其 profile_show_joined_teams
default_team_profile_show_sub_teamsbooleantrue子团队列表。子团队也必须自己开启公开资料才会真正出现

主开关 profile_is_public 没有站点级默认值(隐私优先) — 团队所有者或管理员必须显式开启。

子团队(嵌套团队)

子团队特性的总开关和继承开关。完整语义见对应文档,键本身:

类型默认值说明
enable_sub_teamsbooleantrue总开关;关闭后所有子团队接口返回 403。
max_team_depthinteger5嵌套深度上限(根为 0),管理员接口校验 1–20。
inherit_team_membershipbooleantrue把成员角色级联到后代(有效角色 = max(直接, 继承))。
inherit_team_domainsbooleantrue让上级域名出现在子团队列表,并参与自动验证。

身份组

身份组的按团队权限集在站点侧的兜底默认值。没有总开关 —— 该特性按团队 opt-in 且默认关闭,团队所有者不开启就不会下发任何东西。

类型默认值说明
default_team_role_permissionsobject{}admin 角色的兜底能力授予,例如 {"admin":{"groups:manage":true}}

解析顺序为:单组覆盖 → 团队 role_permissions → 本键 → 代码内置默认(groups:manage 关、groups:assign 开)。每一级只贡献它显式设置过的键,因此这里留空对象就等于让所有团队直接落到内置默认。

邀请链接注册

邀请链接注册的总开关与各项上限。默认关闭 —— 打开它才使团队所有者具备创建账号的能力,且即便打开,每个团队仍需在 Admin → Teams 单独获得授权。

类型默认值说明
enable_team_invite_registrationboolfalse总开关。关闭时所有相关端点返回 403。
team_invite_registration_max_uses_capint1000团队为可注册邀请设置 max_uses 的上限。
team_invite_registration_rate_per_hourint200单个邀请码每小时注册次数。按 IP 限流约束不了被广传的链接。
restricted_user_capabilitiesobject{}放开给受限账号的功能,如 {"app:create": true}。留空即全部拒绝。
restricted_pending_ttl_hoursint72未完成的注册存活多久后被清理。
restricted_dissolve_grace_hoursint168解散停用账号到实际删除之间的宽限期。

能力键:team:createapp:createdomain:createpat:createprofile:publicgpg:manageself:convert。未列出的键落回内置默认值(全部拒绝)。账号安全永不受限。

团队加入门槛(站点底线)

站点级硬性最低要求,任意团队都必须满足。所有者只能在此基础上加严,不能放松到底线之下。

类型默认值说明
default_team_require_2fabooleanfalse底线:任何团队都要求成员至少有一个 TOTP 认证器或 Passkey
default_team_require_verified_emailbooleanfalse底线:任何团队都要求成员的主邮箱已验证

WARNING

开启这些底线会立即对所有现有成员生效 — 没有满足条件的成员将无法继续团队操作,直至自行补齐。建议先在前端通知成员后再切换。

GitHub README 同步

用户可选择从 GitHub 用户仓库同步公开资料 README。缓存遵守 ETag,失败时返回旧内容。

类型默认值说明
github_readme_tokenstring""站点级 GitHub PAT,作为最后一道授权回退。空表示未授权访问(每 IP 60 次/小时)。加密存储
github_readme_cache_ttl_secondsnumber3600在该 TTL 内直接服务缓存内容,过期后才发起带条件 GET
github_readme_token_failuresnumber0自动管理:站点 PAT 的连续 401 计数。达到 3 次后自动清空 token

GPG 登录

类型默认值说明
gpg_challenge_prefixstring""在 clearsign 文本的站点头与随机挑战之间插入的额外行。可用于添加人类可读的标识,让用户能够确认自己签的挑战来自你的站点

第三方通知

类型默认值说明
tg_notify_source_slugstring""用于推送 Telegram 通知的已启用 Telegram OAuth 源 slug。留空即关闭 Telegram 投递。该源的 bot token 同时用作发送通知的 bot
discord_notify_source_slugstring""用于识别 Discord 通知收件人的已启用 Discord OAuth 源 slug。留空即关闭 Discord 投递
discord_bot_tokenstring""用于创建 DM channel 并发送通知消息的 Discord bot token。它与 Discord OAuth 源中的 client secret 是两个不同凭据

社交登录

每个 OAuth 源(GitHub、Google、Microsoft、Discord、Telegram、X、Cloudflare、Generic OIDC、Generic OAuth 2)都已迁入 oauth_sources 表 — 在 Admin → OAuth Sources 中管理,不在本页设置。下方的旧字段仍保留以兼容历史数据,新部署应直接使用 OAuth Sources。

键(旧)说明
github_client_idGitHub OAuth App Client ID
github_client_secretGitHub OAuth App Client Secret
google_client_idGoogle Cloud OAuth 2.0 Client ID
google_client_secretGoogle Cloud OAuth 2.0 Client Secret
microsoft_client_idAzure AD Application (client) ID
microsoft_client_secretAzure AD Client Secret
discord_client_idDiscord Application ID
discord_client_secretDiscord Client Secret

所有 *_client_secret 在数据库中加密存储。源的回调 URL 格式为:

https://your-domain/api/connections/<slug>/callback

邮件 — 发送

类型默认值说明
email_providerstring"none"none | resend | mailchannels | smtp
email_api_keystring""Resend / Mailchannels 的 API key(加密存储)
email_fromstring"noreply@example.com"出站邮件的发件人地址
smtp_hoststring""SMTP 服务器地址(provider = smtp 时)
smtp_portnumber587SMTP 端口
smtp_securebooleanfalsetrue = 隐式 TLS(465);false = STARTTLS(587)
smtp_userstring""SMTP 用户名
smtp_passwordstring""SMTP 密码(加密存储)

邮件 — 接收

类型默认值说明
email_verify_methodsstring"both"link(系统发送)| send(用户发送邮件验证)| both
email_receive_providerstring"cloudflare"cloudflare(Email Workers)| imap(按 cron 轮询 IMAP 收件箱)| none
email_receive_hoststring""verify-<code>@<host> 邮件的域名(仅 Cloudflare)。为空时使用 APP_URL 主机名
imap_hoststring""IMAP 服务器地址(provider = imap 时)
imap_portnumber993IMAP 端口
imap_securebooleantruetrue = 隐式 TLS(993);false = STARTTLS(143)
imap_userstring""IMAP 用户名 — 同时作为接收验证邮件的目标地址展示给用户(验证码作为邮件主题)
imap_passwordstring""IMAP 密码(加密存储)
social_verify_ttl_daysnumber0非零时,通过社交登录验证的邮箱在该天数内一直被信任,过期后才需重新验证。0 表示永不过期

域名验证

域名可通过 DNS TXT、HTML meta、.well-known 文件中任意一种方式验证 — 由用户在添加时选择。已验证的域名会按设定的 cron 频率重新核验。

类型默认值说明
domain_reverify_daysnumber30已验证域名的自动重新核验间隔(天)

诊断与限流

类型默认值说明
login_error_retention_daysnumber30login_errors 表中失败登录记录的保留天数,超过后由 cron 清理
ipv6_rate_limit_prefixnumber64限流时按多少位前缀对 IPv6 地址聚合(避免一个 /64 拥有无限重试次数)

Wrangler 绑定与变量

下列项目在 wrangler.jsonc 中配置,无法在管理面板中编辑。

变量

变量必填说明
APP_URL部署的完整 origin,例如 https://auth.example.com
LOCKDOWN_USERS逗号/分号/空格分隔的用户名列表,列表中的用户无法被任何人(包括管理员)删除。留空即禁用。
LOCKDOWN_TEAMS逗号/分号/空格分隔的团队名称列表,列表中的团队无法被任何人(包括管理员)删除。留空即禁用。
ENABLE_RESET设为 "true" 以启用 Admin → Settings → Danger Zone → Site reset 按钮。未设置或为其他值时隐藏(具有破坏性)。
D1_CONSOLEAdmin → Database 的可用性。默认关闭。 未设置、"off"(也接受 "false" / "0" / "no" / "disabled" / "none")以及任何无法识别的值都会移除整个界面:端点返回 404,标签页消失。"read-only"(也接受 "readonly" / "read")允许浏览与 SELECT,但拒绝一切写入,包括显式带 allow_write 的请求。"full"(也接受 "on" / "true" / "1")为完全访问 —— 但审计日志例外,在任何设置下都是只追加。
KV_CONSOLE同一面板中键值浏览器的可用性。取值与 D1_CONSOLE 相同。未设置时跟随 D1_CONSOLE —— 两者是同一实例存储的两扇窗,因此对其中一个的设置也是对另一个的表态,除非这里另行指定。两者都未设置时,均为关闭。
NO_RESET_COOLDOWN设为 "true" 以跳过请求重置与确认之间的 30 分钟冷却期。即使设置此项,2FA 仍然要求。

绑定

绑定类型必填说明
DBD1 数据库所有持久化状态
KV_SESSIONSKV namespaceJWT 密钥、ID Token RSA 密钥对、按会话存储的元数据
KV_CACHEKV namespace限流计数器、IMAP 拉取游标、图片代理缓存
ASSETSWorkers Assets已构建的 SPA。html_handling: "none" 让 SSR 自行处理 /
SECRETS_KEYSecrets Store secret强烈推荐32 字节 base64url 编码的 AES-GCM 主密钥。绑定后所有敏感字段在 D1 中加密存储

SECRETS_KEY 配置

生成 32 字节主密钥:

bash
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"

在 Cloudflare 控制台创建 Secrets Store,把密钥以 prism-secrets-key 为名存入,然后在 wrangler.jsonc 中加入 secrets_store_secrets 绑定。

重新部署后,在 Admin → Settings → Danger Zone → "Migrate secrets to Secrets Store" 中点击迁移一次,将 D1 中已有的 OAuth/源/SMTP/IMAP/验证码凭据加密。Bearer 类机密(PAT、OAuth code、OAuth token、邀请 token、邮箱验证码、二次验证码、单条备用码)通过姊妹按钮 "Migrate D1 secrets" 迁移成 HMAC-SHA256 哈希,可继续按值检索但无法从数据库还原。

未绑定 SECRETS_KEY 时所有加密/哈希函数均退化为 no-op — 历史明文路径仍然可用,便于平滑升级。

Cron 触发器

jsonc
"triggers": { "crons": ["0 */6 * * *"] }

每 6 小时 worker 会:

  • 重新核验 next_reverify_at 已到期的域名;
  • 拉取 IMAP 邮箱(email_receive_provider = imap 时);
  • 清理 app_event_queuepow_used 中的过期记录;
  • 回收 image_proxy_mappings 中已无源行的孤儿映射。

用户 / 团队删除锁定

如果你有绝对不能被删除的用户或团队 —— 共享服务账号、机器人身份或关键组织团队 —— 请在 wrangler.jsonc 中设置 LOCKDOWN_USERSLOCKDOWN_TEAMS。 被锁定的账号在管理员(或团队所有者)尝试通过 API 删除时会返回 403 错误。

变量接受逗号、分号或空格分隔的名称列表,每个名称的首尾空白会被自动去除。

在 GPL-3.0 许可协议下发布.