Skip to content

Configuration zh

SkimMail docs edited this page Sep 18, 2026 · 8 revisions

English · Tiếng Việt · 中文

Configuration

1.9.0 开始,SkimMail 的配置被拆成了三个"面"(plane)——三个不同的地 方,一个设置项可以存放在其中之一,各自的生命周期和修改方式都不一样。搞清 楚一个设置属于哪个 plane,就能准确知道该去哪里改它、改动什么时候生效。最 重要的一条规则只有一句话:你在环境变量里写的东西永远赢。

三个 plane

Plane 0 —— 引导配置(Bootstrap)

恰好五个 key:DATA_DIRDB_DRIVERDATABASE_URLKEY_PROVIDERLISTEN_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=postgresDATABASE_URL=postgres://… 写进 /var/lib/skimmail/bootstrap.env,然后 重启进程切换过去。之后如果你又把 DATABASE_URL=… 直接加进 /etc/default/skimmail,环境变量这时就赢了——bootstrap.env 里的值从那 一刻起被忽略,尽管它还原封不动地留在文件里。

Plane 1 —— 实例配置(Instance configuration)

这些设置决定了这台 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 …——都会收到同一条拒绝信息。

Plane 2 —— 应用设置(App settings)

针对具体功能、可在运行时调整的旋钮:空闲屏幕锁定的等待时间和会话有效期 (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 做了什么。

Plane 0 —— 引导配置

变量 作用 默认值
DATA_DIR 数据库、blob 和主密钥存放的位置 ./data
DB_DRIVER sqlite | postgres | mysql sqlite
DATABASE_URL DB_DRIVER 不是 sqlite 时使用的连接字符串 (空)
KEY_PROVIDER file(主密钥明文存盘)| passphrase(已包裹) file
LISTEN_ADDR HTTP 服务器监听的地址 :8080

Plane 1 —— 实例配置(环境变量会锁定字段)

变量 作用 默认值
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)

Plane 2 —— 应用设置(环境变量只是初始默认值)

变量 作用 默认值
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_MBBODY_TTL_DAYS 属于 Plane 2:环境变量提供 初始默认值,而你在 Settings ▸ Security ▸ 邮件正文缓存 里保存的值——或 通过 PUT /api/settings/cache 保存的值——会写入数据库并从此胜出。请注意这与 Plane 1 的方向相反:在 Plane 1 里设了变量就等于锁死输入框,而这里环境变量让位 于已保存的值,所以那个面板不会显示任何"由环境变量接管"的提示。

该面板自 1.11.1 起存在。在 1.11.0 中它已经写好,但被放在隐藏的 Storage 标签页里,任何安装都打不开它,那时环境变量确实是唯一的路。见 邮件正文缓存

仅在启动时读取,没有任何 UI 或 CLI 可以覆盖

变量 作用 默认值
MAX_CONNS_PER_ACCOUNT 每个账户的并发 IMAP 连接数(限制在 1–14 之间) 10
BLOB_ENCRYPT 对磁盘上缓存的邮件正文加密(AES-256-GCM,密钥由主密钥派生)。读取始终解密,因此关闭它不会使现有缓存失效。除非你明确关闭,否则始终开启: 只有 false0nooff 会关闭它(不区分大小写,忽略首尾空格)。其它任何值——1TRUEyeson,或者一个拼写错误——都让加密保持开启 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

Shell 里的应急出口:skimmail config

浏览器里 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_DRIVERDATABASE_URLKEY_PROVIDERLISTEN_ADDR,不会更多(DATA_DIR 永远不可能在其中: 它本身就是这个文件所在的位置)——而 Plane 1 永远不会在数据库之外再拥有 一个属于自己的文件。让同一个设置有两个都能独立写入的"家"(一个文件 一行数据库记录,两者都自称是权威来源),正是另一个自托管项目落到"管理员 界面悄悄没法保存修改,因为一个 UI 根本不知道的配置文件每次都在默默获胜" 这种境地的原因。SkimMail 的规则之所以能保持简单,是因为它只需要在一个方 向上成立:环境变量赢过一切,除此之外的每样东西都只活在唯一一个地方。


SkimMail · skimmail@base101.app · 2026-09-18 · commit f525934

Clone this wiki locally