-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration zh
English · Tiếng Việt · 中文
从 1.9.0 开始,SkimMail 的配置被拆成了三个"面"(plane)——三个不同的地 方,一个设置项可以存放在其中之一,各自的生命周期和修改方式都不一样。搞清 楚一个设置属于哪个 plane,就能准确知道该去哪里改它、改动什么时候生效。最 重要的一条规则只有一句话:你在环境变量里写的东西永远赢。
恰好五个 key:DATA_DIR、DB_DRIVER、DATABASE_URL、KEY_PROVIDER、
LISTEN_ADDR。它们的存在只为了回答一个问题——数据库在哪里,怎么打开它?
——而这个问题必须先有答案,之后才谈得上有数据库去存别的东西。
-
DATA_DIR只能通过环境变量设置,而且是永久性的。它不能存在别的地 方,因为它本身就是下面那个文件(以及其他一切)所在目录的名字。 - 另外四个 key 既可以作为环境变量设置,也可以写进
<DATA_DIR>/bootstrap.env——一个由 SkimMail 自己管理的小文件,可以通过 首次运行的安装向导里选数据库那一步来写入,也可以用skimmail config set来写。这个文件存在的目的很明确:让一个用一行curl | sh装好、又 想用 Postgres 的人,有地方可以填 DSN,而不用去手动改一个 systemd 的EnvironmentFile。 - 优先级:环境变量赢过文件,文件赢过内置默认值。
- 这里的改动要等到下一次重启才生效——这里的任何设置都不会热加载,因 为它决定的是一开始要打开哪一个数据库进程。
举个例子。 一个全新的 apt 安装,/etc/default/skimmail 里没设置
DATABASE_URL,会用内置默认值——运行在
/var/lib/skimmail/skimmail.db 这个 SQLite 文件上。如果在首次安装向导里
选择连接一个 Postgres 实例,就会把 DB_DRIVER=postgres 和
DATABASE_URL=postgres://… 写进 /var/lib/skimmail/bootstrap.env,然后
重启进程切换过去。之后如果你又把 DATABASE_URL=… 直接加进
/etc/default/skimmail,环境变量这时就赢了——bootstrap.env 里的值从那
一刻起被忽略,尽管它还原封不动地留在文件里。
这些设置决定了这台 SkimMail 在网络上以什么样子出现:公开 URL、base
path、要不要信任一个反向代理、额外的 CORS/CSRF 来源、登录方式,以及
Google/Microsoft 的 OAuth 应用凭据。在 1.9.0 之前,这些全都是环境变量,只
能靠改文件再重启才能改动;现在它们存在数据库里,可以在浏览器里的
Settings 中修改,也可以在 shell 里用 skimmail config 修改。
| 设置项 |
skimmail config 里的 key |
环境变量 |
|---|---|---|
| 公开 URL | base_url |
BASE_URL |
| Base path(子路径部署) | base_path |
BASE_PATH |
| 信任一个反向代理 | trust_proxy |
TRUST_PROXY |
| 额外的 CORS/CSRF 来源 | trusted_origins |
TRUSTED_ORIGINS |
| 登录方式 | auth.mode |
AUTH_MODE |
| Google OAuth client ID / secret |
oauth.google.client_id / oauth.google.client_secret
|
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET
|
| Microsoft OAuth client ID / secret |
oauth.microsoft.client_id / oauth.microsoft.client_secret
|
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET
|
- 优先级:环境变量永远赢,而且是绝对的。 一个由环境变量提供的字段,
浏览器里会显示为**锁定(locked)**状态,并明确指出要移除哪个变量才能
改成从应用里管理。对一个锁定字段的写入——不管是从浏览器还是从
skimmail config set——都会被拒绝,而不是被悄悄覆盖。 - 改动立即生效——不需要重启。服务器把解析好的配置放在一个指针后面, 每次保存都会切换这个指针,这就是为什么在 Settings 里改 OAuth 应用或公 开 URL 不用重启就能生效。
- OAuth client secret 用主密钥加密存储(见 Security),任
何读取都不会把它返回——API 和
skimmail config list只能报告某个 secret 是否已经设置。 - 登录方式还有一条额外规则:一旦已经存在凭据,浏览器就再也不能修改它了
——只有主机上的
skimmail config set auth.mode …才行,而且把它设为none还额外要求环境变量里有SKIMMAIL_ALLOW_AUTH_NONE=1。被盗用的 管理员会话没法单靠自己关掉身份验证。
举个例子。 你因为要用声明式的方式管理这台实例,在
/etc/default/skimmail 里设置了 BASE_URL=https://mail.example.com。
Settings ▸(安装/关于)现在会把公开 URL 显示为锁定状态,并指出
BASE_URL 就是那个如果想改成从浏览器管理就需要移除的变量。在你把它从那
里移除并重启之前,任何修改尝试——不管是浏览器还是
skimmail config set base_url …——都会收到同一条拒绝信息。
针对具体功能、可在运行时调整的旋钮:空闲屏幕锁定的等待时间和会话有效期 (Security)、同步的并发数/速率/深度以及 IDLE/轮询行为(Sync)、日志级别 和文件轮转(Logs)。这些设置的历史比 1.9.0 的配置改造更早,遵循一条更 简单、单向的规则:
- 环境变量只在第一次——也就是这个设置从来没被保存过之前——提供默认 值。
- 一旦你从对应的 Settings 标签页保存了一次修改,保存下来的值就从此赢 了——包括之后每一次重启——不管环境变量是否还在设置着。这里没有"锁 定"提示,因为根本没有什么可锁:一旦保存过,这个 key 对应的环境变量就 再也不会被参考了。
- 这些设置不在
skimmail config的范围内——那条命令只是 Plane 0 和 Plane 1 的应急出口。
举个例子。 AUTO_LOCK_MINUTES 没有设置,所以屏幕锁定默认是关闭的
(内置默认值是 0,永不锁定)。你在 Settings ▸ Security ▸ Timeouts 里
把它打开并设成 15 分钟。这个 15 现在存在数据库里,并且在之后每次重启
都生效。之后再在环境变量里设置 AUTO_LOCK_MINUTES=30 不会改变任何东西
——你保存的值已经赢了,并且会一直赢下去,直到你再从同一个 Settings 标签
页里改它。
有几个 key 看起来像是属于上面某个功能,但其实只在启动时读取一次,仅此而
已——既没有 Plane 1 那种锁定,也没有 Plane 2 那种覆盖,永远都没有:
TRUSTED_PROXIES(见 Security),以及
LOG_FILE/LOG_FORMAT(日志路径被特意设计成永远不能通过 API 设
置——否则就等于允许从浏览器任意写文件)。
"Plane" 一列说明保存下来的改动(如果有的话)存在哪里;描述列说明环境变量 对这个 plane 做了什么。
| 变量 | 作用 | 默认值 |
|---|---|---|
DATA_DIR |
数据库、blob 和主密钥存放的位置 | ./data |
DB_DRIVER |
sqlite | postgres | mysql
|
sqlite |
DATABASE_URL |
DB_DRIVER 不是 sqlite 时使用的连接字符串 |
(空) |
KEY_PROVIDER |
file(主密钥明文存盘)| passphrase(已包裹) |
file |
LISTEN_ADDR |
HTTP 服务器监听的地址 | :8080 |
| 变量 | 作用 | 默认值 |
|---|---|---|
BASE_URL |
公开 URL——OAuth 回调、WebSocket 来源、PWA manifest | (空) |
BASE_PATH |
把应用部署在某个子路径下,例如 /mail
|
/ |
AUTH_MODE |
passphrase | users | none
|
passphrase |
TRUST_PROXY |
是否信任来自已识别代理跳数的 X-Forwarded-*
|
false |
TRUSTED_ORIGINS |
额外允许的 CORS/CSRF 来源,用逗号分隔 | (空) |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET
|
你自己的 Google OAuth 应用,用于 Gmail 登录 | (空) |
MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET
|
你自己的 Azure 应用,用于 Outlook 登录 | (空) |
在每个 OAuth 应用上注册两个重定向 URI,不是一个:常规方式用
<BASE_URL>/api/oauth/callback,没有公网 HTTPS 地址的实例依赖的手工粘贴 方式用http://localhost:8642。两者都会在 Settings ▸ About ▸ Setup health ▸ OAuth 下显示并附复制按钮。第二个不 是一项设置,也没有对应的环境变量——没有任何东西监听它,它始终只是提供商 需要识别的一个字符串。见 Accounts 里"添加一个 Gmail 或 Outlook 账户(OAuth)"一节。在 Google 和 Microsoft 控制台上注册 应用本身的具体步骤,见 Register the OAuth application。
| 变量 | 作用 | 默认值 |
|---|---|---|
SKIMMAIL_ALLOW_AUTH_NONE |
要真正关闭身份验证,除了 auth.mode=none 之外还必须设置它 |
false(设为 1/true 启用) |
SKIMMAIL_SKIP_CLAIM |
跳过首次运行的 claim code 关卡(见 Security) | false |
TRUSTED_PROXIES |
作为 X-Forwarded-For 可信跳数的 CIDR 列表,用逗号分隔 |
(空 = 仅 loopback/private) |
| 变量 | 作用 | 默认值 |
|---|---|---|
AUTO_LOCK_MINUTES |
客户端自动锁定前的空闲分钟数;0 = 永不锁定 |
0 |
SESSION_TTL_HOURS |
登录会话的有效期;0 = 永不过期 |
720(30 天) |
BACKGROUND_POLL_INTERVAL |
每个账户的轮询兜底周期 | 5m |
MAX_CONCURRENT_SYNCS |
同时进行同步的邮箱数量 | 4 |
SYNC_RATE_PER_MIN |
每分钟最多开始的同步次数 | 10 |
SYNC_DEPTH_DAYS |
按天数计的邮件头同步窗口;0 = 全部历史 |
30 |
SYNC_MAX_RETRIES |
连续失败多少次后自动停止某账户的同步;0 = 永不自动停止 |
3 |
CACHE_MAX_SIZE_MB |
缓存的邮件正文总量超过此值时开始清理;0 = 不限制 |
2048 |
BODY_TTL_DAYS |
删除缓存超过此天数的邮件正文;0 = 永不过期 |
90 |
CLIENT_IDLE_GRACE |
没有客户端连接多久之后触发 idle-downscale | 30m |
IDLE_DOWNSCALE_WHEN_NO_CLIENT |
没有浏览器打开时把 IDLE 降级为轮询 | false |
MAX_IDLE_CONNECTIONS |
并发 IMAP IDLE 连接数上限;0 = 不限制 |
0 |
LOG_LEVEL |
debug | info | warn | error
|
info |
LOG_MAX_SIZE_MB |
日志文件轮转的大小阈值 | 10 |
LOG_MAX_BACKUPS |
保留的已轮转日志文件数量 | 3 |
严格来说,CACHE_MAX_SIZE_MB 和 BODY_TTL_DAYS 属于 Plane 2:环境变量提供
初始默认值,而你在 Settings ▸ Security ▸ 邮件正文缓存 里保存的值——或
通过 PUT /api/settings/cache 保存的值——会写入数据库并从此胜出。请注意这与
Plane 1 的方向相反:在 Plane 1 里设了变量就等于锁死输入框,而这里环境变量让位
于已保存的值,所以那个面板不会显示任何"由环境变量接管"的提示。
该面板自 1.11.1 起存在。在 1.11.0 中它已经写好,但被放在隐藏的 Storage 标签页里,任何安装都打不开它,那时环境变量确实是唯一的路。见 邮件正文缓存。
| 变量 | 作用 | 默认值 |
|---|---|---|
MAX_CONNS_PER_ACCOUNT |
每个账户的并发 IMAP 连接数(限制在 1–14 之间) | 10 |
BLOB_ENCRYPT |
对磁盘上缓存的邮件正文加密(AES-256-GCM,密钥由主密钥派生)。读取始终解密,因此关闭它不会使现有缓存失效。除非你明确关闭,否则始终开启: 只有 false、0、no 和 off 会关闭它(不区分大小写,忽略首尾空格)。其它任何值——1、TRUE、yes、on,或者一个拼写错误——都让加密保持开启
|
true |
LOG_FILE |
轮转日志文件的路径;为空则只输出到 stdout/journal | (空;apt 会设为 /var/log/skimmail/skimmail.log) |
LOG_FORMAT |
text(key=value 格式)| json
|
text |
FIREBASE_ENABLED / FIREBASE_CREDENTIALS
|
通过 Firebase(FCM)实现的移动端推送,可选;大多数自托管用户不需要这个——浏览器 Web Push 无需任何配置即可使用 |
false / (空)
|
VAPID_PUBLIC / VAPID_PRIVATE / VAPID_SUBJECT
|
覆盖自动生成的 Web Push 密钥对 | (空——会自动生成并保存一对密钥) |
UPDATE_FEED_URL |
覆盖自我更新使用的发行版源地址(用于内网镜像) | (空 = GitHub Releases) |
UPDATE_PUBKEY |
用于验证自更新和插件 manifest 签名的 minisign 公钥。官方构建已内置该密钥;只在自定义构建上才需要设置 | (自定义构建上为空 = 更新只停留在通知级别,插件 manifest 不做验签) |
REVOCATION_FEED_URL |
覆盖获取已吊销授权码列表的地址 | (空 = 默认源) |
SKIMMAIL_PLUGINS_URL |
覆盖获取 plugins.json(Remote access / WireGuard engine 插件目录)的地址 |
(空 = 默认的 GitHub Pages manifest) |
BLOB_ENCRYPT 是本表中唯一默认开启的布尔开关,也是唯一把无法识别的值当作
"开启"来处理的。这里其它每个开关都默认关闭——在那种情况下,读不懂一个值是无害
的方向;而默认开启的标志正好相反,所以它必须朝着"仍然加密"的方向失败。
如果你在运行 1.11.0,请检查这一项。 那个版本把值与
true这个词逐字 比较,于是BLOB_ENCRYPT=1、=yes、=on和=TRUE全部被读成关闭, 悄悄停止了对新正文的加密。1.11.1 已修复。在关闭期间缓存下来的正文不会 被就地重写;它们会一直保持未加密,直到离开缓存为止。在 Settings ▸ Security 里调低容量上限或保留天数即可把它们挤出去,再回来时就是加密的了。
AI 工具是唯一一块已经编译进二进制文件、但 Settings 标签页在 1.15.0 中仍处 于隐藏状态的功能——在对应标签页重新打开之前,它的环境变量被有意排除在这张表之 外。Storage 引擎 / S3 blob 的切换开关以前和它一起隐藏,自 1.14.0 起已可见, 相关设置现在就在 Settings ▸ Storage 里。上面那几个正文缓存变量更早以前也是同样 的处境;从 1.11.1 起它们的面板在 Settings ▸ Security。哪些功能被隐藏、为什么被 隐藏,见 Home。
浏览器里 Plane 1 能做的事,skimmail config 在主机上也能做——再加上一件
浏览器做不到的事:一旦已经存在凭据,只有它能改登录方式。
skimmail config list # 每个设置项、它的值,以及它来自哪里
skimmail config get base_url # 只打印一个值,方便写脚本
skimmail config set base_url https://mail.example.com
skimmail config unset base_url # 清除它
skimmail config export # 打印数据库里当前内容对应的环境变量块list 会把 Plane 1 的设置和 Plane 0 的引导配置 key 分成两张表打印(引导
配置的改动需要重启才生效;Plane 1 的改动不需要,命令本身也会这么提示)。
export 会跳过 secret——它们已经加密存储,读不出明文;命令会打印一行注
释提醒你自己重新粘贴。尝试 set 一个已经被环境变量占用的 key,会收到和
浏览器一样的"先去环境变量里移除它"的错误信息。
如果哪天你在浏览器里把自己锁在登录方式之外,这也是回来的路——一旦账户已
经存在,auth.mode 也只能在这里修改。
bootstrap.env 被刻意设计得很窄——只有 DB_DRIVER、DATABASE_URL、
KEY_PROVIDER 和 LISTEN_ADDR,不会更多(DATA_DIR 永远不可能在其中:
它本身就是这个文件所在的位置)——而 Plane 1 永远不会在数据库之外再拥有
一个属于自己的文件。让同一个设置有两个都能独立写入的"家"(一个文件和
一行数据库记录,两者都自称是权威来源),正是另一个自托管项目落到"管理员
界面悄悄没法保存修改,因为一个 UI 根本不知道的配置文件每次都在默默获胜"
这种境地的原因。SkimMail 的规则之所以能保持简单,是因为它只需要在一个方
向上成立:环境变量赢过一切,除此之外的每样东西都只活在唯一一个地方。
SkimMail · skimmail@base101.app · 2026-09-18 · commit f525934