OneTimeSecret v0.26.19 配置参数详解

OneTimeSecret v0.26.19 配置参数详解:ERB 默认模板与 config.yaml 双层体系、site/features/redis/emailer/jobs 十三区块逐项拆解、secret_options TTL 匿名钳制与会话 Cookie Secure 实测验证、弃用键 strict 策略与最小生产配置清单。

OneTimeSecretOneTimeSecret阅后即焚密码安全Redis缓存2026/10/26

OneTimeSecret v0.26.19 配置参数详解

OneTimeSecret v0.26.19 的配置由三层承载:随源码分发的 etc/defaults/config.defaults.yaml(约 2100 行的 ERB 模板,定义全部默认值)、etc/defaults/auth.defaults.yaml(认证模式专用默认值),以及运维侧的 config.yaml(覆盖默认值)与环境变量(最高优先级,直接写在 defaults 模板的取值表达式里)。本文基于 v0.26.19 官方 defaults 文件逐区块拆解全部核心参数,并附三项实测验证(匿名 TTL 钳制、登录用户 TTL 上限、语言列表)。

两个阅读要点:

  1. # Since vX.Y.Z 注记体系:defaults 文件里每个配置项尾部的 # Since vX.Y.Z 标记该设置首发于哪个版本;# Since unreleased 表示下一个版本才发布;没有任何标记的参数意味着它早于 v0.24.0 就存在。这是判断「这个开关在旧版本能不能用」的第一手依据。
  2. 弃用键是启动错误:自 v0.25.5 起 compatibility.deprecated_config_mode 默认 strict——启动时检测到已移除/已迁移的旧配置键(或其环境变量)会直接拒绝启动并在报错里给出迁移提示;只有显式设为 warn(记日志继续跑)或 silent(忽略)才放行。从旧版本升级配置文件时,先跑一次看报错即可拿到完整迁移清单。

一、环境信息

项目值
OneTimeSecret 版本v0.26.19(2026-10 官方最新稳定版)
配置取证对象etc/defaults/config.defaults.yaml(118 KB)、etc/defaults/auth.defaults.yaml
运行实例Debian 12 裸机源码部署(127.0.0.1:3000)与 Valkey 数据存储实例(192.168.146.131:8132),见本专栏 B1/B2 篇
测试账号admin@ots.local / q1w2e3r4(colonel 角色,API Token 鉴权)

部署过程本身见《Debian 12 安装 OneTimeSecret v0.26.19 指南》与《OneTimeSecret v0.26.19 Docker Compose 部署指南》;产品定位与选型对比见《OneTimeSecret v0.26.19 介绍与选型指南》。

二、配置加载体系与优先级

defaults 文件是 ERB + YAML 模板:几乎每一行的取值表达式都是 <%= ENV['XXX'] || 默认值 %> 形态,即「环境变量优先,缺省回落到写死的默认值」。config.yaml 的加载层次在 defaults 之上,实际优先级从高到低为:

环境变量  >  config.yaml  >  config.defaults.yaml(ERB 渲染结果)

认证体系单独一份 defaults(auth.defaults.yaml),原因是 v0.24.0 引入了双认证模式(见第四节),两份文件的加载互不干扰。defaults 文件顶部注释原话:「A trailing # Since vX.Y.Z is the release that setting first shipped in; # Since unreleased means the next one. No marker means it predates v0.24.0.」

顶层区块共 13 个(行号为 v0.26.19 defaults 文件实查):

区块行号职责
site6站点、界面、秘密策略、认证、会话、中间件、网络、管理面(最大区块)
brand1079品牌定制(logo、配色、产品名)
features1108功能开关(多区域/Incoming/自定义域名/组织)
redis1375数据存储连接(Redis/Valkey 兼容)
emailer1429邮件发送(SMTP 及提供商)
email_providers1485发件域名 DNS 校验(SES/SendGrid/Lettermint)
mail1586邮件地址校验(Truemail)
jobs1659后台任务(RabbitMQ)
internationalization1858多语言
diagnostics1936诊断与 Sentry
development1985开发模式专用
compatibility2084弃用配置键的处理策略
experimental2102实验特性(当前为空对象)

三、站点基础:site.host / ssl / secret 与界面

3.1 四个根参数

参数环境变量默认值说明
site.hostHOSTlocalhost:3000对外域名,拼进所有生成的链接
site.sslSSLfalse生成链接时用 https 还是 http;同时参与会话 Cookie 策略(见第五节)
site.secretSECRETnil全局加密密钥。秘密内容的加密、会话签名都派生自它。模板注释用大写字体警告:设置后不可更改,改了会导致既有秘密无法解密,更换前先读官方 runbook(docs/runbooks/secret-rotation.md)并做离线备份
site.secret_verifier_modeSECRET_VERIFIER_MODEwarnSince v0.26.0。启动时自检机制:应用把一份 HKDF 派生的校验值存进数据存储,每次启动比对,发现 SECRET 变了(或连到了别的实例的数据)不再静默失效而是显式报告。warn=大声记日志继续启动(默认);enforce=拒绝启动;off=跳过检查(多租户共用数据存储时的逃生门)

secret_verifier_mode 是 v0.26 系列最值得开的开关:密钥漂移是「一切秘密悄悄读不出来」级别的事故,enforce 把它提前到启动时刻。

3.2 法务链接 site.legal(Since v0.26.8)

六个 URL 参数:terms_url(TERMS_URL)、privacy_url(PRIVACY_URL)、dpa_url(DPA_URL)、cookie_url(COOKIE_URL)、aup_url(AUP_URL)、security_url(SECURITY_URL)。规则:未设置或空值什么都不渲染(没有占位符、没有死链);绝对地址(https://…)新标签页打开,相对路径(如 /terms)站内解析。企业内网部署建议至少把 terms/privacy 指向内部合规页。

3.3 界面开关 site.interface

参数环境变量默认值说明
interface.ui.enabledUI_ENABLEDtrue关掉后 Web 界面只剩一页说明文字,纯 API 模式
interface.ui.homepage.modeUI_HOMEPAGE_MODEnil首页模式。internal:仅内网来源(按 CIDR 匹配)可见完整首页;external:外部访客看到受限首页。注释明确:这只改内容展示,不扩大也不收紧任何 API 权限
interface.ui.homepage.matching_cidrsUI_HOMEPAGE_MATCHING_CIDRS空逗号分隔的网段列表,如 203.0.113.0/24,198.51.100.0/24
interface.ui.homepage.mode_headerUI_HOMEPAGE_MODE_HEADERO-Homepage-ModeCIDR 不命中时的回落判定头
interface.ui.homepage.disabled_variantDEFAULT_DISABLED_HOMEPAGE_VARIANTnilSince v0.25.10。首页表单被鉴权门住时的落地页样式:closed/minimal/v1
interface.ui.capabilities.*UI_CAPABILITIES_BURN/SHOW/RECEIPT/RECIPIENT全 trueSince v0.25.0。控制创建表单上四个可选字段(焚烧/查看回执/收件人)的显隐,只影响 UI 不影响 API
interface.api.enabledAPI_ENABLEDtrue总开关,false 时所有 /api/* 直接 404
interface.api.guest_routes.enabledAPI_GUEST_ROUTES_ENABLEDtrue匿名(guest)API 路由总开关
interface.api.guest_routes.{conceal,generate,reveal,burn,show,receipt}API_GUEST_*全 true细粒度控制匿名可用的六类操作(仅 enabled=true 时检查)

页眉页脚:header.enabled(HEADER_ENABLED)、header.navigation.enabled(HEADER_NAV_ENABLED)、footer_links.enabled(FOOTER_LINKS)、logo.href/show_name/prominent(LOGO_LINK/LOGO_SHOW_NAME/LOGO_PROMINENT,Since v0.26.0)、workspace_links.enabled(WORKSPACE_LINKS,Since v0.25.0)。

3.4 品牌定制 brand.*(顶层区块)

全部 Since v0.26.0(个别 v0.26.3),统一 BRAND_ 前缀:BRAND_PRIMARY_COLOR、BRAND_PRODUCT_NAME、BRAND_PRODUCT_DOMAIN、BRAND_SUPPORT_EMAIL、BRAND_CORNER_STYLE、BRAND_FONT_FAMILY、BRAND_BUTTON_TEXT_LIGHT、BRAND_LOGO_URL、BRAND_LOGO_DARK_URL(v0.26.3)、BRAND_LOGO_ALT、BRAND_FAVICON_URL、BRAND_APPLE_TOUCH_ICON_URL、BRAND_OG_IMAGE_URL、BRAND_TOTP_ISSUER(MFA 认证器里显示的发行方名)、BRAND_SIGNATURE_NAME(邮件签名用名)。旧的 header.branding 嵌套(LOGO_URL/LOGO_ALT/SITE_NAME)已弃用,仍作回落兼容(#3612)。

四、秘密策略:site.secret_options(核心区块)

4.1 TTL 三参数与钳制规则

参数环境变量默认值说明
secret_options.default_ttlDEFAULT_TTLnil创建秘密未传 TTL 时的默认值;nil 表示交给前端默认档
secret_options.ttl_optionsTTL_OPTIONSnil创建页展示的 TTL 档位(秒),逗号分隔字符串
secret_options.ttl_max_anonymousTTL_MAX_ANONYMOUS604800(7 天)Since v0.26.3。匿名创建的 TTL 上限

ttl_max_anonymous 的注释给出了精确的钳制规则(原文要点):默认 604800 是「sane default, not a hard limit」,自托管可升可降;实际生效上限 = min(此值, ttl_options 最大值),再以 365 天封顶;开启计费后免费套餐额度同样参与取小,保证匿名额度不超过认证免费用户。PLAN_TTL_ANONYMOUS 是弃用别名,只在读取这一行时被兼容(ORGANIZATION 的免费套餐回退逻辑读新名不读别名)。

实测一:匿名创建的静默钳制。匿名调用 v1 接口请求 30 天 TTL(2592000 秒):

$ curl -s -X POST http://192.168.146.131:8132/api/v1/share \
    -d 'secret=C1-anon-ttl-2592000&ttl=2592000'
{"custid":"anon","metadata_key":"m1cocto45tkypw3dnsf3hppdpkidvaczs7hvwlrw9wxzpyur1f57fl32kj4kd3","secret_key":"3fpp1z3ex6fahh2hf83q5tmxltxjfc892uhiv9d4divzwhx2q5zyd1h68orv37","ttl":604800,"metadata_ttl":1209600,"secret_ttl":604800,"metadata_url":"http://192.168.146.131:8132/receipt/m1cocto45tkypw3dnsf3hppdpkidvaczs7hvwlrw9wxzpyur1f57fl32kj4kd3","state":"new","updated":1793007282,"created":1793007282,"recipient":[],"share_domain":"","passphrase_required":false,"identifier":"m1cocto45tkypw3dnsf3hppdpkidvaczs7hvwlrw9wxzpyur1f57fl32kj4kd3","secret_identifier":"3fpp1z3ex6fahh2hf83q5tmxltxjfc892uhiv9d4divzwhx2q5zyd1h68orv37","has_passphrase":false,"recipients":[],"receipt_ttl":1209600,"receipt_url":"http://192.168.146.131:8132/receipt/m1cocto45tkypw3dnsf3hppdpkidvaczs7hvwlrw9wxzpyur1f57fl32kj4kd3"}

请求 2592000,响应 ttl: 604800——超限不报错,静默钳制到 7 天。另一个值得注意的细节:匿名的 metadata_ttl/receipt_ttl 是 1209600(14 天),恰好是秘密寿命的两倍,回执比秘密活得更久,方便创建者事后查看状态。

实测二:登录用户不受匿名上限约束。用 colonel 账号的 API Token 走 v2 接口请求同样的 30 天 TTL(v2 创建接口的 JSON 载荷要求嵌套在 "secret" 键下,扁平写法会得到 {"error":"Incorrect payload format"}):

$ curl -s -X POST http://192.168.146.131:8132/api/v2/secret/conceal \
    -u "admin@ots.local:<apitoken>" -H 'Content-Type: application/json' \
    -d '{"secret":{"secret":"C1-ttl-clamp-verify-2026","ttl":2592000}}'
{"success":true,"record":{"receipt":{...,"secret_ttl":2592000,"metadata_ttl":2592000,"receipt_ttl":2592000,"lifespan":2592000,...},"metadata":{...},"secret":{...,"lifespan":2592000,"secret_ttl":2592000,...}},...}

(响应较长,上面节选 TTL 相关字段,数值逐字照抄。)secret_ttl: 2592000 原样生效——ttl_max_anonymous 只约束匿名调用方,要放开 30 天档位,要么给创建方配账号,要么调高该参数。

4.2 口令与内容限制

参数环境变量默认值说明
passphrase.requiredPASSPHRASE_REQUIREDfalse创建秘密时强制要求口令
passphrase.minimum_lengthPASSPHRASE_MIN_LENGTH4注释原话:4 位是为了挡住「一眼猜得到」的口令同时兼容既有集成
passphrase.maximum_lengthPASSPHRASE_MAX_LENGTH128口令长度上限
passphrase.enforce_complexityPASSPHRASE_ENFORCE_COMPLEXITYfalse强制大小写/数字/符号复杂度
content.maximum_lengthSECRET_MAX_LENGTH10000Since v0.26.0。秘密正文字符数上限,服务端创建时强制执行,同时是前端输入框限制的唯一真源。实测确认超长提交被拒(10000 字符 ≈ 一段日志或半份配置文件,放证书链要留意)
generated_value_display_ttlGENERATED_VALUE_DISPLAY_TTL60Since v0.24.0。「生成密码」功能的结果在回执页展示的秒数,0 关闭展示

4.3 匿名创建限流 create_rate_limit(Since v0.26.11)

参数环境变量默认值说明
create_rate_limit.enabledSECRET_CREATE_RATE_LIMIT_ENABLEDtrue保护性默认开启
create_rate_limit.max_per_ipSECRET_CREATE_RATE_LIMIT_MAX_PER_IP500每窗口、每个掩码后客户端 IP 的匿名创建次数
create_rate_limit.windowSECRET_CREATE_RATE_LIMIT_WINDOW3600计数窗口 1 小时
create_rate_limit.lockoutSECRET_CREATE_RATE_LIMIT_LOCKOUT3600触顶后锁 1 小时

这个限流器是真实安全事件驱动的:注释记录了缺陷编号 F-02——修复前现代 API 面的匿名创建完全不限速,单个匿名客户端实测可持续 ~113 请求/秒、每个 ~18 KiB,约 8.6 分钟就能写满 1 GiB 的 Redis(会话也在里面),等于全站瘫痪。计费桶按掩码后的 /24 网段划分(办公网/校园网 NAT 共享一个桶),所以默认值刻意放宽到注册限流的 10 倍;注释同时警告:调太紧等于给整个网段断供秘密创建。只对匿名生效,认证调用方不计费。

4.4 密码生成器 password_generation

default_length(PASSWORD_GEN_LENGTH,默认 12)、maximum_length(PASSWORD_GEN_MAX_LENGTH,默认 128,Since v0.26.0,服务端拒绝超长请求防 DoS,与前端 Zod 校验对齐)、character_sets.uppercase/lowercase/digit/symbol(PASSWORD_GEN_UPPERCASE 等,默认全开)。

五、认证开关与限流

5.1 site.authentication(六开关 + 三限流器)

参数环境变量默认值说明
authentication.enabledAUTH_ENABLEDtrue连同 API 鉴权一起关闭的总闸
authentication.signupAUTH_SIGNUPtrue注册开关。典型玩法:初始化阶段开放,手工建完账号后关闭,变身邀请制
authentication.signinAUTH_SIGNINtrue登录开关(临时维护时可关)
authentication.autoverifyAUTH_AUTOVERIFYfalseSince 前默认行为:新账号要收验证邮件才能登录。私有实例设 true 免邮件直达(前提是 SMTP 没配好也不想配)
authentication.requiredAUTH_REQUIREDfalse开启后首页的秘密表单必须登录才可见——「仅团队成员可创建」模式,但保留站点导航与品牌
authentication.allowed_signup_domainsALLOWED_SIGNUP_DOMAIN空逗号分隔的邮箱域名白名单,如 example.com,company.org;限制注册,不影响把秘密分享给任意邮箱

三个内建限流器(全部默认开启、窗口 1 小时、双层计数:按客户端 IP 一层 + 按提交邮箱一层兜底防换 IP):

限流器保护端点默认额度版本
reset_request_rate_limit忘记密码请求10/IP + 30/邮箱,锁 3600sSince v0.26.3
email_login_request_rate_limit魔法链接登录请求(仅 full 模式)30/IP + 90/邮箱,锁 3600sSince unreleased
create_account_rate_limit注册接口10/IP,锁 3600sSince v0.26.3

create_account_rate_limit 的注释同样记录了实际漏洞:修复前注册接口在两种认证模式下都无限速,攻击者可无限制造 Customer 记录(无 TTL)加欢迎邮件,用子地址(user+1@、user+2@…)把容量攻击折叠到同一个邮箱。环境变量对应 CREATE_ACCOUNT_RATE_LIMIT_ENABLED/MAX_PER_IP/WINDOW/LOCKOUT。

5.2 auth.defaults.yaml:simple 与 full 双模式

mode(AUTHENTICATION_MODE,Since v0.24.0)二选一:

  • simple(默认):纯 Redis 认证,不依赖 SQL。自托管最小部署用这个。
  • full:Rodauth 体系,需要 SQL 数据库(database_url 默认 sqlite://data/auth.db,可换 PostgreSQL/MySQL/MS SQL;迁移连接 database_url_migrations 仅 Postgres/MySQL/MSSQL 需要)。功能开关全在这里:lockout(防爆破锁定,默认开)、password_requirements(口令强度,默认开)、active_sessions(活跃会话管理,默认开)、remember_me(默认开)、verify_account(邮箱验证,默认开)、mfa(TOTP+恢复码,默认关)、email_auth(魔法链接,默认关)、webauthn(生物识别/安全钥匙,默认关,配套 webauthn_verify_account/webauthn_autofill Since v0.26.5)、sso(OmniAuth 外部身份源,Since v0.24.5;主开关下挂 OIDC/Entra ID/Google/GitHub/GitLab/Apple/SAML 七类提供商,各自独立环境变量)。

restrict_to(Since v0.25.0):把登录页限制为单一认证方式(password/email_auth/webauthn/sso 四选一,对应 AUTH_PASSWORD_ONLY 等四个环境变量)。注释强调这是访问控制不是展示偏好——其余方式的直接 404;四个互斥开关设了两个以上直接拒绝启动(#4139,fail loud 设计);底层 feature 没开时同样启动失败,绝不静默降级。

SSO 补充项:sso.allow_platform_fallback_for_tenants(自定义域名能否回落到平台级 SSO 凭据,默认 false)、sso.trust_email_for_linking(SSO 身份按邮箱自动关联既有账号,默认 false 且注释明言这是唯一被认可的例外,仅建议运营商同时掌控两边时开启,Since v0.26.2)。

六、会话与安全中间件

6.1 site.session

参数环境变量默认值说明
session.secretSESSION_SECRET回落到 site.secret会话签名密钥,可与全局密钥分离
session.expire_after—86400滚动过期 24 小时
session.absolute_timeoutSESSION_ABSOLUTE_TIMEOUT2592000Since v0.26.14。绝对过期 30 天,无论多活跃;0 关闭
session.key—onetime.sessionCookie 名
session.secureSESSION_COOKIE_SECURE条件逻辑见下文
session.same_site—laxstrict/lax/none;注释说明 lax 是 Stripe/OAuth 跳转所需
session.httponly—trueRack 层始终 true,JS 不可读

session.secure 是本文档最值得展开的一段条件逻辑(模板 504-510 行实抄):

<% if %w[true false].include?(ENV['SESSION_COOKIE_SECURE'].to_s) %>
secure: <%= ENV['SESSION_COOKIE_SECURE'] %>          # 显式设置永远赢
<% elsif ENV['SSL'] == 'true' %>
secure: true                                          # SSL 开 → Secure
<% elsif ENV['SSL'] == 'false' && %w[dev development test testing].include?(ENV['RACK_ENV'].to_s.downcase) %>
secure: false                                         # 仅非生产环境允许无 Secure
<% end %>

三个分支之外的缺口(生产模式 + SSL=false)走 fail closed:键缺省,由启动逻辑 ssl_enabled? 回落强制 secure: true。注释原话点破动机:生产环境 inheriting SSL=false 的部署几乎都是 TLS 在边缘代理终结,裸 HTTP 会话 Cookie 是降级缺陷而非合法配置;真要纯 HTTP 生产部署,请显式用 SESSION_COOKIE_SECURE=false。这正是本专栏 B2 篇排障实录第一坑的根因——纯 HTTP 部署登录态保持不住,SESSION_COOKIE_SECURE: "false" 是必填项。

session.skip_paths(Since v0.26.5):六个不落会话的探针路径 /health、/health/advanced、/auth/health、/api/v1/status、/api/v2/status、/api/v3/status。修复前负载均衡/监控的每次轮询都会写一个带完整 TTL 的会话键(#3997)。注意匹配是全路径精确等值,且注释警告永远别把 /api/v*/secret/*/status 加进来——那是带审计的能力令牌读,不是探针。

6.2 site.middleware(九个开关)

全部 MIDDLEWARE_ 前缀,Since v0.24.0:static_files(默认开)、authenticity_token(CSRF 校验,默认开——API 用 Token 鉴权时不受影响,浏览器会话流依赖它)、http_origin(默认关)、xss_header(默认关)、frame_options(默认开,防点击劫持)、path_traversal(默认开)、cookie_tossing(默认开)、ip_spoofing(默认关)、strict_transport(HSTS,默认开)。除排查特殊兼容性问题外不建议动。

6.3 site.network 与反代

assume_https(ASSUME_HTTPS,Since v0.26.2)、public_host_rewrite(PUBLIC_HOST_REWRITE,Since unreleased)、trusted_proxy(可信代理配置,决定 Rack::Request#ip 解析出的客户端 IP 可信度——它同时喂给首页 CIDR 判定和所有按 IP 的限流器,TLS 反代部署务必配置,否则限流会把整个代理出口当一个客户端)。

七、管理面:site.admin

管理面(Colonel)自己的三层防护,行号 786-1058:

访问围栏:admin.allowed_hosts(ADMIN_ALLOWED_HOSTS,Since v0.26.5,显式列出可服务管理面的主机名,未设置时锚定在 features.domains.default + site.host 及其 www 变体;设成空串也是合法值——运行时同样回落到锚定集合,但启动时打 WARN 提醒「你写的白名单什么都没圈住」);admin.allowed_cidrs(ADMIN_ALLOWED_CIDRS,Since v0.26.0,网络层白名单,不命中的请求得到和主机围栏一样的 404);rodauth_admin_url(Since v0.26.12)。

四档操作限流(全部 Since v0.26.11,COLONEL_ 前缀环境变量):

档位默认额度锁定保护对象
elevation5 次 / 900s900s提权重试(防爆破管理口令)
mutation120 次 / 300s300s一般写操作
destructive10 次 / 300s900sTIER 1 破坏性动词(purge、角色变更、吊销、删除、DLQ 清空);计费在确认与全部锁都通过之后,被 403 拒掉的尝试不扣预算
handle_resolve60 次 / 300s300s两个可能触发 10000 键 SCAN 的句柄解析读;其余 colonel 读操作刻意不限速(会打断控制台仪表盘)

独立会话边界(Since v0.26.11):admin.session.enabled(ADMIN_SESSION_LIFETIME_ENABLED,默认开)、idle_timeout 3600(ADMIN_SESSION_IDLE_TIMEOUT)、absolute_timeout 43200(ADMIN_SESSION_ABSOLUTE_TIMEOUT)。只管 /api/colonel 的 API 访问权,不缩短普通会话 Cookie;注释点出威胁模型:全局会话是 24h 滚动无上限,挂着的管理标签页等于常驻管理能力。两个坑:管理台若有轮询请求会把 idle 边界悄悄废掉;活动时钟是全站共享的,摸一下租户路由也能续上管理窗口。

八、功能开关:features.*

8.1 多区域 regions

enabled(REGIONS_ENABLED,默认 false)、current_jurisdiction(JURISDICTION,本实例所属法域)、jurisdictions(JURISDICTIONS,Since v0.25.5,格式 EU:eu.example.com,CA:ca.example.com 的 ID:域名对,法域感知部署用——自托管单实例用不到)。

8.2 Incoming Secrets(反向投递)

匿名用户经 /incoming 专用表单给预配置收件人发加密秘密,收件人收邮件通知:

参数环境变量默认值说明
incoming.enabledINCOMING_ENABLEDfalse只管本域;自定义域名的 incoming 由组织 entitlement 另行控制
incoming.memo_max_lengthINCOMING_MEMO_MAX_LENGTH50备注字段上限
incoming.default_ttlINCOMING_DEFAULT_TTL604800默认 7 天
incoming.default_passphraseINCOMING_DEFAULT_PASSPHRASEnil全部 incoming 秘密的统一口令
incoming.rate_limit.*INCOMING_RATE_LIMIT_*10/IP + 30/收件人,3600sSince v0.26.0,在秘密创建和发信之前按 IP 和收件人哈希双层限流
incoming.recipientsINCOMING_RECIPIENT_1..4空格式 email[,显示名],如 INCOMING_RECIPIENT_1=support@example.com,Support Team;邮箱启动即哈希,API 响应里永不出现明文

典型用途:安全页面的「向我们报告漏洞」入口。

8.3 自定义域名 domains

enabled(DOMAINS_ENABLED)、require_verified(DOMAINS_REQUIRE_VERIFIED,Since v0.25.2,注册未验证的自定义分享域直接拒)、serve_unverified(Since unreleased)、default(DEFAULT_DOMAIN,链接 URL 的默认域,空则用 site.host)、link_domains(LINK_DOMAINS,Since v0.26.5,链接域名选择器的白名单;注释详述三态语义——未设置给规范域、设置了精确给列表、设了但为空 = 启动报错,混合列表则剔除坏条目放行)、validation_strategy(DOMAINS_VALIDATION_STRATEGY:passthrough 默认不校验;approximated 走 approximated.app API 管 SSL 与校验,需配 APPROXIMATED_API_KEY 等五项;caddy_on_demand 配合 Caddy 按需 TLS,应用侧自查 TXT 记录 + A/AAAA + 443 握手)。

8.4 组织与其他

organizations.enabled(ENABLE_ORGS,Since v0.25.0)及其四子开关(ORGS_SSO_ENABLED、ORGS_CUSTOM_MAIL_ENABLED、ORGS_INCOMING_SECRETS_ENABLED、ORGS_AUDIT_LOGS_ENABLED Since v0.26.4,前三默认关、审计默认开);secret_activity.collect(SECRET_ACTIVITY_COLLECT,默认开,Since v0.26.4)、max_events 10000、geo_country_enabled(Since v0.26.5,活动记录带 GeoIP 国别);markdown.enabled(MARKDOWN_ENABLED,Since unreleased)。

九、数据存储:redis 区块(Redis/Valkey 通吃)

9.1 连接 URI 的四级优先级

uri 的取值表达式(1391-1398 行)实现的优先级:

1. VALKEY_URL 或 REDIS_URL      —— 显式连接串(最高)
2. IN_DOCKER=1                  —— 强制 redis://host.docker.internal:6379
3. AUTO_DETECT_DOCKER=1         —— 探测 /.dockerenv 后同上
4. 缺省                          —— redis://CHANGEME@127.0.0.1:6379

格式 redis://[:password@]host[:port]/[db],兼容 Redis、Valkey 及一切 Redis 协议服务。两个易踩点:其一,缺省密码占位符是 CHANGEME——不改密码也不改 URI 时,应用与数据存储之间是明文无鉴权连接;其二,AUTO_DETECT_DOCKER 靠根目录是否存在 /.dockerenv 判断环境,非官方预设环境别开。

9.2 require_tls 与分库

require_tls(VALKEY_REQUIRE_TLS 或 REDIS_REQUIRE_TLS 任一为 true 即生效,Since unreleased):开启后启动时 URI 必须是 rediss:///valkeys://,否则拒绝启动;默认 false 时按主机形态分级报告——公网域名/公网 IP 打 WARNING(口令、会话、秘密将明文过网),单标签主机名或私网/CGNAT/链路本地 IP 只打 INFO(所以默认组网不刷屏),回环地址不吭声。

dbs 五个逻辑库(custom_domain/customer/metadata/secret/feedback,VALKEY_DBS_* 或 REDIS_DBS_*):默认全部 0——注释明说是为了简化连接池、兼容 Redis-as-a-Service(很多托管服务只给一个库);要隔离时按需拆 0-15。

十、邮件体系:emailer + email_providers + mail.truemail

10.1 emailer(发送通道)

参数环境变量默认值说明
emailer.modeEMAILER_MODEsmtp发送模式;本地调试注释里推荐 Mailpit(SMTP 1025 端口 + Web UI 8025)
emailer.sender_providerCUSTOM_MAIL_PROVIDER回落 modeSince v0.25.8。自定义发件域供应(DNS/DKIM)与发送通道解耦,可选 ses/sendgrid/lettermint/smtp2go/smtp
emailer.fromFROM_EMAIL / FROMCHANGEME@example.com发件地址,生产必改
emailer.from_nameFROM_NAMESupport发件人显示名(Since v0.24.0)
emailer.reply_toREPLYTO_EMAIL回落 FROM_EMAIL回信地址(Since v0.24.0)
emailer.host/port/user/pass/auth/tls/sslSMTP_HOST/PORT/USERNAME/PASSWORD/AUTH/TLS/SSL587 端口标准 SMTP 三件套;pass 在模板里被双引号包裹——注释解释是防凭据里的 YAML 特殊字符炸解析
emailer.feedback_toFEEDBACK_TO_EMAILnilSince v0.25.2。/feedback 表单的收件地址,不设则发给库里第一个 colonel
emailer.show_logoEMAILER_SHOW_LOGOfalseSince v0.25.6。HTML 邮件里嵌 logo 图(收件人访问不到你的站点时别开,裂图比没图更减信)

email_providers 区块(1485 行起)是 SES/SendGrid/Lettermint 三家的发件域名 DNS 校验参数(DKIM/SPF 记录生成策略),每家都有合理默认,注释明言「不配也能工作」。

10.2 mail.truemail(收件地址校验)

注册/分享时对邮箱地址做格式校验的引擎,默认 default_validation_type: :regex(纯正则,不查 DNS 不连 SMTP);verifier_email(VERIFIER_EMAIL,默认 CHANGEME@example.com,:smtp 校验模式才必需);allowed_domains_only: false(true 时白名单外域名一律判无效);DNS 解析服务器默认 1.1.1.1 / 8.8.4.4 / 208.67.220.220(内网无外网 DNS 时这里会卡注册);smtp_fail_fast: false、smtp_safe_check: true。

实战关联两处:B1 篇排障实录的注册 422——脚本化注册参数名必须是 Rodauth 风格的 login 而不是 email,否则服务端拿到空邮箱,Truemail 报 Truemail regex validation for failed(failed 前空一格就是空地址);B1 篇常见问题里的「验证邮件发不出」——autoverify=false 时代码会尝试发信,SMTP 未配置或 Truemail DNS 不可达都会卡在注册流。

十一、后台任务与全局杂项

11.1 jobs(RabbitMQ 后台任务)

enabled(JOBS_ENABLED,默认 false):关闭时邮件发送回落为同步模式(fallback_to_sync,JOBS_FALLBACK_SYNC,默认 true)——小规模自托管不装 RabbitMQ 是官方支持的形态;rabbitmq_url(RABBITMQ_URL,默认 amqp://guest:guest@localhost:5672/dev)、channel_pool_size(默认 5)。workers/scheduler 下挂八类作业微调(plan_cache_refresh、catalog_retry、dlq_consumer、domain_refresh、expiration_warnings、favicon_fetch、favicon_backfill、maintenance),默认值即可,不用碰。

11.2 internationalization

enabled(I18N_ENABLED,默认 false)、default_locale(I18N_DEFAULT_LOCALE,en)、date_format/datetime_format(I18N_DATE_FORMAT/DATETIME_FORMAT,Since v0.24.6,预设 locale/iso8601/us/eu/eu-dot/uk/long 或直接给 date-fns 格式串)、fallback_locale 回落链(如 fr: [fr_FR, fr_CA, en])、locales 候选列表 30 项(ar/bg/ca_ES/…/zh)。

实测三:官方镜像实际可用的语言。调 /api/v2/supported-locales:

$ curl -s http://192.168.146.131:8132/api/v2/supported-locales \
    -u "admin@ots.local:<apitoken>"
{"success":true,"locales":["en"],"default_locale":"en","locale":"en"}

配置里列了 30 种语言,实际返回只有 en——前端资产按需构建,官方主分发物只带英文。想要中文界面需要自行构建前端资产(源码部署路径可行,B1 篇的前端构建步骤正是入口);纯 API 使用则完全无感。

11.3 diagnostics / development / compatibility / experimental

  • diagnostics.enabled(DIAGNOSTICS_ENABLED,默认 false)+ sentry 子块:错误上报,自托管默认关。
  • development.enabled 由 RACK_ENV 决定(仅 dev/development 为 true);debug(ONETIME_DEBUG);frontend_host(FRONTEND_HOST,Vite 开发服务器地址)。两个开发专用危险开关要认清:allow_nil_global_secret(ALLOW_NIL_GLOBAL_SECRET,Since v0.24.0——无 site.secret 也能启动,注释明言此时秘密加密形同虚设,仅限事故恢复/迁移临时用,生产关闭 development 时强制 false);devbasicauth/devsessionauth(DEV_BASIC_AUTH/DEV_SESSION_AUTH,Since v0.24.6——自动创建 dev_ 前缀的 20 小时短命测试用户,生产环境注册即抛 SecurityError)。
  • compatibility.deprecated_config_mode(DEPRECATED_CONFIG_MODE,默认 strict,Since v0.25.5):本文开头讲过的弃用键策略。
  • experimental: {}(Since v0.26.0):当前为空,注释说明是给未来特性开关留的扩展位(上一任住户「Colonel 控制台切换」已随新控制台转正而退役)。

十二、最小生产配置清单

综合 defaults 注释里的安全建议与两篇部署实测,生产环境的最小覆盖集:

# .env / 环境变量(生产必设项)
HOST=ots.example.com            # 对外域名
SSL=true                        # TLS 反代终结后仍写 true(链接与 Cookie 策略都靠它)
SECRET=<openssl rand -hex 32>   # 全局加密密钥,生成后离线备份、永不再改
SECRET_VERIFIER_MODE=enforce    # 密钥漂移改为拒绝启动(v0.26.0+)
SESSION_SECRET=<另一随机串>      # 会话签名与全局密钥分离
FROM_EMAIL=ots@example.com      # 真实发件地址
SMTP_HOST/PORT/USERNAME/PASSWORD=…   # 可用 SMTP 通道
VALKEY_URL=redis://<强密码>@<数据存储主机>:6379/0
TTL_MAX_ANONYMOUS=604800        # 匿名分享上限,按需收紧(默认 7 天)

内网纯 HTTP 测试部署额外加 SESSION_COOKIE_SECURE=false(否则登录态保持不住,见 B2 排障第一坑);想关注册收邀请制就 AUTH_SIGNUP=false;纯内网不想收验证邮件就 AUTH_AUTOVERIFY=true。

十三、排障交叉引用

三处配置相关的实测排障已在本专栏沉淀,本文不再重复:

  1. 会话 Cookie Secure 与纯 HTTP 部署(session.secure 三态逻辑)——《OneTimeSecret v0.26.19 Docker Compose 部署指南》排障实录第一坑;
  2. 注册接口参数名与 Truemail 空地址报错(mail.truemail 校验)——同篇排障实录第二坑;
  3. 匿名 TTL 静默钳制(ttl_max_anonymous)——同篇排障实录第四坑,本文第三节给出了对照实测(登录用户不受钳制)。

源码部署侧的 SMTP/验证邮件依赖见《Debian 12 安装 OneTimeSecret v0.26.19 指南》常见问题一节。

十四、参考链接