Stalwart v0.16.26 配置参数详解
Stalwart v0.16 配置体系三层模型实测详解:config.json 引导配置、环境变量与 JMAP 管理对象(监听器/认证策略/账号/域名/DKIM)逐字段解读与生效规则汇总
Stalwart v0.16.26 配置参数详解
目标:以 v0.16.26 实测为基础,讲透 Stalwart 的配置体系——从只存"引导信息"的 config.json,到存于数据存储中的系统设置、监听器、认证策略、账号与域名、DKIM 签名等配置对象,逐段给出实测数据、字段逐项解说与生效规则。所有回显均在 Ubuntu 22.04 + Stalwart v0.16.26 实测通过,可直接对照自己的实例验证。
一、环境信息
本文沿用 《Ubuntu 22.04 安装 Stalwart v0.16.26 指南》完成初始化的同套实测环境,配置对象均已实际创建并验证。
1.1 基础环境
- 操作系统:Ubuntu 22.04
- Stalwart 版本:v0.16.26
- 部署方式:官方目录布局(配置
/etc/stalwart、数据/var/lib/stalwart、日志/var/log/stalwart),systemd 托管 - 服务器主机名:
mail.lab.example.com,默认域lab.example.com - 管理员:
admin@lab.example.com(密码q1w2e3r4);测试账号:demo@lab.example.com - 管理 API 入口:
http://127.0.0.1:8080/jmap(下文所有配置读写都经此端点)
1.2 配置体系总览
动任何参数前先建立整体认知。v0.16 的配置分为三层,各层的存放位置、变更工具、生效方式完全不同:
| 层 | 存放位置 | 内容 | 变更方式 |
|---|---|---|---|
| 引导层 | /etc/stalwart/config.json | 数据存储后端连接 + 向导写入的系统设置 | 手工编辑 / 向导写回,重启生效 |
| 环境层 | /etc/stalwart/stalwart.env | 进程环境变量(如恢复管理员、代理) | 手工编辑,重启生效 |
| 对象层 | 数据存储内(RocksDB 等) | 监听器、账号、域名、DKIM、密码策略、队列…… | JMAP 管理 API / WebAdmin / stalwart-cli,部分需重启 |
三层之外的"运行态"(队列消息、日志、遥测)也在数据存储内,属运维对象而非配置对象。下文按层展开。
二、引导层:config.json 逐项详解
config.json 是 Stalwart 启动时读的第一个文件,决定"去哪里取其余全部配置"。看一份实测文件:
cat /etc/stalwart/config.json
实测输出(初始化完成后的完整内容,为便于阅读做了格式化):
{
"@type": "RocksDb",
"path": "/var/lib/stalwart/",
"blobSize": 16834,
"bufferSize": 134217728,
"poolWorkers": null,
"cacheSize": 134217728,
"server": {
"hostname": "mail.lab.example.com"
},
"session": {
"defaultDomain": "lab.example.com"
},
"settings": {
"mta": {
"directory": { "defaultDomain": "lab.example.com" },
"rcpt": { "domain": "lab.example.com" }
}
},
"tls": {
"acme": { "request": { "domains": [] } }
}
}
2.1 数据存储段(@type/path/blobSize/bufferSize/poolWorkers/cacheSize)
这部分决定数据的物理落点,是 config.json 的本职:
| 字段 | 实测值 | 解说 |
|---|---|---|
@type | RocksDb | 存储后端类型。可选还有 PostgreSql、MySql、Sqlite、FoundationDb、S3(对象存储,常作 Blob 层) |
path | /var/lib/stalwart/ | RocksDB 数据目录(含索引与 Blob 文件),结尾斜杠是官方生成风格 |
blobSize | 16834(约 16KB) | 内联阈值:小于该值的 Blob 直接写入键值对,大于的走独立文件对象,小附件读放大更少 |
bufferSize | 134217728(128MB) | RocksDB 写缓冲总大小,影响写吞吐与内存占用;小内存机器可降到 64MB |
poolWorkers | null | 存储线程池 worker 数,null 为按 CPU 核数自动 |
cacheSize | 134217728(128MB) | 存储缓存大小,命中缓存可减少磁盘读 |
若要切换到外部数据库(例如高可用场景用 PostgreSQL),把对象替换为对应后端字段即可,形如 {"@type":"PostgreSql","host":"db.internal","port":5432,"database":"stalwart","user":"stalwart","password":"...","poolMax":20}——字段名以官方文档为准,切换前务必用 --export 做全量备份(见 《Stalwart 日常运维手册》)。
2.2 向导写入的系统设置段(server/session/settings/tls)
server、session、settings、tls 四段是初始化向导(x:Bootstrap/set)写回的结果:
server.hostname:服务器对外主机名,影响 SMTP 的 Banner、TLS 证书匹配与队列日志标识;session.defaultDomain/settings.mta.directory.defaultDomain/settings.mta.rcpt.domain:三处共同把lab.example.com设为默认域——本地收件人解析的兜底域、登录名省略域名的补全域;tls.acme.request.domains:ACME 自动证书要申请的域名清单,数组为空即未启用(实测环境没有公网 DNS,故向导时显式关闭了证书申请)。
2.3 生效规则与编辑注意
config.json 是启动引导文件:手工修改后必须重启 stalwart 服务才生效(systemctl restart stalwart)。另外三点实操注意:
- 该文件由向导/系统写回,手工编辑前后都要备份(
cp config.json config.json.bak); - 文件权限应为
0640 root:stalwart——服务要以组权限读它,且向导流程需要向/etc/stalwart目录写回文件,目录权限给0750 root:stalwart(《Ubuntu 22.04 安装 Stalwart v0.16.26 指南》 5.4 节有权限不足的反面实测); - JSON 语法错误会让服务启动失败,改完先用
python3 -m json.tool /etc/stalwart/config.json校验。
三、环境层:stalwart.env
/etc/stalwart.env 由 systemd unit 的 EnvironmentFile=-/etc/stalwart/stalwart.env 加载,存放进程级环境变量。实测内容(初始化完成后清理了固定凭据):
cat /etc/stalwart/stalwart.env
实测输出:
# This file contains the environment variables for the Stalwart Mail Server.
常用变量一览(均为实测可用项):
| 变量 | 用途 | 备注 |
|---|---|---|
STALWART_RECOVERY_ADMIN | 引导模式/恢复模式的临时管理员凭据(user:pass) | 官方脚本默认生成随机值;初始化完成后建议删除 |
https_proxy / http_proxy | 出站代理(如 WebUI 资源包下载、外部 DNSBL 查询经代理) | 实测配置后 WebUI bundle 下载成功 |
改完重启生效:systemctl restart stalwart。
四、对象层:系统配置对象逐类详解
对象层是 v0.16 配置的主战场:一切以"类型化对象"存在数据存储中,用 JMAP 的 query/get/set 方法管理。方法是统一的:
x:<对象类型>/query —— 列出对象 ID
x:<对象类型>/get —— 读取属性
x:<对象类型>/set —— 创建(create)/更新(update)/删除(destroy)
(x: 前缀表示 Stalwart 私有扩展对象,标准 JMAP 对象如 Email/Mailbox 无前缀。)下面按配置面逐类讲解,全部示例都在实测环境验证过。
4.1 网络监听器(NetworkListener)
监听器决定"在哪些地址端口提供哪些协议"。先列出全部监听器:
curl -s http://127.0.0.1:8080/jmap -u "admin@lab.example.com:q1w2e3r4" \
-H "Content-Type: application/json" \
-d '{"using":["urn:ietf:params:jmap:core","urn:stalwart:jmap"],"methodCalls":[["x:NetworkListener/query",{"filter":null},"c0"],["x:NetworkListener/get",{"#ids":{"resultOf":"c0","name":"x:NetworkListener/query","path":"/ids"},"properties":null},"c1"]]}' \
| python3 -c "import json,sys;d=json.load(sys.stdin);[print(it['id'],'|',it['name'],'|',it['protocol'],'|',list(it['bind'].keys()),'| implicit:',it['tlsImplicit']) for it in d['methodResponses'][1][1]['list']]"
实测输出(9 个监听器,前 7 个为 v0.16 默认,后 2 个为手动补建):
jkc7rdqaaeqb | smtp | smtp | ['[::]:25'] | implicit: False
jkc7rdqaafab | submissions | smtp | ['[::]:465'] | implicit: True
jkc79uvmaaaa | submission | smtp | ['[::]:587'] | implicit: False
jkc7rdqaafqb | imaps | imap | ['[::]:993'] | implicit: True
jkc79uvmaaqa | imap | imap | ['[::]:143'] | implicit: False
jkc7rdqaagab | pop3s | pop3 | ['[::]:995'] | implicit: True
jkc7rdqaagqb | sieve | manageSieve| ['[::]:4190'] | implicit: False
jkc7rdqcahab | https | http | ['[::]:443'] | implicit: True
jkc7rdqcahqb | http | http | ['[::]:8080'] | implicit: False
逐字段解读一个实测对象(https 监听器,属性最多的一类):
| 字段 | 实测值 | 解说 |
|---|---|---|
name | https | 监听器名,日志与 WebAdmin 中显示 |
protocol | http | 协议处理器:smtp/imap/pop3/manageSieve/http |
bind | {"[::]:443": true} | 键为"地址:端口"(IPv6 通配同时覆盖 IPv4),值 true 启用 |
useTls | true | 该监听器启用 TLS 能力 |
tlsImplicit | true | 关键开关:true=隐式 TLS(连上即握手,465/993/995/443);false=明文起步、客户端 STARTTLS 升级(25/587/143) |
socketBacklog | 1024 | 内核 accept 队列长度 |
socketNoDelay | true | 禁用 Nagle,降低交互延迟(邮件服务合理默认) |
socketReuseAddress / socketReusePort | true | 地址/端口复用,重启不等待 TIME_WAIT |
maxConnections | 8192 | 该监听器最大并发连接 |
tlsTimeout | 60000 | TLS 握手超时(毫秒) |
tlsDisableProtocols / tlsDisableCipherSuites | {} | 要禁用的 TLS 协议版本/套件黑名单,默认全启用 |
创建/修改监听器的实测例子见 《Ubuntu 22.04 安装 Stalwart v0.16.26 指南》 3.7 节(587/143 补建)。生效规则:监听器变更必须重启服务(实测创建后 587/143 端口在重启后才出现)。
4.2 认证策略(Authentication)
认证策略是 singleton 对象(全局唯一),控制密码强度、失败锁定等。读取:
curl -s http://127.0.0.1:8080/jmap -u "admin@lab.example.com:q1w2e3r4" \
-H "Content-Type: application/json" \
-d '{"using":["urn:ietf:params:jmap:core","urn:stalwart:jmap"],"methodCalls":[["x:Authentication/get",{"ids":["singleton"]},"c0"]]}' \
| python3 -c "import json,sys;print(json.dumps(json.load(sys.stdin)['methodResponses'][0][1]['list'][0],indent=1))"
实测输出(节选核心字段):
{
"id": "singleton",
"passwordMinStrength": "zero",
"enableRateLimiting": true,
"rate": { "requests": 30, "period": "1m" },
"authAttempt": { "limit": 6, "delay": 3000, "expiresIn": 30000 },
...
}
关键字段解说:
passwordMinStrength:密码强度下限(实测已调为zero)。默认low,会拒绝q1w2e3r4这类弱口令(实测报secret is too weak);可选档位从松到严为zero/low/medium/high/extreme,生产环境建议至少medium;enableRateLimiting+rate:认证接口限流(实测默认每分钟 30 次);authAttempt:失败尝试惩罚(默认 6 次失败后逐步延迟、窗口 30 秒)。
生效规则实测:修改该对象返回成功,但运行中的服务策略不变,需重启才真正生效(《Ubuntu 22.04 安装 Stalwart v0.16.26 指南》 2.6 节的两段实测:改完不重启改密仍被拒,重启后成功)。同时它是受保护配置——初次修改时曾返回 passwordMinStrength was not honored due to insufficient privileges,重启服务后同一调用成功,遇到时按"改完重启"处理。
4.3 账号与凭据(Account / User)
账号体系核心是 User 对象(挂在主账号 Account 下),实测创建 demo@lab.example.com 的完整调用:
curl -s http://127.0.0.1:8080/jmap -u "admin@lab.example.com:q1w2e3r4" \
-H "Content-Type: application/json" \
-d '{"using":["urn:ietf:params:jmap:core","urn:stalwart:jmap"],"methodCalls":[["x:Account/set",{"create":{"demo":{"@type":"User","name":"demo","domainId":"b","credentials":{"0":{"@type":"Password","secret":"q1w2e3r4"}},"description":"Demo test account"}}},"c0"]]}' \
| python3 -c "import json,sys;print(json.dumps(json.load(sys.stdin)['methodResponses'][0][1],indent=1))"
实测输出:
{
"accountId": "b",
"newState": "48392017",
"created": {
"demo": { "id": "c" }
}
}
字段与结构解说:
@type: "User":显式声明对象类型(管理面对象的固定写法,缺省会报 unknownType);name:本地部分,与domainId指向的域组合成完整地址demo@lab.example.com;credentials:凭据字典,键为序号(0、1…),每项@type决定凭据类型——Password(口令)、Totp(两步验证)、AppPassword、OauthBearerToken等,一个用户可并存多把凭据;- 返回的
id(c)是后续 IMAP/JMAP 用的 accountId。
常用运维写法:
# 改密码(路径式更新:账号ID/credentials/序号/secret)
'{"update":{"b":{"credentials/0/secret":"新密码"}}}'
# 停用账号(注意 isEnabled 在域对象上,用户账号用 description 或直接删除)
'{"update":{"c":{"description":"disabled 2026-10-18"}}}'
4.4 域名对象(Domain)
域名是收发策略的载体。实测读取默认域(ID b):
curl -s http://127.0.0.1:8080/jmap -u "admin@lab.example.com:q1w2e3r4" \
-H "Content-Type: application/json" \
-d '{"using":["urn:ietf:params:jmap:core","urn:stalwart:jmap"],"methodCalls":[["x:Domain/get",{"ids":["b"],"properties":null},"c0"]]}' \
| python3 -c "import json,sys;print(json.dumps(json.load(sys.stdin)['methodResponses'][0][1]['list'][0],indent=1))"
实测输出(节选):
{
"name": "lab.example.com",
"isEnabled": true,
"catchAllAddress": null,
"subAddressing": { "@type": "Enabled" },
"allowRelaying": false,
"reportAddressUri": "mailto:postmaster",
"certificateManagement": { "@type": "Manual" },
"dkimManagement": {
"@type": "Automatic",
"algorithms": { "Dkim1Ed25519Sha256": true, "Dkim1RsaSha256": true },
"selectorTemplate": "v{version}-{algorithm}-{date-%Y%m%d}",
"rotateAfter": 7776000000,
"retireAfter": 604800000,
"deleteAfter": 2592000000
},
"dnsZoneFile": "..."
}
逐项解说:
isEnabled:域启用开关,关闭后该域收件直接拒绝;catchAllAddress:catch-all 目标(null=未启用),设为某账号 ID 后所有未知收件人都进它;subAddressing:子地址(user+tag@)扩展支持,Enabled时 user+tag 投给 user;allowRelaying:对内域必须保持 false——该域的邮件由本服务器收发,设 true 反而变开放转发风险;dkimManagement:DKIM 自动化管理,Automatic模式下按selectorTemplate(实测模板v{version}-{algorithm}-{date-%Y%m%d}生成v1-rsa-20261018、v1-ed25519-20261018两个选择器)自动建签名,rotateAfter/retireAfter/deleteAfter控制轮换/退役/删除周期(毫秒,实测 90 天轮换、7 天退役、30 天删除);dnsZoneFile:一键生成的完整 DNS zone 文本(MX/SPF/DKIM/DMARC/MTA-STS/TLSRPT/自动发现全包含),加域后从对象直接取走发布即可。
4.5 DKIM 签名(DkimSignature)
域名创建时自动生成两条签名(RSA + Ed25519 双算法,收件方按支持情况选择验证)。实测读取:
curl -s http://127.0.0.1:8080/jmap -u "admin@lab.example.com:q1w2e3r4" \
-H "Content-Type: application/json" \
-d '{"using":["urn:ietf:params:jmap:core","urn:stalwart:jmap"],"methodCalls":[["x:DkimSignature/query",{"filter":null},"c0"],["x:DkimSignature/get",{"#ids":{"resultOf":"c0","name":"x:DkimSignature/query","path":"/ids"},"properties":null},"c1"]]}' \
| python3 -c "import json,sys;d=json.load(sys.stdin);[print(json.dumps({k:(v if k!='publicKey' else v[:24]+'...') for k,v in it.items()},indent=1)) for it in d['methodResponses'][1][1]['list']]"
实测输出(两条签名的公共字段一致,仅算法与选择器不同;privateKey 服务端已脱敏为 **):
{
"@type": "Dkim1RsaSha256",
"selector": "v1-rsa-20261018",
"domainId": "b",
"stage": "active",
"canonicalization": "relaxed/relaxed",
"headers": { "From": true, "To": true, "Date": true, "Subject": true, "Message-ID": true },
"report": true,
"expire": null,
"auid": null,
"createdAt": "2026-10-18T09:16:57Z",
"nextTransitionAt": null,
"publicKey": "MIIBIjANBgkqhkiG9w0BAQ..."
}
{
"@type": "Dkim1Ed25519Sha256",
"selector": "v1-ed25519-20261018",
"domainId": "b",
"stage": "active",
...
}
字段解说:
@type:算法类型(Dkim1RsaSha256/Dkim1Ed25519Sha256),决定 DNS TXT 里k=与邮件头a=;selector:DNS 查询的<selector>._domainkey.<domain>前缀,发布 DNS 记录时要用;stage:生命周期阶段(active使用中;配合域名上的轮换策略还会出现new/retire等);canonicalization:头/正文的规范化算法,relaxed/relaxed为业界通用;headers:参与签名的信头清单(From/To/Date/Subject/Message-ID 覆盖防篡改关键面);report:请求收件方回传 DKIM 失败报告(对 DNS 记录追加r=y标记的声明)。
签名验证实测:向 demo 邮箱投递一封信后从 IMAP 取原始信头(实测见 《Ubuntu 22.04 安装 Stalwart v0.16.26 指南》 4.3 节):
DKIM-Signature: v=1; a=ed25519-sha256; d=lab.example.com; s=v1-ed25519-20261018; ...
DKIM-Signature: v=1; a=rsa-sha256; d=lab.example.com; s=v1-rsa-20261018; ...
两条签名同时存在且 d=/s= 与对象配置严格对应。
4.6 系统设置对象(SystemSettings 与向导对象)
除上述对象外,还有几类 singleton/列表对象承担全局设置,给出实测存在的对象类型与用途速查:
| 对象(x: 前缀) | 单例/列表 | 用途 | 实测确认 |
|---|---|---|---|
Bootstrap | 单例 | 初始化向导状态(完成后不可再用) | 3.x 节实测 |
SystemSettings | 单例 | 服务器级杂项设置 | 登录读取正常 |
Authentication | 单例 | 认证策略 | 4.2 节详解 |
NetworkListener | 列表 | 监听器 | 4.1 节详解 |
Account(User/Domain/Group…) | 列表 | 账号目录 | 4.3/4.4 节详解 |
DkimSignature | 列表 | DKIM 签名 | 4.5 节详解 |
QueuedMessage | 列表 | 外发队列消息 | 《Stalwart 日常运维手册》详解 |
Task | 列表 | 后台任务 | 《Stalwart 日常运维手册》详解 |
不确定某类对象是否存在/字段含义时,用 CLI 快速自省(stalwart-cli 1.0.13 实测):
export STALWART_URL=http://127.0.0.1:8080 STALWART_USER=admin@lab.example.com STALWART_PASSWORD=q1w2e3r4
stalwart-cli describe
实测输出(节选,对象类型清单):
Account, Alias, Authentication, Domain, DkimSignature, NetworkListener, QueuedMessage,
ReloadSettings, SpamdTrain, SystemSettings, Task, Tenant, ...
describe <类型> 可进一步看单类型的字段定义。
五、反垃圾与病毒过滤配置概览
Stalwart 内置垃圾过滤栈开箱即用(无需安装 Rspamd 也有基础能力),实测一封普通测试邮件到达收件箱时自动追加的评分头:
X-Spam-Status: No, score=0.40
X-Spam-Bar: +
X-Spam-Report: Spam detection software, running on the server, has identified this
incoming email as possible spam...
score/Bar:综合评分与可视化条,X-Spam-Status: No表示未达拦截阈值;- 评分来源包括启发式规则、DNSBL 实时黑名单查询、SPF/DKIM/DMARC 校验结果、发件方信誉等。
相关配置对象与扩展点:
- 外挂 Rspamd:如需贝叶斯学习、模糊哈希等更强检测,可把 Stalwart 的垃圾过滤处理器指向外部 Rspamd 服务(HTTP 接口对接),Stalwart 只负责调用与结果执行;
- 病毒扫描:同样走外置服务对接(如 ClamAV),命中后可配置拒绝/隔离/打标动作;
- 阈值与动作:评分阈值、命中后动作(打标/投递/拒绝)在管理面相应对象中调整,本篇不展开逐项实测。
一句话结论:默认行为已可用(实测评分头正常生成、正常邮件进收件箱),强化场景建议外挂 Rspamd,属"接入即得"的配置而非另起炉灶。
六、配置变更的生效规则总结
把全文实测的生效行为汇总成表,改配置前先查这张表:
| 配置项 | 变更工具 | 生效方式 | 实测依据 |
|---|---|---|---|
| config.json(存储/系统设置) | 手工编辑 | 重启服务 | 2.3 节 |
| stalwart.env(环境变量) | 手工编辑 | 重启服务 | 三章 |
| NetworkListener 监听器 | API/WebAdmin | 重启服务(端口实测重启后出现) | 4.1 节 |
| Authentication 密码策略 | API/WebAdmin | 重启服务(受保护配置,不重启不生效) | 4.2 节 |
| Account/User 凭据 | API/WebAdmin/CLI | 即时生效(实测改密后新密码直接可登录) | 安装指南 2.6 节 |
| Domain/DkimSignature | API/WebAdmin | 即时生效(新邮件按新签名配置处理) | 4.5 节 |
| 垃圾过滤/病毒对接 | API/WebAdmin | 即时生效 | 五章 |
通用的"温和重载"入口也存在(ReloadSettings 动作、stalwart-cli 对应命令),但对监听器/认证策略这类核心对象,实测结论是重启最可靠。
七、常见问题
7.1 网上教程里的 config.toml 在 v0.16 不生效
现象:按旧教程创建/编辑 /etc/stalwart/config.toml 后重启,配置毫无反应。
原因与处理:v0.16 起配置模型重构,TOML 主配置已被废除,配置在数据存储中通过管理 API 维护;文件系统的 config.json 只承载引导信息。处理方式:忘掉 config.toml,改用本篇四章的 API/WebAdmin/CLI 三件套;跨版本迁移参考官方 UPGRADING/v0_16 文档。
7.2 修改密码策略/监听器后"不生效"
现象:API 返回 updated 成功,但行为没变(弱口令仍被拒/新端口没监听)。
原因与处理:这两类对象运行时不会热加载(实测 Authentication 修改后必须重启才拦截弱口令;NetworkListener 重启后才监听新端口)。执行 systemctl restart stalwart 后验证:
systemctl restart stalwart && sleep 5 && ss -ltn | grep <端口>
7.3 配置 API 返回 insufficient privileges
现象:x:Authentication/set 等调用返回 ... was not honored due to insufficient privileges(实测回显见 4.2 节)。
原因与处理:受保护配置的变更被运行时拒收,多数情况是实例刚改过配置未重启、权限上下文未刷新。重启服务后用管理员凭据重试即可。
7.4 config.json 手工编辑后服务起不来
现象:systemctl restart stalwart 后服务 failed,日志报 JSON 解析错误。
处理:先用 python3 -m json.tool /etc/stalwart/config.json 校验语法;从备份恢复(cp /etc/stalwart/config.json.bak /etc/stalwart/config.json)。养成"改前备份、改后校验"的习惯即可规避。
7.5 对象 ID 与 accountId 混淆
现象:JMAP 调用里 accountId 传错导致 404/权限错误。
澄清(实测值):管理 API 的 accountId 是主账号 ID(本实测环境恒为 b,管理员所在账号);而 Email/query 等邮件对象用的 accountId 是用户自己的账号 ID(demo 用户为 c)。二者同名不同义,排错时先 x:Account/query 确认目标对象的 ID,再区分调用的是管理方法还是邮件方法。
八、参考链接
- Stalwart 配置总览文档:https://docs.stalw.art/config/overview
- 服务器设置参考:https://docs.stalw.art/config/server
- 存储后端配置(RocksDB/SQL/S3):https://docs.stalw.art/config/storage
- 监听器与 TLS 配置:https://docs.stalw.art/config/listeners
- 认证与密码策略:https://docs.stalw.art/config/authentication
- DKIM 管理文档:https://docs.stalw.art/config/dkim
- JMAP 协议规范 RFC 8620(query/get/set 语义):https://datatracker.ietf.org/doc/html/rfc8620
- v0.16 升级与配置模型变更说明:https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md