Jami 20260403.0 配置参数详解

承接安装篇部署好的无头 Jami 服务端,讲清 Jami 20260403.0 的两级配置体系(daemon 全局 `~/.config/jami/dring.yml` 与账户级 `~/.local/share/jami/<accountID>/config.yml`)与运行态参数(`getAccountDetails` 59 键、`getVolatileAccountDetails` 9 键),逐类解释 DHT 引导节点与代理、TURN、名字服务、TLS 证书等核心参数,并给出一条完整的「修改 → 生效 → 落盘 → 重启保持 → 回退」实测闭环。

JamiJami即时通讯VoIPP2P视频会议2026/10/26

Jami 20260403.0 配置参数详解

目标:承接安装篇部署好的无头 Jami 服务端,讲清 Jami 20260403.0 的两级配置体系(daemon 全局 ~/.config/jami/dring.yml 与账户级 ~/.local/share/jami/<accountID>/config.yml)与运行态参数(getAccountDetails 59 键、getVolatileAccountDetails 9 键),逐类解释 DHT 引导节点与代理、TURN、名字服务、TLS 证书等核心参数,并给出一条完整的「修改 → 生效 → 落盘 → 重启保持 → 回退」实测闭环。全部操作在 Ubuntu 22.04 服务器(主机名 jamisrv1)实测执行。

一、环境信息

项值来源
实验系统Ubuntu 22.04 服务器(主机名 jamisrv1,无图形界面)实测
软件版本jami-daemon 20260403.0~dfsg1-1(Jami Core 16.0.0,OpenDHT 3.7.8)jamid --version 实测
运行形态jamiserver 专用用户 + 私有会话 D-Bus + systemd 服务 jamiserver.service(安装篇部署结果)实测
实验账户accountID ff5bdfae563e6707,别名 headless-srv1,RingID 813cbf71078e4d16b0462ce1c694c54bc2f52979实测
实验日期2026-10-26实测
关联文章部署过程见《Ubuntu 22.04 安装 Jami 20260403.0 指南》;架构与选型见《Jami 20260403.0 介绍与选型指南》本专栏

Jami 是无中心服务器的分布式 P2P 平台,配置哲学与有中心节点的软件不同:绝大多数参数默认值即生产值,账户身份、密钥全部由本地 daemon 生成与保管,真正需要人工介入的只有网络类参数(引导节点、TURN、DHT 代理、名字服务)。本篇围绕这一主线展开。

二、前置条件

  1. 已按安装篇完成部署:jami-daemon 与 dbus-x11 已安装,jamiserver.service 处于 active (running)。
  2. 可用 jamiserver 身份连接其私有会话 D-Bus(总线地址文件 /tmp/env-jamiserver 由服务启动脚本生成):
sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager cx.ring.Ring.ConfigurationManager getAccountList ""'
as 1 "ff5bdfae563e6707"
  1. 权限提示:会话 D-Bus 属于创建它的用户(jamiserver),所有 busctl --user 命令必须以 sudo -iu jamiserver(或先 su - jamiserver)执行;root 直接连接会报 Transport endpoint is not connected。

三、参数查看与修改方式

3.1 参数的三个存放位置

Jami 的参数分三层,职责互不重叠:

层位置内容读写方
daemon 全局~/.config/jami/dring.yml音频/视频设备、提示音、历史记录、插件等与具体账户无关的全局偏好daemon 启动读取,客户端经 D-Bus 修改
账户级~/.local/share/jami/<accountID>/config.yml单个账户的全部持久化参数(只存非默认值)daemon 维护
运行态daemon 内存完整参数视图(59 键)+ 实时注册状态(9 键)getAccountDetails / getVolatileAccountDetails 只读

关键结论(实测确认):账户级 config.yml 只落盘与默认值不同的键。默认参数不出现在文件里,因此「文件比 API 返回的短」属正常现象;想知道全量默认值,用 getAccountDetails 看。

3.2 查看:API 优先于文件

# 完整账户参数(59 键)
sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager cx.ring.Ring.ConfigurationManager getAccountDetails s ff5bdfae563e6707'
a{ss} 59 "Account.accountDiscovery" "false" "Account.accountPublish" "false" "Account.activeCallLimit" "-1" "Account.alias" "headless-srv1" "Account.allModeratorEnabled" "true" "Account.allowCertFromContact" "true" "Account.allowCertFromHistory" "true" "Account.allowCertFromTrusted" "true" "Account.archiveHasPassword" "false" "Account.audioPortMax" "32766" "Account.audioPortMin" "16384" "Account.autoAnswer" "false" "Account.defaultModerators" "" "Account.denySecondCall" "false" "Account.deviceName" "jamisrv1" "Account.dhtPort" "0" "Account.dhtProxyListUrl" "https://config.jami.net/proxyList" "Account.dhtProxyServerEnabled" "false" "Account.dhtProxyServerPort" "42443" "Account.displayName" "headless-srv1" "Account.dtmfType" "overrtp" "Account.enable" "true" "Account.hostname" "bootstrap.jami.net" "Account.localInterface" "default" "Account.localModeratorsEnabled" "true" "Account.mailbox" "" "Account.managerUri" "" "Account.managerUsername" "" "Account.peerDiscovery" "false" "Account.presenceSubscribeSupported" "true" "Account.proxyEnabled" "false" "Account.proxyListEnabled" "true" "Account.proxyServer" "dhtproxy.jami.net:[80-95]" "Account.publishedAddress" "" "Account.publishedSameAsLocal" "true" "Account.rendezVous" "false" "Account.ringtoneEnabled" "true" "Account.ringtonePath" "default.opus" "Account.sendComposing" "true" "Account.sendReadReceipt" "true" "Account.type" "RING" "Account.uiCustomization" "" "Account.upnpEnabled" "true" "Account.useragent" "" "Account.username" "813cbf71078e4d16b0462ce1c694c54bc2f52979" "Account.videoEnabled" "true" "Account.videoPortMax" "65534" "Account.videoPortMin" "49152" "DHT.PublicInCalls" "true" "RingNS.uri" "" "TLS.certificateFile" "/home/jamiserver/.local/share/jami/ff5bdfae563e6707/ring_device.crt" "TLS.certificateListFile" "" "TLS.password" "" "TLS.privateKeyFile" "/home/jamiserver/.local/share/jami/ff5bdfae563e6707/ring_device.key" "TURN.enable" "true" "TURN.password" "ring" "TURN.realm" "ring" "TURN.server" "turn.jami.net" "TURN.username" "ring"
# 运行态注册状态(9 键,实时变化)
sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager cx.ring.Ring.ConfigurationManager getVolatileAccountDetails s ff5bdfae563e6707'
a{ss} 9 "Account.active" "true" "Account.deviceAnnounced" "true" "Account.deviceID" "d49eef0fb5e2a129fc0564c11c5c39bdb13675fe9f0e88c1553abfb4e3a1ab43" "Account.dhtBoundPort" "33089" "Account.proxyServer" "" "Account.registrationStatus" "REGISTERED" "IM.offCall" "true" "Transport.statusCode" "100" "Transport.statusDescription" ""

3.3 修改:setAccountDetails 实测全链路

修改账户参数的正规路径是 D-Bus setAccountDetails。busctl introspect 确认其签名为 sa{ss}(账户 ID 与键值映射是同一个签名整体):

.setAccountDetails                method    sa{ss}                          -            -

实测把 Account.autoAnswer 从 false 改为 true 并做全链路验证:

sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager cx.ring.Ring.ConfigurationManager setAccountDetails "sa{ss}" ff5bdfae563e6707 1 Account.autoAnswer true'

命令无输出返回(void 方法)。依次验证四件事:

# ① 运行态立即生效
$ getAccountDetails → "Account.autoAnswer" "true"

# ② 账户级文件落盘(此前该键不存在——默认值不落盘)
$ grep -n "autoAnswer" ~/.local/share/jami/ff5bdfae563e6707/config.yml
4:autoAnswer: true

# ③ 重启服务后仍保持
$ sudo systemctl restart jamiserver.service
$ systemctl is-active jamiserver.service
active
$ getAccountDetails → "Account.autoAnswer" "true"   # 重启后仍为 true

回退(改回 false)后该键从 config.yml 中消失(回归默认即不再落盘):

sudo -iu jamiserver bash -c 'source /tmp/env-jamiserver; busctl --user call cx.ring.Ring /cx/ring/Ring/ConfigurationManager cx.ring.Ring.ConfigurationManager setAccountDetails "sa{ss}" ff5bdfae563e6707 1 Account.autoAnswer false'
$ getAccountDetails → "Account.autoAnswer" "false"
$ grep -c "autoAnswer" ~/.local/share/jami/ff5bdfae563e6707/config.yml
0

两个实测发现:

  1. 签名必须整体传 sa{ss}。若把签名拆成 s … "a{ss}" … 两段传,busctl 会报 Too many parameters for signature(第一段 s 只消费账户 ID,后段键值对成了多余参数);参数再少一段则报 Too few parameters for signature。这是 busctl 调用 Jami 最常踩的坑。
  2. 每次 setAccountDetails 会触发账户重载与重新注册。实测修改后立即查询 volatile 状态,会看到 registrationStatus 短暂变为 TRYING,约 25 秒内回到 REGISTERED、deviceAnnounced 回到 true。批量修改时不必逐项等待,改完统一等它收敛即可。

3.4 直接编辑文件的边界

dring.yml 与账户 config.yml 是 daemon 写出的状态存储:账户级文件在每次 setAccountDetails、账户重载时由 daemon 回写。生产上应把 D-Bus API 当作唯一写入路径,避免与运行中的 daemon 产生状态竞争。若确需离线批量调整(如整目录迁移),先 systemctl stop jamiserver.service 再编辑,启动后用 getAccountDetails 逐项复核。

四、参数总览

4.1 账户运行参数 59 键分组

getAccountDetails 返回的 59 个键按用途分 9 组(实测值取自本环境,↔ 表示该项可在客户端图形界面「高级设置」中找到对应开关):

分组键实测值
身份与设备Account.alias / displayName / username / deviceName / type / enable / archiveHasPassword / useragent / mailboxheadless-srv1 / headless-srv1 / 813cbf71…52979 / jamisrv1 / RING / true / false / "" / ""
DHT 网络Account.hostname / dhtPort / localInterface / publishedAddress / publishedSameAsLocal / upnpEnabled / peerDiscovery / accountDiscovery / accountPublishbootstrap.jami.net / 0 / default / "" / true / true / false / false / false
DHT 代理Account.proxyEnabled / proxyServer / proxyListEnabled / dhtProxyListUrl / dhtProxyServerEnabled / dhtProxyServerPortfalse / dhtproxy.jami.net:[80-95] / true / https://config.jami.net/proxyList / false / 42443
TURNTURN.enable / server / username / password / realmtrue / turn.jami.net / ring / ring / ring
名字服务RingNS.uri""(用内置默认 ns.jami.net)
TLSTLS.certificateFile / privateKeyFile / certificateListFile / password<账户目录>/ring_device.crt / <账户目录>/ring_device.key / "" / ""
呼叫行为DHT.PublicInCalls / Account.autoAnswer / activeCallLimit / denySecondCall / rendezVous / dtmfTypetrue / false / -1 / false / false / overrtp
媒体与端口Account.videoEnabled / audioPortMin / audioPortMax / videoPortMin / videoPortMaxtrue / 16384 / 32766 / 49152 / 65534
行为与准入Account.sendReadReceipt / sendComposing / allModeratorEnabled / localModeratorsEnabled / defaultModerators / allowCertFromContact / allowCertFromHistory / allowCertFromTrusted / presenceSubscribeSupported / ringtoneEnabled / ringtonePath / uiCustomization / managerUri / managerUsernametrue / true / true / true / "" / true / true / true / true / true / default.opus / "" / "" / ""

4.2 运行态 volatile 9 键

键含义实测
Account.registrationStatus注册状态机:TRYING(连接中)→ REGISTERED(可寻址)REGISTERED
Account.deviceAnnounced设备证书是否已在 DHT 网络宣告true
Account.dhtBoundPort本机 DHT 实际绑定端口(dhtPort=0 时由系统分配)33089
Account.deviceID本设备指纹(与 ring_device.crt 对应)d49eef0f…a1ab43
Account.active账户是否启用true
Account.proxyServer实际使用的 DHT 代理(未启用时为空)""
IM.offCall非通话状态消息开关true
Transport.statusCode / statusDescription底层传输状态码100 / ""

排障时这 9 个键比配置文件直接得多:registrationStatus=TRYING 说明还在连 DHT;REGISTERED 但 deviceAnnounced=false 说明已入网但设备宣告未完成(重启后约需数十秒);dhtBoundPort 可用于防火墙放行实测端口。

4.3 daemon 全局 dring.yml 顶层结构

accounts:
  []                                    # 账户引用列表
preferences:
  historyLimit: 0                       # 历史保留天数上限(0 不限)
  ringingTimeout: 30                    # 振铃超时(秒)
  historyMaxCalls: 20                   # 历史记录条数上限
  md5Hash: false
  order: ff5bdfae563e6707/              # 账户排序
  portNum: 5060                         # 历史 SIP 形态遗留项
  searchBarDisplay: true
  zoneToneChoice: North America         # 提示音地区
voipPreferences:
  playDtmf: true                        # 本地回放 DTMF 音
  playTones: true
  pulseLength: 250
audio:
  audioApi: alsa                        # 音频后端(无头服务器常见 alsa/pulse 二选一)
  alwaysRecording: false
  captureMuted: false
  playbackMuted: false
  recordPath: ""
  volumeMic: 1
  volumeSpkr: 1
  audioProcessor: webrtc                # 音频处理引擎
  automaticGainControl: true            # AGC 自动增益
  voiceActivityDetection: true          # VAD 活动检测
  noiseReduce: auto                     # 噪声抑制
  echoCancel: auto                      # 回声消除
  alsa:
    cardIn: 0
    cardOut: 0
    cardRing: 0
    plugin: default
    smplRate: 44100
  pulse:
    devicePlayback: ""
    deviceRecord: ""
    deviceRingtone: ""
video:
  recordPreview: true
  recordQuality: 0
  decodingAccelerated: true             # 硬件解码
  encodingAccelerated: false            # 硬件编码
  conferenceResolution: 1280x720        # 会议默认分辨率
  devices:
    - name: desktop
      id: desktop
      input: desktop
      video_size: 0x0
      channel: ""
      framerate: 30
plugins:
  pluginsEnabled: false                 # 插件体系总开关
  installedPlugins:
    []
  loadedPlugins:
    []

说明两点:portNum: 5060 是 Jami 前身(SIP 形态)的遗留默认值,Jami 账户不使用该端口传输;video.devices 在无显示器的服务器上仅有占位设备项,不影响 daemon 消息与呼叫信令功能。

五、核心参数分类详解

5.1 账户身份与设备

键说明
Account.usernameRingID(40 位十六进制公钥指纹),账户创建时由密钥对派生,不可修改
Account.alias / displayName本地别名与展示名,仅影响界面显示,可随时 setAccountDetails 修改
Account.deviceName设备名,对端可见(实测 jamisrv1);建议改成有辨识度的主机名
Account.type账户类型:RING 即 Jami 账户(SIP 类型为对接传统 SIP 的形态)
Account.archiveHasPassword账户 archive(archive.gz)是否设了口令;true 时恢复账户需输入口令
Account.managerUri / managerUsernameJAMS(Jami Account Management Server)企业托管用,个人账户为空
Account.uiCustomizationJAMS 下发的界面定制项,个人账户为空

5.2 DHT 引导节点(Account.hostname)

Account.hostname = bootstrap.jami.net 是账户加入公共 OpenDHT 网络的入口。官方文档《Use Jami on a LAN》明确了三条规则:

  1. 多引导节点用分号分隔:bootstrap.jami.net;your.bootstrap.tld 是合法写法,daemon 依次尝试;
  2. 任何 DHT 节点都可当引导节点:直接填 IP:port(公共 OpenDHT 网络默认端口 4222,本环境实测 daemon 动态绑定 33089 对外提供节点服务);
  3. 自建引导节点:用 OpenDHT 的 dhtnode 工具拉起稳定节点(内存态、默认上限 8 MiB),然后把地址填入该键,即可组建完全离网的私有 DHT 网络。

局域网/离网场景的替代方案是打开 Account.peerDiscovery(UDP 广播发现同网段节点,无需填任何 IP)。本环境实测两个发现类开关(peerDiscovery / accountDiscovery / accountPublish)默认均为 false——服务器场景直连公共网络时无需打开。

Account.dhtPort = 0 表示不指定固定端口,由系统动态分配(实测分配到 33089)。需要过防火墙固定放行时,可把 dhtPort 设为固定值后重启账户,再从 volatile 的 dhtBoundPort 复核。

5.3 DHT 代理(移动端省电机制,服务器默认关闭)

键实测值说明
Account.proxyEnabledfalse是否经 DHT 代理同步。官方文档:代理用于移动设备省电(避免持续 DHT 同步),桌面/服务器直连即可
Account.proxyServerdhtproxy.jami.net:[80-95]官方公共代理地址(多个端口区间)
Account.proxyListEnabled / dhtProxyListUrltrue / https://config.jami.net/proxyList允许从官方列表服务拉取可用代理
Account.dhtProxyServerEnabled / dhtProxyServerPortfalse / 42443把本机 daemon 自身作为 DHT 代理对外提供服务(REST API),服务器如需给局域网移动端供电可开启

服务器场景结论:保持 proxyEnabled=false。volatile 里的 Account.proxyServer 为空串即印证当前是直连模式。

5.4 TURN 与 NAT 穿透

键实测值说明
TURN.enabletrueNAT 穿透失败时的中继回退开关
TURN.serverturn.jami.net官方公共 TURN(coTURN)
TURN.username / password / realmring / ring / ring官方公共 TURN 的默认凭据(非机密,官方文档明示可自建替换)
Account.upnpEnabledtrue尝试经 UPnP 在网关上映射端口
Account.publishedAddress / publishedSameAsLocal"" / true对外宣告地址;true 表示宣告本地地址

实测观察(安装篇 FAQ 同源):在无 UPnP 网关的网络里,daemon 日志会持续出现 DHT UPnP mapping … state=FAILED,随后自动回落直连公共 DHT,registrationStatus 仍为 REGISTERED——UPnP 失败不阻塞注册。纯内网部署时可保留默认(连不上 TURN 会被自动忽略),有安全诉求时可 setAccountDetails 指向自建 coTURN。

5.5 名字服务(RingNS.uri)

实测 RingNS.uri = "":空值时 daemon 使用内置默认 ns.jami.net(基于区块链的注册/查询服务)。自建名字服务器后把该键设为自建服务地址即可让本账户的「注册用户名」走私有目录;不改也不影响任何通信——RingID 直连从不依赖名字服务。账户级设置意味着不同账户可同时挂不同名字服务器。

5.6 TLS 与证书

键实测值说明
TLS.certificateFile<账户目录>/ring_device.crt本设备证书(账户创建时自动生成)
TLS.privateKeyFile<账户目录>/ring_device.key设备私钥(600 权限,绝对不可外泄)
TLS.certificateListFile / TLS.password"" / ""证书链与私钥口令(自动生成体系下为空)

Jami 的 TLS 材料全部由 daemon 自动生发(设备证书 + 账户 receipt 体系),无需也不应手工替换。安全边界在于文件系统权限:ring_device.key 与 archive.gz 是账户身份本体,config.yml 中的 ringAccountReceipt/ringAccountReceiptSignature 两个字段是账户在 JamiNS 的注册凭据(base64 大字段,daemon 维护,勿手工编辑)。

5.7 呼叫与消息行为

键实测值说明
DHT.PublicInCallstrue呼叫准入总开关(客户端「高级设置」对应项),关闭后本 RingID 不再响应 DHT 层入呼请求
Account.autoAnswerfalse自动应答(本篇 3.3 节用它完成了修改闭环实测)
Account.activeCallLimit-1并发呼叫上限,-1 不限
Account.denySecondCallfalse通话中拒绝第二路呼叫
Account.rendezVousfalse汇聚点模式:true 时账户作为会议汇聚点等待多方拨入(客服/会议机器人形态)
Account.dtmfTypeoverrtpDTMF 发送方式
Account.sendReadReceipt / sendComposingtrue / true已读回执 / 「输入中」提示
Account.ringtoneEnabled / ringtonePathtrue / default.opus来电铃音
Account.allowCertFromContact / History / Trustedtrue / true / true对端证书准入来源(联系人/历史/手工信任)三开关

5.8 媒体端口区间

Account.audioPortMin/Max = 16384/32766、Account.videoPortMin/Max = 49152/65534 是 ICE 媒体流候选端口区间;防火墙如需精确放行媒体流,按这两个区间(加上 5.2 的 DHT 端口)配置。Account.videoEnabled=false 可整体关闭视频能力(纯消息服务器可关,减少无谓的媒体协商)。

5.9 dring.yml 全局参数要点

全局文件里最值得管理的是音频处理与编解码链路:audio.audioProcessor: webrtc 配合 automaticGainControl/voiceActivityDetection/noiseReduce/echoCancel 四开关(服务器做语音机器人/录音场景常关 AGC 与 EC);video.decodingAccelerated/encodingAccelerated 控制硬件编解码(无 GPU 服务器保持默认 false);plugins.pluginsEnabled 默认关闭,Jami 插件体系按需启用。这些修改同样建议经客户端 API 路径完成,dring.yml 由 daemon 统一回写。

六、生产配置模板

场景 A:无头消息/通知服务器(1 vCPU、1 GB 内存、5 GB 磁盘,消息与 presence 场景)——默认值几乎即生产值,建议只做三件事:

# ① 设备名改成有辨识度的主机名(对端可见,便于审计)
setAccountDetails "sa{ss}" ff5bdfae563e6707 1 Account.deviceName jamisrv1
# ② 纯消息场景关闭视频能力
setAccountDetails "sa{ss}" ff5bdfae563e6707 1 Account.videoEnabled false
# ③ 每次修改后确认收敛回 REGISTERED
getVolatileAccountDetails → "Account.registrationStatus" "REGISTERED"

场景 B:自建基础设施(私有 DHT + 自建 TURN + 自建名字服务,2 vCPU、2 GB 内存、20 GB 磁盘,内网隔离通信):

# 多引导节点(自建 dhtnode 优先,官方兜底)
setAccountDetails "sa{ss}" ff5bdfae563e6707 2 \
  Account.hostname "bootstrap.jami.net;dhtnode.internal:4222" \
  RingNS.uri "https://ns.internal:8443"
# 自建 TURN(官方凭据体系替换)
setAccountDetails "sa{ss}" ff5bdfae563e6707 4 \
  TURN.enable true \
  TURN.server turn.internal:3478 \
  TURN.username jamiprod \
  TURN.realm ring

两个场景共同注意:Account.dhtPort 固定端口 + 防火墙放行(DHT 端口、16384-32766、49152-65534);服务器保持 DHT 代理关闭;密钥目录纳入备份策略(archive.gz + ring_device.key,见安装篇第 6.6 节)。

七、常见问题(实测命中)

  1. busctl 调 setAccountDetails 报 Too many parameters for signature:签名要整体传 sa{ss},不能拆成 s 与 a{ss} 两段(见 3.3 节)。报 Too few parameters 同理是签名与实参数量不匹配。
  2. 改完参数 volatile 里出现 registrationStatus: TRYING:setAccountDetails 触发账户重载并重新注册,实测约 25 秒内回 REGISTERED、deviceAnnounced=true,属正常过渡态;批量改参数后统一等待收敛即可。
  3. config.yml 里找不到某个参数:账户级文件只落盘非默认值。默认参数只在 getAccountDetails 里可见;修改过一次(即使改回默认)也会触发回写整理。
  4. Account.username 想改成名:RingID 是公钥指纹不可改;想在网络层绑定可读名字需走名字服务注册(RingNS),注册结果反映在 Account.registeredName(未注册时为空串)。
  5. UPnP 日志报 FAILED 要不要处理:无 UPnP 网关的网络必现,daemon 自动回落直连 DHT,注册状态不受影响;仅当需要被动入呼且网络可开 UPnP 时才值得排查网关。
  6. dring.yml 与 jamid 的命名差异:文件名保留项目历史名 dring,可执行文件为 /usr/libexec/jamid,是同一套 daemon 的历史命名延续,不是两个组件。

八、参考链接