This document was written by AI and has been manually reviewed.
管理员指南
管理面板位于 /admin,仅对 role = admin 的用户可见。第一个管理员账号在首次初始化时创建。后续管理员可通过 Admin → Users → Edit User → Role → Admin 提升权限。
仪表盘
显示四个汇总统计数据:
| 统计项 | 说明 |
|---|---|
| 总用户数 | 所有已注册账号 |
| OAuth 应用数 | 所有已注册应用 |
| 已验证域名数 | 通过验证的域名 |
| 活跃令牌数 | 未过期的 OAuth 访问令牌 |
统计下方还会显示运维警告 — 最重要的是:当 SECRETS_KEY 已绑定但 D1 数据尚未迁移时会提示。点进 设置 → Danger Zone 即可一次性完成加密。
设置
设置按标签页分组。所有更改立即生效——无需重新部署。
通用
- Site name — 显示在浏览器标签和邮件模板中
- Site description — 显示在登录页面
- Site icon URL — PNG/SVG Logo 的链接
- 注册模式 —
开放(任何人可注册)、仅限邀请(需要邀请令牌)或关闭(禁止新注册) - Require email verification — 用户必须点击验证链接后才能登录
General 标签页靠下:
- 安全联系方式(
security_contact)— 接收漏洞报告的mailto:地址或 https URL。设置后,Prism 会在/.well-known/security.txt提供符合 RFC 9116 的文件; 留空则禁用该文件。 - 安全策略 URL(
security_policy_url)— 可选,指向您的披露策略的链接,会以Policy:形式加入security.txt。
外观
- Accent color — 驱动整个 FluentUI 主题的十六进制颜色,保存后立即生效。
- Custom CSS — 注入到每个页面的
<style>块,适合在不修改源码的情况下进行品牌定制。
法律条款
- 隐私政策 — Markdown,发布于
/privacy。 - 服务条款 — Markdown,发布于
/terms。
两者均使用 Markdown 编写(渲染与净化方式与个人主页 README 相同),无需登录即可访问。 每份已发布页面的链接都会出现在每个页面的页脚。留空某个字段会同时隐藏对应页面及其页脚 链接。每份文档上限为 256 KiB。参见 配置 → 法律页面。
安全 / 会话
- Session TTL(天) — 登录会话的有效期
- Access token TTL(分钟) — OAuth 访问令牌有效期
- Refresh token TTL(天) — OAuth 刷新令牌有效期
- Sudo 模式 TTL(分钟) — 用户成功完成一次 2FA 步骤提升后,同一
(用户, 会话, 应用)在该时长内的后续挑战可跳过 TOTP/Passkey 重新提示。0表示完全禁用 sudo 模式。每次确认时仍要求用户勾选行动确认复选框。详见 OAuth → 步骤提升 2FA。 - Require captcha for 2FA — 站点全局:每次步骤提升确认都必须通过当前启用的验证码。应用也可针对单个挑战开启。
captcha_provider = none时无效。 - IPv6 限流前缀长度 — 限流时 IPv6 地址按多少位前缀聚合(默认
/64)。避免一个/64拥有无限重试次数。
机器人防护
选择一个验证码提供商:
| 提供商 | 说明 |
|---|---|
| 无 | 不启用机器人防护 |
| Cloudflare Turnstile | 需要 Turnstile 站点密钥 + 密钥,提供免费套餐 |
| hCaptcha | 需要 hCaptcha 站点密钥 + 密钥 |
| reCAPTCHA v3 | 需要 Google reCAPTCHA v3 站点密钥 + 密钥,无感验证 |
| 工作量证明 | 无需第三方服务,难度 20 在现代硬件上约需 0.1–2 秒 |
选择 Cloudflare Turnstile 后,会出现验证端点设置,用于选择分发组件的主机:全球 challenges.cloudflare.com 或中国大陆 challenges.cloudflare-cn.com。可选:始终全球、始终中国大陆,或按浏览器语言(客户端)、按请求地区(服务端)、按浏览器地区(客户端)自动选择。
只要选择「始终全球」以外的任何模式,就会额外出现中国大陆 site key 和中国大陆密钥两个字段。它们是必需项而非可选项:Turnstile 组件的 region 在创建时即固定,且每个主机只接受属于自己 region 的密钥——因此中国大陆主机需要一个以 region: "china" 创建的第二组件,而这又需要 Cloudflare China Network 权限。
若中国大陆 site key 留空,所有模式的行为都等同于「始终全球」,因此在无法支持的情况下选择偏向中国大陆的模式不会有任何代价:访客会得到全球组件,而不是卡在 Error: 400020 的组件上。Prism 还会在使用前用中国大陆主机验证已配置的密钥,不可用时回退到全球。参见 turnstile_endpoint_mode。
邮件
邮件设置分为两个子标签页:发送和接收。
发送
- Email provider —
none、resend、mailchannels或smtp - API key — Resend 或 Mailchannels 的 API 密钥
- SMTP 设置 — 主机、端口、加密方式、用户名、密码(选择
smtp时显示) - From address — 验证邮件和通知邮件的发件地址
- 发送测试邮件 — 向管理员邮箱发送测试邮件,验证发件功能是否正常
接收
- 邮箱验证方式 — 控制用户验证邮箱的方式:
link— 系统向用户邮箱发送验证链接send— 用户发送邮件以验证邮箱(具体方式取决于接收方式)both— 用户可以选择任一方式
- 接收方式 — Prism 如何接收入站验证邮件:
Cloudflare Email Workers— 事件驱动,邮件触发 Worker 的email()处理程序。需要配置 Cloudflare Email Routing。用户向verify-<code>@<host>发送邮件。IMAP— Prism 按计划任务周期(默认每 6 小时)轮询 IMAP 邮箱。适用于任何邮件提供商。用户以验证码为邮件主题,发送到配置的 IMAP 邮箱地址(例如receive@prism.example.com)。无— 禁用邮件接收(仅支持链接验证方式)
- 接收域名 — 用于接收
verify-<code>@<host>验证邮件的域名(仅 Cloudflare Email Workers 使用)。留空则默认使用APP_URL的主机名。 - IMAP 设置 — 主机、端口、加密方式、用户名、密码(接收方式为
imap时显示)。IMAP 用户名(邮箱地址)将作为验证邮件的收件地址展示给用户。 - 测试邮件接收 — 生成测试验证码和地址,验证入站邮件是否正常工作
批量账号操作
Admin → Users 支持用复选框选中账号,并对整组执行启用、停用或删除。
选择基于明确的 ID 并一次性提交,因此服务端作用的对象与屏幕上确认的完全一致 —— 服务端重新求值的筛选条件可能命中预览与按下之间新出现的行。每次调用上限 50 个账号;这是影响范围的限制,而非性能限制。删除时需要把数量原样输入一遍 —— 这是误点击唯一给不出的东西。
你自己的账号始终会被跳过,LOCKDOWN_USERS 仍然保护其中的账号不被删除(但不阻止停用 —— 该列表的存在是为了让实例始终保有可用的管理员,而停用是可逆的)。被跳过的账号会连同原因一并列出,而不只是给个数字。
团队邀请
Admin → Invites → 团队邀请 列出实例上全部未失效的团队邀请,可筛选出可创建账号的那些。邀请此前只能从签发它的团队内部看到,而当链接泄漏、问题变成「这个创建者还发出过什么」时,这是错误的索引。
界面会显示令牌,因为追查泄漏的链接意味着把别人收到的东西与实际存在的记录对上。撤销会立即使链接失效;已通过它创建的账号不受影响 —— 用 GET /api/admin/restricted-users?invite_token=… 查找那些账号。
通知路由
Admin → Users → 管理 → 资源 显示某账号是否有自定义通知规则集及其规则数量,并可重置为按事件的默认设置。
只给计数,不给内容。处理「我收不到邮件了」的运营者需要知道是否有规则集在生效;而读取某人把什么路由到哪些地址和聊天账号是另一回事,这里不提供。一个把所有东西都路由到无处的规则集,从用户角度看与「通知坏了」完全一样 —— 重置就是修复手段。
公告板
Admin → Notices 用于撰写在产品内展示的公告:计划内停机、政策变更、安全通告。
它的存在是为了取代「给所有人群发邮件」。公告并不是任何人订阅过的事件,因此无法遵循按事件的通知偏好;向全部账号发送是共享发信域上不受限的外发量;而且无论收件人是否受影响,邮件都会送达。公告出现在受影响的人本来就在的地方,发布成本为零,并且可以撤下。
迁移未应用时
公告板的表来自一次迁移,而部署 Worker 并不会应用迁移。在执行 wrangler d1 migrations apply 之前,公告板在各处都读取为空 —— 包括每次加载都会渲染它的登录页面 —— 且 Admin → Notices 会返回 503,说明情况并给出需要执行的命令。
实例上的其他部分不受影响。这里的识别是刻意收窄的:只有「no such table」与「no such column」会触发降级,因此真正的数据库故障仍然会以失败的形式暴露,而不是伪装成一块空的公告板。
撰写
公告在发布前是草稿,因此半成品永远不会出现在屏幕上。编辑器使用与公告板相同的净化流程预览渲染结果 —— 正文是 Markdown,即便由管理员撰写也按不可信内容处理,因为「存储型 XSS 会触达每一个登录用户」的地方,不该同时是「没有任何检查」的地方。
| 字段 | 作用 |
|---|---|
| 级别 | info、warning 或 critical —— 决定配色 |
| 受众 | 见下 |
| 开始/结束时间 | 展示窗口。只是存储,而非由任务调度:读取查询本身就按时间过滤,因此公告会自行出现与消失 |
| 可关闭 | 对必须留在屏幕上的内容(如进行中的事故)关闭此项 |
| 置顶 | 无论新旧都排在其他公告之上 |
受众
| 受众 | 谁能看到 |
|---|---|
public | 所有人,包括未登录访客,显示在登录与注册页面 |
users | 所有已登录账号 |
admins | 仅站点管理员 |
team | 某个团队的直接成员 |
public 是最值得用的一个:「02:00 UTC 维护」对无法登录的人最有价值。
受众是一个小的枚举而非规则引擎,因为公告板真正需要的每一种受众都能仅凭请求本身判定,且都不需要查看者的会话回答不了的查询。
关闭
读者关闭公告只对自己生效,其他人仍然看得到。编辑公告不会让它重新出现 —— 关掉一个错别字的人,并不会因为错别字被修好就想再看一遍。「重新展示」是一个独立的、需要明确执行的操作,并会报告清除了多少条关闭记录。
未登录的访客无法关闭:没有地方记录这件事,而一个下次加载又冒出来的公告,比没有关闭按钮更糟。
删除公告会连同其关闭记录一并删除。若只是想撤下,「取消发布」会把它保留为草稿 —— 这通常才是「撤下来」的意思。
域名重新验证
- Domain reverify interval(天) — Prism 按该频率对每个已验证域名重新核验所记录的证明(DNS TXT、HTML meta 标签或
.well-known文件 — 由用户在添加时选择),默认 30 天。
公开资料
- Enable public profiles — 主开关。关闭后
/u/<username>与/t/<team-id>一律返回 404,无视任何用户/团队的个人开关。详见 公开资料。 - 用户资料 / 团队资料默认值 — 用户(或团队)尚未自定义某字段时使用的默认值。修改默认值会立即对继承用户生效;不会覆盖已显式设置的值。
团队加入门槛
站点级硬性最低要求,每个团队都必须满足。所有者只能在此基础上加严,不能放松。
- 默认要求 2FA — 任何团队都要求成员至少有一个 TOTP 认证器或 Passkey。
- 默认要求验证邮箱 — 任何团队都要求成员的主邮箱已验证。
WARNING
开启这些底线会立即对所有现有成员生效 — 没有满足条件的成员将无法继续团队操作,直至自行补齐。请先通知成员后再切换。
子团队(嵌套团队)
整个子团队特性也在这一页配置。默认值适用于大多数运营场景;按需关掉各个开关可缩小特性范围。完整语义见 团队 → 子团队。
- 启用子团队 — 总开关。关闭后所有子团队接口返回 403,UI 的“子团队”标签被隐藏,数据库中的
parent_team_id行被忽略(保留但不参与继承,可随时重新启用而不丢数据)。 - 最大嵌套深度 — 硬性上限,校验区间 1–20。默认值 5 足以覆盖大多数组织;调大会让每次授权检查多一次 DB 往返。
- 继承团队成员资格 — 开启(默认)后,父团队成员在每个后代团队上都至少拥有相同角色(
有效 = max(直接, 继承))。关闭后仅看直接成员行 —— 子团队的管理员必须显式添加。 - 继承已验证域名 — 开启(默认)后,上级域名作为只读条目(带
inherited_from)出现在子团队列表,子团队为上级已验证父域的子域添加时也会自动通过验证。关闭后子团队必须重新自行验证想用的域名。 - 公开资料默认展示子团队 — 设置站点级默认
default_team_profile_show_sub_teams。每个团队仍可在 团队 → <team> → 设置 → 公开资料 → 子团队 中覆盖。
通知与 Telegram
- Telegram 通知源 — 用于推送 Telegram 通知的已启用 Telegram OAuth 源 slug,复用其 bot token。留空即关闭 Telegram 投递(邮件和 Webhook 投递不受影响)。详见 通知。
诊断
- Login error 保留天数 —
login_errors表中失败登录记录的保留期,超过后由 cron 清理。
Danger Zone
会改变数据库形态的工具,每个都是一次幂等的批量迁移,重复执行安全。
- Migrate secrets to Secrets Store — 加密 site_config 中的密钥(验证码 secret、社交源
client_secret、SMTP/IMAP 密码、GitHub README PAT、OAuth 应用client_secret)。需先绑定SECRETS_KEY。 - Migrate D1 secrets — 把 bearer 类机密(PAT、OAuth token/code、邀请 token、邮箱验证码、二次验证码、单条备用码)替换为 HMAC-SHA256 哈希。明文不再存储;候选值在查询时同样哈希后用
WHERE col = ?比较。 - 迁移团队为 team-as-user 行 — 为每个团队补建一个
kind = 'team'的合成users行,使oauth_apps.owner_id能统一连接。 - 迁移图片代理映射 — 为关闭式图片代理上线之前已经存在的头像 / 图标 URL 注册映射。
- 迁移恢复码 — 重新哈希历史明文备用码。
- 站点重置 — 清空并重新初始化。目标管理员需先签署一封邮件确认;管理面板再要求输入确认词触发清空。具有破坏性,且需要已配置邮件提供商。该按钮仅在
wrangler.jsonc中设置ENABLE_RESET = "true"时显示。
OAuth 来源
Admin → OAuth Sources 是配置所有社交登录提供商的地方。与简单的开关不同,每个来源是一个独立命名的 OAuth 连接,拥有自己的 slug、凭据和显示名称,支持同一提供商类型的多个来源(例如两个 GitHub 应用,或 Keycloak 与 Google 并存)。
来源字段
| 字段 | 说明 |
|---|---|
| Slug | 唯一 URL 键 — 出现在回调 URL 中,格式为 /api/connections/<slug>/callback |
| 提供商 | 基础 OAuth 类型(GitHub、Google、Microsoft、Discord、Telegram、X、Cloudflare、通用 OIDC、通用 OAuth 2) |
| 显示名称 | 显示在登录/注册按钮上的标签 |
| Client ID | OAuth 应用的客户端 ID |
| Client Secret | OAuth 应用的客户端密钥 |
| 受信任 | 为 true(默认)时,通过该来源的社交登录跳过邮箱验证 |
| 启用 | 切换是否在登录页面显示该来源,禁用不会删除数据 |
通用 OIDC 来源
当提供商为通用 OpenID Connect 时,会出现额外的端点 URL 字段:
- Issuer URL — 提供商的 issuer 地址(如
https://accounts.example.com)。点击自动发现按钮,Prism 会从{issuer}/.well-known/openid-configuration自动填充三个端点。 - 授权 URL — OAuth 2.0 授权端点
- 令牌 URL — 令牌交换端点
- 用户信息 URL — 获取用户资料的端点
可选的 Scopes 字段用于自定义请求的权限范围(默认:openid email profile)。
通用 OAuth 2 来源
当提供商为通用 OAuth 2 时,同样显示上述三个 URL 字段,但没有 OIDC 自动发现功能,需手动填写全部 URL。
回调 URL
每个来源的回调 URL 为:
https://<your-prism-domain>/api/connections/<slug>/callback在提供商的开发者控制台创建 OAuth 应用时,请注册此 URL。
详细的各提供商配置说明请参阅社交登录配置。
邀请
当注册模式为仅限邀请时,邀请标签页可创建和撤销邀请令牌。
- 邮箱(可选) — 将邀请限定到特定邮箱地址
- 最大使用次数 — 留空表示不限次数
- 有效期(天) — 可选
邀请链接可直接复制分享。邮件发送需要配置邮件提供商。
用户
用户列表支持搜索和排序。点击用户行可打开详情视图。
用户操作
| 操作 | 效果 |
|---|---|
| 更改角色 | 在 user 和 admin 之间切换 |
| 停用 | 阻止登录;现有令牌在过期前仍然有效 |
| 标记邮箱已验证 | 手动验证而无需发送邮件 |
| 删除 | 永久删除用户及其所有数据(级联删除会话、应用、关联等) |
删除用户不可逆。其 OAuth 应用也会一并删除,这将导致使用这些应用的所有第三方集成失效。
如果用户名在 wrangler.jsonc 的 LOCKDOWN_USERS 环境变量中列出,删除按钮将被隐藏且 API 返回 403 — 该用户永久受保护。详见 配置 → Wrangler 绑定与变量。
账号详情页
Admin → Users → 管理 会完整打开一个账号。列表视图只给你一行和三个开关;这个页面覆盖的是用户能对自己做的一切 —— 这些能力此前只存在于 /api/user/me/* 之后,因此除本人外谁也够不着。
概览
| 操作 | 说明 |
|---|---|
| 修改用户名 / 邮箱 / 显示名称 | 自助 API 没有任何路径能改这些。修改邮箱会清除其已验证状态,除非在同一请求中一并设置 |
| 设置或清除密码 | 系统不会通知用户,用户也不会知道该密码 —— 请通过带外渠道交付并让其自行修改。若账号没有可用的第三方登录,清除密码会被拒绝 |
| 重置 2FA | 移除全部认证器、Passkey以及恢复码。此后仅凭密码即可登录 |
| 移除单个因素 | 适用于只丢了一个认证器的情况 |
| 验证 / 提升 / 删除邮箱 | 包括主邮箱 —— 自助流程只能通过提升一个已验证的备用地址来更换主邮箱 |
访问凭据 —— 个人访问令牌、已绑定的第三方、GPG 密钥和已授权的应用,每项都可撤销。撤销授权时会一并删除据此签发的令牌和授权码;只撤销记录却留下访问权,比什么都不做更糟。若账号没有密码且只剩最后一个第三方绑定,解绑会被拒绝。
资源 —— 个人域名,以及该账号所属的全部团队。成员关系在团队本身修改,站点管理员可以打开任意团队。
审计 —— 该账号自己的日志:它做过的一切,以及管理员对它做过的一切。
该页面上的每一项操作都会同时写入平台日志和用户自己的审计日志,后者带 site_admin: true 标记。第二份副本正是关键:管理员修改他人凭据,恰恰是账号持有者最需要能查到的事件。
此处永远不会明文返回令牌。令牌以哈希形式存储,管理员并不比其他人更有理由读到它。
「以该用户身份登录」是刻意不提供的。 上述每一项操作都带着操作者的名字;为他人签发的会话会把这些操作洗进用户自己的历史里,而在签发那一刻做多少日志,都修不好系统其余部分随后记录的内容。
应用
应用列表显示所有用户的全部 OAuth 应用,包括:
- 所有者用户名
- 验证状态
- 启用/停用状态
应用审核
| 操作 | 效果 |
|---|---|
| 验证 | 在授权页面上为应用添加已验证徽章 |
| 停用 | 阻止应用完成新的授权流程,现有令牌继续有效 |
已验证的应用在授权页面上显示对勾标记,表示已由管理员审核。
团队
Admin → Teams 列出全实例所有团队,包含所有者、成员数和加入门槛标记。
| 操作 | 效果 |
|---|---|
| 查看 | 浏览成员、所属应用、已验证域名 |
| 管理 | 无需加入即可以所有者级权限打开该团队自己的页面 |
| 添加成员 | 将任意账号加入团队,角色最高可至 co-owner |
| 创建 | 新建团队并指定其所有者 |
| 解散 | 删除团队。团队拥有的应用会先被重新分配给团队所有者,避免被级联删除 |
如果团队名称在 LOCKDOWN_TEAMS 环境变量中列出,解散按钮将被隐藏且 API 返回 403。详见 配置 → Wrangler 绑定与变量。
disable_user_create_team 会对非管理员隐藏「新建团队」按钮 — 启用后只有管理员能创建团队(已存在的团队继续工作)。
站点管理员对所有团队的权限
站点管理员对每一个团队都拥有所有者级权限,无论是否为其成员 —— 而且即便对自己所属、甚至自己作为成员拥有的团队,这份权限也是以站点身份行使的。这里没有另一套仅供管理员使用的团队 API —— /api/teams/* 直接把管理员当作所有者对待,因此普通团队页 /teams/:id 就是管理界面,不会与另一套平行实现逐渐脱节。
由于这份权限属于站点而非某个成员身份,团队页会始终显示一条横幅予以说明,并且每次操作都会被标记为 site_admin: true —— 对每一个团队都如此,哪怕管理员正是该团队的所有者。若要改以自己的成员身份行事,管理员可使用切换到普通视图(见下文)。
从团队外部可以做到:
- 打开并编辑任意团队 —— 名称、描述、头像、资料可见性、加入门槛、角色权限、群组、应用与域名。
- 把任何人加入任何团队。团队自身的加入门槛(2FA、已验证邮箱)以及受限账号的范围限制会被覆盖;审计记录会在
bypassed字段中写明具体跳过了哪些检查。 - 修改任意成员的角色,包括将成员提升为
owner(同一次操作会把在任所有者降为 co-owner),也包括直接把所有者降级。 - 移除任意成员,包括所有者。只要还有人可以提升,剩余成员中级别最高者(同级取加入最早者)就会在同一批次中接任,因此常规情况不会意外让团队失去所有者。
- 转移所有权,目标甚至可以是尚未加入的用户。
没有所有者的团队
在无人可提升的情况下降级或移除所有者,会让该团队没有所有者。这是刻意可达的状态:另一种做法是拒绝操作,并要求管理员去提升一个他们未必想提升的人 —— 那等于团队所有者压过了站点。
这不会破坏任何东西。没有任何 schema 约束要求存在所有者行;「没有所有者」正是每个团队在创建之后、加入第一名成员之前的样子;而 dissolveTeam 在重新分配应用时本来就会回退到执行操作的管理员。管理员团队列表会把所有者显示为 —,任何管理员都可以把某人提升到该席位。
两个操作都会在响应中说明(owner_vacated),审计记录也带同样的标记,因此团队能够看到它发生了,而不是事后才发现。
有两条限制对管理员同样生效。它们都不是「所有者压过管理员」—— 对团队所有者同样成立:
- 所有权不能交给通过团队邀请注册的账号 —— 否则该限制就能从内部被重新配置。
LOCKDOWN_TEAMS仍然阻止删除。该列表由部署实例的人设置,属于任何管理员之上的一层。
每一次越权操作都会写入该团队自己的审计日志,元数据中带 site_admin: true,使团队能够分辨这是所有者自己的变更,还是站点越过他们做出的操作。
权限提升绑定在会话上。个人访问令牌只携带其自身被赋予的作用域,因此管理员的 apps:write 令牌仍然只是一个 apps:write 令牌,不会变成站点级万能钥匙。
普通视图
覆盖权限默认开启,但管理员可以关闭它。在任意「管理员本人确为成员」的团队上,横幅里会给出切换到普通视图:切换后,当前会话改以该成员身份行事 —— 使用真实角色、写入自己的审计条目(不带 site_admin 标记),团队自身的加入门槛也重新生效。横幅随之翻转,提供切回的入口。
这是当前会话的一种视图,而非持久设置:开启期间,仪表盘会在每个请求上带上 X-Prism-Team-View: member 头,而刷新页面即回到默认的管理员视图。这是刻意为之 —— 忘记自己切换过的管理员,绝不会被锁在一个自己并不属于的团队之外。在普通视图下打开一个自己并非成员的团队,会显示一段简短的「切回」提示,而不是团队内容 —— 与任何非成员看到的完全一致。
邀请链接注册
开启 enable_team_invite_registration 后,Admin → Teams 会多出两个控制项。
授权 / 撤销 —— 授予团队发放「可创建账号」邀请链接的权限。这是两道门中的第二道;没有它,团队所有者自己的开关不起任何作用。撤销时会一并关闭该开关,使通道立即关停,而不是在日后恢复授权时自动重新打开。
豁免项通过同一个端点设置。只有邮箱验证可被豁免,且仅限管理员操作 —— 它是唯一成本随注册量线性增长的检查。Captcha、工作量证明以及各类限流一律不可豁免。
解散(分段) —— 解散签发过账号的团队会注销这些账号,因此不走普通的删除按钮(对这类团队会返回 409)。阶段一用一条语句一次性停用全部受影响账号,无论数量多少。阶段二在 restricted_dissolve_grace_hours 之后分批删除,中间留有撤销窗口。
只删除来源为该团队的账号。用自己的 Prism 账号加入的成员、以及已转换的账号,均不受影响。
GET /api/admin/restricted-users?invite_token=… 可列出某个链接产生的所有账号 —— 邀请码泄漏时就用它定损。
完整模型见 团队 → 受限账号。
请求日志
Admin → Request Logs 是 Worker 每条请求的分页可筛选表 — 方法、路径、状态、耗时、IP、UA、用户 ID(如有登录)以及对应的审计日志条目(如有)。
- 筛选:按方法、状态范围、路径前缀或用户。
- Spectate:打开类似
tail -f的实时视图,自动刷新。 - 导出 CSV:把当前筛选导出为 CSV。
- 详情:单条请求的完整 timing 和审计联动。
- 清空:删除整张表(或仅清空 spectate 缓冲)。
请求日志独立于审计日志:一次请求可能未引发任何审计动作,cron 触发的审计也没有对应的请求行。
记录出站请求 是单独的调试开关,用于记录 Worker 发往外部 API 的请求,例如 Telegram 和 Discord 通知投递。开启后,Prism 会把这些调用写入 request_logs,用外部 URL 作为 path,并在详情中保存已脱敏的请求 / 响应正文。除非正在排查第三方投递失败,否则请保持关闭,因为它会记录消息内容并让每次出站调用额外读取一次 KV。
登录错误
Admin → Login Errors 列出所有失败的认证尝试(密码错误、TOTP 错误、挑战过期等),含 error_code、identifier、IP、metadata。login_error_retention_days 控制保留时长,超时由 cron 清理。
域名
Admin → Domains 列出实例上的全部域名,个人与团队的都在内,支持搜索并按验证状态筛选。此前域名只能通过其所有者账号或团队访问,而这对运营者真正关心的问题来说是错误的索引:谁声明了 example.com?
| 操作 | 效果 |
|---|---|
| 强制验证 | 不检查 DNS 直接标记为已验证 |
| 撤销验证 | 清除已验证标记 |
| 删除 | 从其所有者处移除该域名 |
强制验证是覆盖,不是校验。它面向 worker 无法访问其 DNS 的域名 —— split-horizon、内部 TLD、注册商故障 —— 在这些场景下的替代方案是该域名永远无法使用。审计记录会写入 method: admin_override,正是为了让「管理员断言的已验证」与「DNS 记录证实的已验证」始终可区分。
实例级操作
管理界面的其余部分都是一次处理一行。这些操作一次作用于全部对象,且每一项都是对事故的响应,而非日常任务。
登出所有人
Admin → Settings → Danger Zone → 登出所有人 会删除全部活跃会话。按下之前会显示数量。默认保留你自己的会话 —— 运营者在处置事故的中途把自己登出,就得穿过自己正试图控制的那套系统重新登录 —— 「包括我自己的会话」用于你的会话本身就是隐患的情况。
OAuth 令牌不受影响,那些按应用或按账号撤销,见下文。
切断一个应用
Admin → Applications → 撤销全部访问 会删除某个应用的全部令牌、刷新令牌、待用授权码以及同意记录。同意记录才是关键:若保留它们,每个用户都会直接跳过同意页 —— 这不是在泄漏事故中按下该按钮的人所理解的「已撤销」。可选择在同一操作中停用该应用。
切断一个账号
Admin → Users → 管理 → 访问凭据 → 撤销全部授权 对某个账号在全部应用上执行同样的操作。个人访问令牌是独立的,在各自区域撤销 —— 它们是账号自己的凭据,而非授予第三方的东西。
转移应用
Admin → Applications → 转移 可把应用移交给任意用户或团队。应用此前只能在用户与其自己的团队之间移动;没有任何方式能把它转给无关账号,而当所有者离开、或应用最初就建在错误身份下时,恰恰需要这个。client ID 与密钥保持不变,已部署的集成可继续工作。
解除邀请注册限制
Admin → Users → 管理 → 解除限制 把通过团队邀请注册的账号转换为普通账号。自助路径要求先拥有已验证的真实邮箱;已通过其他方式确认持有者身份的运营者可以豁免这一条,审计记录会写明他们这么做了。
作用域授权
Admin → Scope grants 列出提升权限的 OAuth 授权:site:* 允许应用在全实例范围内行动,site:team:* 则可以在没有团队所有者同意的情况下伸入该团队。
这些授权在授权时被写入,之后再未出现在任何界面上 —— 没有任何地方列出它们,也没有任何地方能撤销它们,想知道某个应用还持有什么,只能去读表。无法枚举的权限,就是无人能收回的权限。
撤销授权会阻止该权限被续期。据此已签发的令牌在过期前仍然有效,因为它们绑定的是应用而非这一行记录 —— 如需立即失效,请同时使用切断一个应用。
维护任务
Admin → Settings → 维护任务 可按需执行八个定时任务中的任意一个:域名重新验证、邮箱轮询、四项清理,以及两个回收器。
它们平时由 cron 触发器每六小时执行一次 —— 这对稳态是合适的节奏,但对运营者真正想起它们的每一个时刻都不合适:DNS 刚修好而域名仍未验证、解散已排期而账号仍在。
这里调用的就是调度器调用的同一批函数,并且是 await 而非延后执行,因此响应报告的是任务做了什么,而不是任务已启动。任务本身有计数的会返回计数;没有计数的返回 null,而不是编造一个 0。每次执行都会审计,失败也不例外。
数据库
Admin → Database 提供对实例背后 D1 数据库的直接访问:带行内编辑器的表结构浏览器,以及一个 SQL 控制台。
默认关闭,需显式开启
wrangler.jsonc 中的 D1_CONSOLE 变量决定这个界面存在多少。
| 取值 | 效果 |
|---|---|
未设置(以及 off / false / 0 / no,或任何无法识别的值) | 默认值。 界面消失 —— 端点返回 404,标签页不再显示 |
read-only / readonly / read | 仅浏览与 SELECT。一切写入都会被拒绝,包括显式带 allow_write 的请求 —— 调用方无法翻越运营者的设置 |
full / on / true / 1 | 完全访问,但下述审计日志除外 |
默认关闭,因为这是产品中最宽的一扇门,而「因为没人说不」就打开的门,对一个能清空整张表的功能来说是错误的默认。无法识别的值同样视为关闭,因此拼写错误会安全地失败。该设置每次请求都会读取,因此改动只需一次 wrangler deploy。
KV_CONSOLE 以同样方式控制键值浏览器,未设置时跟随 D1_CONSOLE,因此两者都不动的实例完全没有直接存储访问入口。
审计日志在此处只追加
audit_events 与 audit_log 可以从控制台读取,但永远无法写入 —— 无论是 full 模式、显式带 allow_write,还是运营者铁了心要改。行编辑器拒绝插入、更新与删除;SQL 控制台拒绝任何提到它们的非读取语句。sqlite_master(以及 PRAGMA writable_schema)出于同一理由被间接纳入:那正是绕开「按表名设限」的手法 —— 把表改名。
理由就是本页的其余部分。站点管理员可以伸进任意团队、重置任何人的凭据、读取数据库的大部分内容 —— 而「这是谁干的」的答案就在这两张表里。一个能编辑它们的控制台会让这个答案一文不值。
语句检查刻意从宽:对其他表的写入,只要恰好包含 audit_events 这个字符串,同样会被拒绝。代价是重写一次语句;反向的错误代价则是一份可被篡改的审计日志。
这是对该界面的限制,不是密码学保证。持有 Cloudflare 账号的人可以直接对 D1 执行 SQL,这里的任何东西都拦不住。它的作用是让产品本身不提供这个操作 —— 于是篡改必须离开产品进行,那是另一种行为,也留下另一种痕迹。
它之所以存在,是因为其他每一个管理界面都是数据库的「精选视图」,而精选视图总会恰好少了你此刻真正需要的那一列。它同时也是整个产品中最危险的界面 —— 一条语句就能清空一张表或授予管理员权限 —— 因此有三重约束:
- 写操作必须显式开启。 任何非纯读取的语句在写入模式关闭时都会被拒绝,这样一次手滑的控制台会话不会毁掉它本只想读取的数据。
PRAGMA x = y计为写操作;分类在不确定时一律偏向「写」。 - 全部审计。 每一条语句 —— 读取、写入、以及被拒绝的 —— 都会带着 SQL、行数和操作者写入平台审计日志。
- 标识符绝不从输入拼接。 浏览器和行编辑器会先把表名、列名与实时表结构比对后再加引号。只有控制台接受原始 SQL,且它是一次显式的、被审计的操作。
浏览表
选择一张表即可分页浏览其行。表头标出主键列,建表 DDL 显示在表格上方,筛选框接受原始 SQL 的 WHERE 片段(不含关键字本身)。
行可以就地编辑、插入和删除。更新只发送你实际改动过的列。输入框留空表示 NULL,表格中也会与空字符串区分渲染 —— 二者并非同一个值,在你即将编辑的表里不能看起来一样。
没有主键的表可以浏览但不能按行编辑:无法定位单独一行。存在 rowid 时会使用它,因此这只影响 WITHOUT ROWID 表。这类表请从控制台编辑。
SQL 控制台
对线上数据库执行一条或多条语句。用分号分隔的多条语句在同一个事务中执行,因此中途失败的脚本不会留下任何痕迹。结果集上限 500 行,达到上限时会标记为已截断。
写入模式是一个开关,开启后会额外增加一次确认步骤,在执行前再次展示该语句。
端点
| 端点 | 用途 |
|---|---|
GET /api/admin/db/tables | 表清单,含行数、列与 DDL |
GET /api/admin/db/tables/:table/rows | 分页读取行(page、limit、order_by、dir、where) |
POST /api/admin/db/tables/:table/rows | 插入一行 |
PATCH /api/admin/db/tables/:table/rows | 按主键更新一行 |
DELETE /api/admin/db/tables/:table/rows | 按主键删除一行 |
POST /api/admin/db/query | 执行 SQL(修改数据需 allow_write) |
以上全部位于 requireAdmin 之后,且仅接受会话认证。
键值浏览器
Admin → Database 的第三个标签页。KV 里存放着决定实例某一天行为的大部分内容 —— 调试开关、进行中的 OAuth 状态、sudo 授权、待执行的站点重置 —— 而其他任何界面都不会呈现这些。
KV 没有结构定义,因此导航是命名空间选择器加前缀输入框,而不是表清单。暴露两个命名空间:sessions(会话、系统标志、签名密钥)与 cache。列表使用 KV 自己的不透明游标分页,因此只能前后翻页,无法跳到指定页码。
密钥材料不会返回。 system:jwt_secret 及签名密钥对会被标记并且不带值返回,也无法通过该界面写入。读取 JWT 密钥等同于能为任意账号签发会话 —— 而这正是本管理界面刻意不提供的能力,且「可自选的签名密钥」与「被窃取的签名密钥」是同一种能力。删除这类键是允许的:那是轮换操作,会在审计日志中留下明显记录,下一次请求会重新生成。依赖旧密钥的全部会话与令牌都会失效。
按前缀清空每次调用限一页,并会跳过密钥材料;响应会说明删除了多少、跳过了多少,以及是否还有剩余。
KV_CONSOLE 对它的控制方式与 D1_CONSOLE 对数据库标签页的控制一致,未设置时跟随 D1_CONSOLE。
审计日志
审核日志标签页展示平台作用域日志(Transparent Platform Control)——即所有管理员操作。用户与团队各自拥有独立的作用域日志;完整模型、筛选与作用域化 Webhook 详见 审核日志。它是一个分页的追加型重要事件列表:
| 事件 | 触发条件 |
|---|---|
user.register | 成功注册 |
user.login | 成功登录 |
user.login.failed | 登录失败 |
user.logout | 退出登录 |
user.delete | 账号删除 |
user.password_changed | 通过 资料 → 安全 改密 |
totp.enabled | TOTP 认证器启用 |
totp.disabled | TOTP 认证器移除 |
passkey.registered | 新 Passkey 已添加 |
passkey.deleted | Passkey 已删除 |
gpg.key_added | 注册了 GPG 公钥 |
gpg.key_deleted | 删除了 GPG 公钥 |
gpg.login | 通过 GPG 签名挑战登录 |
oauth.authorize | 用户批准了 OAuth 应用 |
oauth.token | 令牌已颁发 |
oauth.consent_revoked | 用户撤销了应用授权 |
oauth.2fa.verify | 步骤提升 2FA 完成 |
oauth.2fa.sudo_revoked | 用户主动结束了 sudo 宽限期 |
team.created | 团队创建 |
team.member_added | 成员加入(邀请或管理员添加) |
team.member_removed | 成员退出或被移除 |
team.transferred | 团队所有权转移 |
domain.added / verified / deleted | 域名生命周期 |
connection.added / removed | 社交账号绑定生命周期 |
oauth_source.create / update / delete | OAuth 源生命周期 |
invite.create / revoke | 站点邀请生命周期 |
admin.config.update | 站点配置已更改 |
admin.user.update | 管理员修改了用户 |
admin.user.delete | 管理员删除了用户 |
admin.app.update | 管理员验证或停用了应用 |
admin.team.delete | 管理员解散了团队 |
admin.secrets.migrate | 触发了 site_config 或 D1 secrets 的迁移 |
admin.reset.* | 站点重置请求 / 取消 / 确认 |
admin.db.query.read | SQL 控制台执行了只读语句 |
admin.db.query.write | SQL 控制台执行了写入语句 |
admin.db.query.error | 控制台语句被拒绝或执行失败 |
admin.db.row.insert / update / delete | 通过表浏览器编辑了行 |
admin.user.password_set | 管理员设置或清除了账号密码 |
admin.user.2fa_reset | 管理员移除了全部第二因素 |
admin.user.totp_removed / passkey_removed | 管理员移除了单个因素 |
admin.user.token_revoked | 管理员撤销了个人访问令牌 |
admin.user.connection_removed | 管理员解绑了社交账号 |
admin.user.gpg_key_removed | 管理员移除了 GPG 密钥 |
admin.user.email_verified | 管理员将地址标记为已验证 |
admin.user.primary_email_changed | 管理员提升了备用地址为主邮箱 |
admin.user.email_removed | 管理员删除了备用地址 |
admin.user.domain_removed | 管理员移除了个人域名 |
admin.user.authorization_revoked | 管理员撤销了 OAuth 授权 |
admin.user.converted | 管理员解除了邀请注册限制 |
admin.revoke.all_sessions | 实例上的全部会话被删除 |
admin.revoke.app | 某应用的令牌与同意记录被撤销 |
admin.revoke.user_grants | 某账号的 OAuth 授权被撤销 |
admin.app.transfer | 应用更换了所有者 |
admin.domain.force_verify / unverify | 管理员断言或撤销了域名验证 |
admin.domain.delete | 管理员删除了域名 |
admin.kv.read / write / delete | 读取或修改了某个键值条目 |
admin.kv.purge | 删除了某前缀下的全部键 |
admin.scope_grant.revoke | 撤销了站点或团队作用域授权 |
admin.session.revoke | 管理员结束了单个会话 |
admin.maintenance.run / error | 按需执行了定时任务 |
admin.users.bulk_delete / _deactivate / _activate | 对多个账号执行了批量操作 |
admin.team_invite.revoke | 管理员撤销了团队邀请链接 |
admin.user.notification_rulesets_cleared | 管理员重置了账号的通知规则 |
admin.notice.create / update / delete | 撰写或删除了公告 |
admin.notice.publish / unpublish | 公告上线,或被撤下 |
每条记录包含操作的 user_id(系统操作为 null)、action、可选的 resource_type / resource_id、metadata JSON 以及 ip_address。
OAuth scope 的完整参考见 OAuth → Scopes 与 团队 → OAuth scope。