Skip to content

Persistent chat

Persistent chat means messages survive a disconnect. Without it, a Mumble server forgets everything as soon as a user leaves, just like a phone call.

Fancy Mumble does not implement this as a single scheme. The server provides the storage, key-distribution, and rate-limiting machinery, and each channel picks one of three modes that decide how encryption and history work. The modes range from “channel forgets everything” to “server stores the ciphertext” to “end-to-end encrypted via Signal Sender Keys”.

Chat showing yesterday's conversation after a reconnect

These belong in the environment: block of your docker-compose.yml (or in [server] of mumble-server.ini). They turn the feature on and set defaults; they do not pick a per-channel protocol.

environment:
MUMBLE_CONFIG_PCHATENABLED: true
MUMBLE_CONFIG_PCHATREQUIREREGISTRATION: false
MUMBLE_CONFIG_PCHATDEFAULTMAXHISTORY: 5000
MUMBLE_CONFIG_PCHATDEFAULTRETENTIONDAYS: 90
MUMBLE_CONFIG_PCHATMAXPAYLOADSIZE: 1048576
KeyDefaultDescription
pchatenabledtrueMaster toggle for the persistent-chat subsystem. If false, the server rejects every Pchat* message regardless of channel protocol.
pchatrequireregistrationfalseIf true, only registered users can post into a persistent channel.
pchatdefaultmaxhistory5000Default cap on stored messages per channel. Used when a channel is created; admins can override per channel.
pchatdefaultretentiondays90Default age cap in days. 0 means “keep forever”. Override per channel.
pchatmaxpayloadsize1048576Max bytes per encrypted envelope (1 MB). Image attachments are base64-encoded inside the envelope, so size this for the largest inline attachment you want to allow.
pchatpendingkeyrequestmaxdays7How long an unfulfilled key-distribution request lingers before cleanup.
pchatpendingfulfilledmaxhours24How long a fulfilled key-distribution request stays around (so late joiners can pick it up).
pchatperuserpending5Hard limit on simultaneous outstanding key requests per user.
pchatperchannelpendingsoftcap100Soft cap on outstanding key requests per channel; prevents request floods.

A small set of token-bucket rate limits is also applied on the server (msg: 30 / 60s, fetch: 10 / 60s, key_announce: 5 / 60s, key_exchange: 20 / 60s). These are not currently configurable.

Each channel has one persistence mode, chosen from the channel editor in the admin UI. New channels default to None and store nothing until an admin picks a mode.

NoneFull Archive ExperimentalSignal V1
History for newcomers-Yes - full log up to the retention limit.No - brand-new joiners get no backlog.
Missed messages on reconnect-All missed messages are delivered by the server on reconnect - nothing is lost.Automatically pushed on reconnect for existing members.
Server holds messages-Yes, as encrypted ciphertext the operator cannot read.Only a short-lived offline queue; no permanent archive.
If the server is seized-Encrypted ciphertext is exposed (unreadable without the channel key).Only the transient queue exists - no full history to hand over.
Works without Fancy MumbleYes (live chat only)Read-only if senders enable dual-path (see below) - no history, cannot send encrypted.Read-only if senders enable dual-path (see below) - voice only otherwise, cannot send encrypted.
SetupNothing to do.Set the mode, optionally add key custodians.Requires the signal-bridge library on every client.
Best forEphemeral voice rooms.Community and support channels where history is useful.Small private groups where “no trace on the server” matters most.

The two persistent modes differ in who holds keys and where messages live.

Full Archive Experimental

Section titled “Full Archive ”
  • The server keeps an opaque encrypted envelope per message, plus a small amount of metadata (message_id, channel_id, sender_hash, timestamps, optional replaces_id).
  • Symmetric channel keys are held by key custodians - a list of TLS certificate hashes attached to the channel (pchat_key_custodians). When a new member joins, they emit a PchatKeyRequest; the server relays it to online custodians up to a relay cap that scales with the number of online members. A custodian answers with a PchatKeyExchange, preceded by a PchatKeyChallenge (HMAC challenge so the custodian knows it is talking to a legitimate session).
  • If no custodian is online, the request is queued (pchatpendingkeyrequestmaxdays) and replayed when one connects.
  • Trust model: the server is honest-but-curious. It cannot read messages, but a malicious operator could replace the binary and intercept future key-exchange traffic. This is the same trust model as virtually every “encrypted at rest” group chat.
  • Uses the Signal protocol’s Sender Keys for group messaging, via signalapp/libsignal.
  • Because libsignal is AGPL-3.0, Fancy Mumble ships it as a separate shared library - signal-bridge - built from crates/signal-bridge as a cdylib and loaded at runtime via libloading. The MIT- licensed Fancy Mumble client links to it dynamically; the AGPL obligations stay scoped to that one .dll / .so / .dylib.
  • The server does not permanently store Signal V1 messages as a searchable archive. However, it does queue encrypted envelopes for known offline members (existing key holders) in a transient offline queue. When a known member reconnects, the server drains that queue and bundles the Sender Key Distributions (SKDMs) for each sender whose messages appear in it, so the client can decrypt them without needing any other member online. Fetch requests (history) against a Signal V1 channel still return empty - the offline queue is a push, not a pull.
  • A client without signal-bridge available cannot participate in a Signal V1 channel at all (it can still join the channel for voice).

In addition to picking a mode, an admin can set:

UI fieldWire fieldDefault sourceMeaning
Protocolpchat_protocolnoneWhich mode applies to this channel.
Max historypchat_max_historypchatdefaultmaxhistoryMax messages kept (Full Archive only).
Retention dayspchat_retention_dayspchatdefaultretentiondaysMax age in days; 0 = forever (Full Archive only).
Key custodianspchat_key_custodiansemptyTLS cert hashes of users that hold and distribute the channel key.

To set them:

  1. Open the channel editor (right-click a channel → Edit).
  2. Open the Protocol dropdown and choose:
    • None (standard volatile chat) to disable persistence on this channel.
    • Full Archive (all messages) for an open searchable log; the server stores encrypted ciphertext.
    • Signal V1 (E2EE via Signal Protocol) for group E2EE. Missed messages are queued and pushed on reconnect; new joiners see no history.
  3. Set Max history and Retention days (ignored for Signal V1).
  4. Add the cert hashes of the Key custodians that should answer join-time key requests (Full Archive only; for Signal V1 the existing online members serve the same role automatically).
  5. Save.
Per-channel persistent chat settings tab

For Full Archive channels, encrypted envelopes live in the same SQLite database as the rest of the server state, at /data/mumble-server.sqlite inside the container. The schema includes separate tables for messages, user keys, member-join records, key-holder lists, pending-key-requests, the offline delivery queue, reactions, and pins.

For high-traffic servers point the server at PostgreSQL by setting MUMBLE_CONFIG_DBDRIVER=QPSQL and friends.

A rough sizing guide:

  • Each encrypted text envelope is roughly 200–400 bytes on disk including indexes and metadata.
  • 10 000 messages per day on a busy server is about 3 MB per day, so about 1 GB per year before reactions / images.
  • Inline images are base64-encoded inside the envelope. They will dominate disk usage if allowed; size pchatmaxpayloadsize accordingly.

Plan disk space, and back the volume up regularly. See Upgrade & backup.

Signal V1 channels store nothing at rest, so they contribute zero to ongoing disk usage.

To turn persistence off for a sensitive channel without touching the server-wide config, set the channel’s Protocol back to None (standard volatile chat). Existing stored ciphertext is not deleted by toggling - the server simply stops accepting new messages and stops serving fetches for that channel. To clear stored history, use the admin “Purge channel” action.

environment:
MUMBLE_CONFIG_PCHATENABLED: false

This makes the server reject every Pchat* message. Existing rows are not deleted, so you can re-enable later without data loss.

ClientHistory on a Full Archive channelLive chat in a Signal V1 channel
Fancy Mumble (desktop / Android, with signal-bridge)yesyes
Fancy Mumble built without signal-bridgeyesno - channel is voice-only for them
Vanilla Mumblenono
Other forksdepends on whether they implement the Pchat* proto messagesalmost certainly no

Vanilla Mumble users still see live TextMessages in real time on any channel - Pchat* is an additive layer on top of the existing text-message wire protocol.

Users can enable dual-path sending in Settings → Privacy → Enable dual-path sending. When on, every message sent in an encrypted channel is also sent as a plain TextMessage whose body is replaced with [Encrypted message]. Legacy clients (vanilla Mumble, older forks) then show that placeholder instead of seeing nothing at all.

Dual-path is off by default. Users who want strict ciphertext isolation - so that no unencrypted traffic for the channel ever traverses the normal message path - should leave it disabled.

  • “My channel doesn’t keep history.” Check the channel’s Protocol, not just pchatenabled on the server. A new channel defaults to None.
  • “Signal V1 shows no history when I reconnect.” For existing members the server queues missed messages and delivers them on reconnect automatically - if you see nothing, check that your signal-bridge library loaded correctly. If you are a brand-new joiner, that is expected: there is no backlog for first-time joins.
  • “Key request just hangs.” For Full Archive: no key custodian is online; the request stays queued until one connects (up to pchatpendingkeyrequestmaxdays). For Signal V1 - new joiner: no online channel member is available to distribute the Sender Key; the request is similarly queued. Existing reconnecting Signal V1 members do not need a key request - the server bundles the necessary sender keys with the offline message queue automatically.
  • “History grows forever.” pchatdefaultretentiondays (or the channel’s pchat_retention_days) is 0. Set a value, then cleanup runs daily.
  • “Cannot delete a message.” The channel’s delete ACL does not include your role. See Roles & permissions.
  • “Plugin keys aren’t picked up via env vars.” They are - the Fancy Mumble Docker entrypoint accepts every key in the upstream mumble-server.ini, including pchat*. See Configuration reference.

Continue with Push notifications.