-
Notifications
You must be signed in to change notification settings - Fork 0
Operations zh
English · Tiếng Việt · 中文
安装之后如何运行 SkimMail:保护数据、看清它在做什么、更新它,以及理解它在磁盘 上主动做了哪些事。
这一页提到的每个界面都是已发布版本里真实存在的。凡是还没有界面的地方,这 一页会直说,并给出对应的命令。
只有一个目录,DATA_DIR —— apt 包是 /var/lib/skimmail:
| 路径 | 是什么 |
|---|---|
skimmail.db |
数据库:账户、邮件头、规则、设置 |
blobs/ |
缓存的邮件正文(见邮件正文缓存) |
master.key |
加密所有已存账户密码的密钥 |
plugins/ |
按需下载的运行时插件 |
丢失 master.key 无法挽回。 数据库里每一个账户密码都用它加密,从 1.11.0
起缓存的邮件正文也是。没有密钥的数据库不是「功能降级」,而是根本读不出来。
任何值得保留的备份都必须同时包含两者;只备份 skimmail.db 几乎没有意义。
反过来也成立,而这正是人们清理时最容易做错的地方:删掉 master.key 却留着数
据库,是三种可能状态里最糟的一种。要么整个 DATA_DIR 都删,要么一个都别
删。
Settings ▸ Backup,仅所有者。完整指南——归档里有什么、加密与密码规则、按 计划自动备份、如何恢复,以及在真正需要之前如何验证一次恢复——单独成页: 备份与恢复。
这里值得重复一句,因为它解释了上面那张表:归档把 master.key 作为自己内部的
一个独立条目带走。这正是丢失这个文件不是「从备份恢复」类问题、而更像丢失一行
数据库记录的原因——缺密钥的数据库和缺密钥条目的归档,会以同一种方式失效。
Settings ▸ Logs。 日志级别(debug / info / warn / error)是一个
实时开关:改完立即生效,无需重启,并且会覆盖 LOG_LEVEL(见配置)
设定的启动默认值。
实际查看日志有三种方式:
-
应用内实时查看器,就在同一个界面上——通过 WebSocket 的实时流,打开时 回填最近约 400 条缓冲记录,可按级别过滤、按文字自由搜索,并有暂停/继续,让 繁忙的实例不会在你读到之前就把某一行滚走。不需要主机的 shell 访问权限。
-
轮转文件,当设置了
LOG_FILE时(apt 包将其设为/var/log/skimmail/skimmail.log)。路径本身是仅限环境变量的——UI 或 API 都无法改变它,因此运行在 SkimMail 内部的任何东西都不可能被诱导写入任意文 件。UI 能在运行时改变的是:是否写入文件、轮转的大小 (LOG_MAX_SIZE_MB,1–1024 MB,默认 10),以及保留多少个轮转副本 (LOG_MAX_BACKUPS,0–20,默认 3)。一条用量条显示当前文件有多满; Download 把文件流式传给浏览器,Truncate 在确认后清空它。 -
journal,无论是否有文件 sink 都始终开启:
sudo journalctl -u skimmail -f记得加
sudo。没有它你会看到空的或不完整的 journal,而不是错误提示—— 读起来像「什么都没发生」,实际上是你没有权限看。
格式(text 或 json,LOG_FORMAT)是仅限环境变量、UI 中只读的——级别和轮
转是仅有的两个带实时开关的旋钮。
权限说明。 这个标签页本身在 operator 及以上级别就能打开,查看实时查
看器是 operator 级别的操作。这里的其余一切——读取或修改级别与轮转设置、下载
文件、清空文件——在 API 里都仅限所有者
(GET/PUT /api/settings/logs、GET /api/logs/download、
POST /api/logs/truncate)。因为这个界面要先加载完设置才会渲染其他任何内
容,今天一个 operator 打开 Settings ▸ Logs 看到的会是一个永远加载不完
的界面,而不是一个隐藏了设置卡片的查看器——在围绕这个标签页分配角色之前值得
知道这一点。见用户与角色。
Settings ▸ Updates,仅所有者。SkimMail 能检查自己的发布源(默认是
GitHub Releases;可用 UPDATE_FEED_URL 覆盖为自托管或隔离网络的镜像——见
配置),并且在某些部署方式下能自行安装更新。
检查是可选启用的,默认关闭——在你打开它或点击 Check now 之前,不会有 任何请求发往 GitHub。打开自动检查后,它会按一个周期轮询(默认 24 小时,可 调整),从启动后两分钟开始,这样一次重启永远不会引发意外的「打电话回家」。 比当前运行版本新的发布会显示发行说明和版本徽章;Skip 会记录下那个版本, 让它不再被提示。
这个按钮能不能真正安装,取决于你的部署方式:
| 部署方式 | SkimMail 报告的结果 | Update 按钮会做什么 |
|---|---|---|
| 预构建的二进制 tarball,解压到服务用户自己拥有的目录 | self |
下载、验证、原地替换并自行重启 |
| 容器(Docker / containerd / Kubernetes) | managed |
拒绝;改为拉取并运行新镜像 |
| apt 包 | managed |
同样拒绝——附带的 systemd unit 里的 ProtectSystem=strict 让 /usr/bin 对 skimmail 服务用户只读,即使不在容器里,二进制也无法改写自己 |
检测是自动的(SkimMail 会寻找 /.dockerenv 和容器 cgroup,再尝试在自己可执
行文件旁创建一个临时文件)——没有设置可以覆盖这个判断。既然 apt 路径也是
managed,就照常方式更新:
sudo apt update && sudo apt install --only-upgrade skimmail
发布后马上出现 "Unable to locate package",几乎总是本地索引过期,而不是发布
失败。先跑 sudo apt update。
当按钮真的能运行时——部署方式是 self、有针对你平台发布的发布资产、并
且这个构建带有发布验证密钥(每一个官方 apt/Docker/tarball 构建都有)——
Update now 会下载发布资产以及 SHA256SUMS 和它的 .minisig 签名;用官方
构建里内置的固定密钥验证签名;用已验证的校验和列表核对资产的 SHA256;解压
二进制;把当前的二进制改名为 <binary>.old;把新的改名替换进去;然后
re-exec 进入新进程——进程会替换自己,所以没有单独的「重启」步骤。如果替换
成功但 re-exec 本身失败了(少见,取决于平台),新的二进制其实已经装好了,
只需手动重启一次服务即可完成。
不带验证密钥构建的二进制只会停留在通知级别。 它会告诉你有更新,然后拒绝 安装,而不是安装一个自己无法校验的东西——同一把密钥也支撑着下面「插件」一节 里的 manifest 签名。
回滚是手动的,而且只有 self 部署才可能。 apply 这一步在成功时从不会
删除 <binary>.old,所以如果新版本出了问题,你可以停止服务、把 .old 改回
当前的二进制名字,再重启。这里没有按钮,也没有针对失败更新的自动检测——决定
和恢复都是你自己的事。
自动安装更新(嵌套在检查开关下面,且只有那个开关也打开时才有作用)会在一
次检查发现可自更新的发布时立即应用它。在 managed 部署上,这个开关没有任何
可以触发的对象,因为它想触发的更新会以按钮同样的方式被拒绝;UI 会在开关旁边
说明这一点。
Settings ▸ Plugins,仅所有者。出口和远程访问相关功能以运行时插件的形式 提供,而不是编译进基础二进制,这样一个只读邮件的安装能保持精简。今天存在三 个插件:
| 插件 | 类型 | 服务于 |
|---|---|---|
cloudflared |
external(Cloudflare 官方二进制,已固定) | 远程访问 —— Cloudflare Tunnel |
skimmail-tunnel-ts |
SkimMail 自行构建 | 远程访问 —— Tailscale |
skimmail-egress-wg |
SkimMail 自行构建 | Connections 上的内嵌 WireGuard 引擎 |
它们位于 DATA_DIR/plugins/(见上面的「所有东西放在哪里」),以服务器自身
的权限作为子进程运行,并且每次被 spawn 时都会重新验证——不只是安装时。
Install 为你的 OS/架构预取已固定的构件,做 SHA256 校验,并显示下载进度 条;此时还没有任何东西在运行,所以不需要确认。Update 把已安装的插件对 账到 manifest 当前固定的版本——如果它正支撑着某个正在运行的东西(一条正在 运行的隧道、一个启用中的内嵌 WireGuard 出口),确认框会点名短暂中断的是什 么,随后它先停止再重新拉起。Uninstall 直接删除文件,同样先点名影响。内 嵌 WireGuard 引擎还有一个在插件存在于磁盘之前使用的进程内「builtin」构建; 它在这个界面里既不可安装也不可移除,因为根本没有文件可以操作。
在 API 里读取清单是 operator 级别的路由,但整个 Settings ▸ Plugins 标 签页在 UI 里要求所有者,所以实际上低于所有者的角色完全无法通过应用触达这个 界面。
哪些插件可以安装、以及各是什么版本,来自 plugins.json,发布在与 apt 仓库
相同的 GitHub Pages 源上,和自更新用的是同一个信任根。在带有发布验证密钥
的构建上(自 1.10.0 起,且官方构建都是如此),manifest 自身的 .minisig
签名会在其中任何内容被信任之前先被校验,缺少签名会被直接拒绝——一个固定的
哈希值,其可信度不会超过承载它的那份文档,删掉一个文件不能成为关掉这项校验
的办法。没有验证密钥的构建(自定义构建且未设置 UPDATE_PUBKEY)仍然对每个
构件强制执行 SHA256 固定,但会记录一条一次性警告,说明这个固定值本身未经
验证。
Settings ▸ Sync。 每个账户的状态、最近一次错误,以及连续失败次数。反复失败 的账户会被自动停止,而不是无限重试;这块面板就是你看到并重新启用它的地方。
这个停止是有意为之,其他功能都尊重它。例如后台正文预取会完全跳过被自动停止的账 户,并且绝不把自己的失败计入那个计数器——可选的后台工作不应该有能力停掉一个 本来健康的邮箱。
完整模型——面板的三种状态、自动停止的阈值及其取值范围,以及哪些同步旋钮需要 重启才能生效——单独成页:同步与同步健康。
缓存的邮件正文现在有了生命周期。完整说明见邮件正文缓存; 从运维角度看,有三件事会自动发生:
1.11.0 首次启动时的一次对账。 它为早期版本缓存的正文建立索引,并在文件系统 存储上删除它们遗留的孤儿对象。你只会看到一次:
reconciled body cache store=fs indexed=1843 bytes=284127744 extra=12 deleted=12
只有一次。它会记录自己已经跑过,所以重启不会重复。
在 S3 上它只统计然后停下。 日志行以 needs_purge=true 结尾,什么都不会被
删除,因为那个存储桶可能同时存放着你的备份归档,而 SkimMail 不会去猜。删除是由
你来决定的:
Settings ▸ Security ▸ 邮件正文缓存 ▸ 清理无归属对象,或者同一件事的 API 形式:
curl -sS -b cookies -X POST http://localhost:8080/api/settings/cache/purge
两条路都仅限所有者。按钮是 1.11.1 才有的;在 1.11.0 上 API 是唯一的路,因为 那时面板被放在一个隐藏的设置标签页里。
持续清理。 超过 CACHE_MAX_SIZE_MB 时最久未读的正文先被清除;超过
BODY_TTL_DAYS 则按年龄过期。环境变量提供初始值(见配置);
从 1.11.1 起,这两个限制可以在 Settings ▸ Security 里修改,在那里保存的
值从此胜过环境变量。
apt remove 会停止并注销服务,并原封不动地保留 DATA_DIR——包括主密钥。
apt purge 也一样:没有 postremove 脚本,因此不存在任何自动删除你邮件的东西。
彻底清除是手动的,且不可撤销:
sudo rm -rf /var/lib/skimmail
sudo userdel skimmail # 可选:服务账户
不删除就重新安装,会从你离开的地方原样继续——不需要重新下载任何东西。
参考 — 首页 · 配置 · 安全 · 故障排查 · 常见问题
SkimMail · skimmail@base101.app · 2026-09-15 · commit dffbb18