$ xmrhost-cli notes show --slug=matrix-homeserver-threat-model
[$ ] note: matrix-homeserver-threat-model
// Threat model for a self-hosted Matrix homeserver — federation, registration spam, AS-level attacks
// 2026-05-06 · diff=advanced · read=16min · tags=[matrix, synapse, federation, threat-model, hardening] · by=Vex
// ABSTRACT
abstract
A walk-through of the threat model for a self-hosted Synapse / Dendrite / Conduit homeserver: who you are protecting against, what they can actually do at the federation layer, the registration-spam economy that nobody warned you about, the AS-level interception risk for federation traffic, and the operational controls (registration policies, federation allow-lists, room-version pinning, per-room access-token rotation) that meaningfully change the attacker's surface. Synapse-flavoured but the threat model applies to any homeserver implementation.
Who this is for
You’ve stood up a Matrix homeserver — Synapse, Dendrite, Conduit; doesn’t matter for this note — and you’re hosting a small community on it. The basics are working: federation talks to matrix.org, element.io clients can log in, end-to-end encryption is set up correctly. Now you’re asking: what should I actually be defending against?
The answer is structurally different from the answer for, say, a self-hosted Mastodon instance. Matrix’s federation model, registration economy, and protocol surface produce a specific set of attacker categories. The upstream documentation covers configuration; this note covers who you’re configuring against.
Three attacker categories, in order of likelihood
The most likely attacker is the registration-spam operator. This is a person or a botnet that scans the public Matrix homeserver list, finds homeservers with open registration, signs up thousands of accounts, and uses those accounts to spam the federation — typically with cryptocurrency-promotion content, occasionally with abusive content meant to exhaust the homeserver’s storage budget. This attacker has no specific interest in you; you’re just on a list. The defence is the registration policy. The cost of getting it wrong is reputational (the homeserver gets defederated by other homeservers) and operational (the storage budget gets used up by spam-account media).
The second most likely attacker is the federation-abuse operator. This is someone who has signed up an account on a homeserver they control (or someone else’s lax-registration homeserver) and uses it to harass users on your homeserver via federation. The attacker can’t read encrypted-room contents but can join unencrypted rooms, send federation-level events to room IDs they’ve discovered, and exhaust the homeserver’s room-event storage budget by repeatedly creating-and-leaving rooms. The defence is the federation allow-list (or, more often, the deny-list of known-bad homeservers — the brand operators of the-bad-place.example style homeservers).
The third and rarest attacker is the AS-level interceptor. This is someone with the ability to observe (and potentially manipulate) traffic between your homeserver and other homeservers at the autonomous-system level — a state-level actor, an ISP under court order, or an upstream provider compromised at the network level. Federation traffic between Matrix homeservers IS TLS-encrypted, so a passive interceptor sees only the metadata: which homeservers federate with which, and the volume / timing of federation traffic. An active interceptor with a compromised CA could mount a TLS-MitM attack against federation — the homeserver checks the federation peer’s TLS certificate against a Web PKI trust root, the same trust root that any browser uses, with all the well-known weaknesses thereof. [RFC 6125]RFC 6125 — TLS server-identity verification
The first two categories are present from the day you go online. The third is a “depending on your threat model” matter; for a community-of-friends homeserver, it’s probably not on the threat list. For a homeserver hosting newsroom-source-protection rooms, it absolutely is.
Registration policy — the single highest-value lever
The Synapse registration_enabled config flag is the on/off switch for new accounts. The three sane settings:
# /etc/matrix-synapse/homeserver.yaml — registration policy excerpt.
# OPTION A — fully closed registration. Operator manually creates accounts.
# This is the right default for any homeserver smaller than ~50 users.
enable_registration: false
# OPTION B — token-gated registration. Operator distributes signup tokens
# (one per intended user) out-of-band. Tokens are single-use; the operator
# can revoke them.
enable_registration: true
registration_requires_token: true
# OPTION C — invite-only registration via a bot/admin in an existing room.
# Requires the matrix.org "matrix-registration" service or equivalent.
enable_registration: false
# (registration handled out-of-band by an invite-bot account)
A homeserver with enable_registration: true and registration_requires_token: false will, within 6-12 weeks of being publicly reachable, be discovered by registration-spam operators. The first sign is usually a sudden 5-10x increase in the rate of new accounts; the second sign is a flood of identical messages cross-posted to many federated rooms. [Matrix.org spec — Registration and Authentication]
Generating registration tokens via the admin API
The relevant admin endpoint is /_synapse/admin/v1/registration_tokens/new:
{
"token": "Lw0Ph9xK4nQ8aB2c",
"uses_allowed": 1,
"pending": 0,
"completed": 0,
"expiry_time": 1714000000000
} The token is a single-use 16-character string; distribute it out-of-band, the recipient pastes it into the Element registration flow, and the token’s pending count goes to 1, then completed after the account-creation transaction finalises.
Federation allow-list / deny-list
Synapse supports federation_domain_whitelist (allow-list — only listed homeservers can federate with you) and federation_domain_blacklist (deny-list — listed homeservers cannot federate with you). Use the deny-list for general operation and the allow-list only if your homeserver is operated for a specific community that has no need to federate beyond a known set.
# /etc/matrix-synapse/homeserver.yaml — federation deny-list.
# Hosts that cannot federate with this homeserver. Used to block known-bad
# homeservers operated by spam / abuse actors. Maintain the list as part
# of normal operations; sync from community-curated lists like the
# matrix-org "trust and safety" room's published lists.
federation_domain_blacklist:
- "homeserver.run.by.spammers.example"
- "abuse-host-1.example"
- "abuse-host-2.example"
# Conversely, an allow-list is the right shape for a closed-community
# homeserver. NEVER use both; allow-list takes precedence and the
# deny-list becomes meaningless.
# federation_domain_whitelist:
# - "matrix.org"
# - "tchncs.de"
# - "fairydust.space"
For deny-list hygiene: the matrix.org community publishes blocklists; pull from those, but review before applying. A blocklist that’s too aggressive defederates the homeserver from large parts of the network and produces support-channel angst from users who can’t reach their friends.
Room-version pinning
Matrix room versions evolve over time; older room versions have known weaknesses (the most famous: room version 1 allowed “state resets” via federation that could elevate an attacker’s power level). Force new rooms to use the current recommended version:
# /etc/matrix-synapse/homeserver.yaml
default_room_version: "11"
Existing rooms on older versions can be upgraded by the room admin via the /upgrade command in Element. For an active homeserver, periodically audit which rooms are on which versions; the brand’s /docs/matrix-room-version-audit entry covers the audit query.
Federation traffic and the CA-trust question
Matrix federation traffic between homeservers uses HTTPS over TLS, with the federation peer’s TLS certificate checked against the standard Web PKI. The homeserver builds a trust chain rooted in the system CA bundle (Mozilla’s root list on most distros). A successful active TLS-MitM attack against federation requires either:
- A compromised root CA (the “DigiNotar 2011” scenario).
- A misissued certificate from a legitimate CA (the “Symantec 2017” scenario).
- A compromised intermediate CA controlled by the attacker.
- A coerced subordinate CA in the target’s home jurisdiction.
For a high-threat-model homeserver — newsroom-source-protection rooms; whistleblower contact rooms; political-organisation coordination rooms — the standard Web PKI trust model is too broad. The mitigation is to pin the expected federation peers’ certificates, which Synapse supports via federation_certificate_verification_whitelist for development and tls_certificate_pinning patterns layered above it via reverse-proxy controls in production. Implementing this correctly is non-trivial and out of scope for this note; the brand’s /docs/matrix-federation-cert-pinning entry covers it. [RFC 7469]RFC 7469 — Public Key Pinning Extension for HTTP (deprecated for browsers, still relevant for federated systems)
The storage-exhaustion attack
A federation-abuse operator with one or more accounts on permissive homeservers can fill your homeserver’s storage by: creating thousands of rooms, federating them to your homeserver via a member account, posting moderate-sized media to each, then leaving the rooms. Your homeserver retains the room-event history (for the rooms it has joined) and the media (for the period defined by media_retention).
The defences are layered:
# Bound the per-user upload size and the cumulative media-store size.
max_upload_size: 50M
media_retention:
local_media_lifetime: 90d
remote_media_lifetime: 14d
# Limit the rate at which a single account can create rooms via federation.
rc_federation:
window_size: 1000
sleep_limit: 10
sleep_delay: 500
reject_limit: 50
concurrent: 3
# Bound the rate at which a homeserver can request federation events.
rc_federation_request:
per_second: 50
burst_count: 100
These rate limits don’t prevent the attack but slow it enough that you have time to identify the attacking homeserver and add it to the deny-list before the storage budget is exhausted.
Logging discipline (what NOT to log)
Synapse logs are surprisingly chatty by default. The DEBUG-level log of federation events includes room IDs, event IDs, and (for unencrypted rooms) the event content. For a homeserver with a “minimal logging” stance, override the per-handler log level:
# /etc/matrix-synapse/log.config
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s'
handlers:
file:
class: logging.handlers.RotatingFileHandler
formatter: precise
filename: /var/log/matrix-synapse/homeserver.log
maxBytes: 104857600
backupCount: 10
encoding: utf8
# Drop DEBUG-level federation logs entirely.
null:
class: logging.NullHandler
loggers:
synapse:
level: WARNING
synapse.federation.transport:
level: ERROR
handlers: [file]
synapse.federation.federation_server:
level: ERROR
handlers: [file]
synapse.handlers.message:
level: ERROR
handlers: [file]
root:
level: WARNING
handlers: [file]
For an even stricter stance, log to a memory-only ring buffer (via journald with Storage=volatile) rather than to disk. Trade-off: post-incident forensics get harder. The right balance depends on your threat model.
Per-room access-token rotation
Synapse access tokens are long-lived by default — they don’t expire unless the user explicitly logs out. For a homeserver hosting high-sensitivity rooms, force token expiry via the SSO / OIDC flow’s access_token_lifetime, then require re-authentication every 24-72 hours.
This breaks Element’s “stay logged in forever” UX and will produce support-channel friction. For most homeservers it’s the wrong trade-off; for a newsroom-style homeserver, it’s the right trade-off. Document the policy somewhere users can find it, and pair it with hardware-key 2FA (FIDO2 / WebAuthn) so the re-authentication is a touch-key press, not a password retype.
Backups and the ratchet-state problem
Encrypted-room state (the Megolm ratchet) is held client-side, not server-side. A homeserver backup that loses the user’s client-side state means the user loses access to encrypted-room history. The mitigation is Secure Backup (formerly “key backup”): the user uploads an encrypted copy of their Megolm keys to the homeserver, encrypted with a passphrase only they know. [Matrix Spec — Secure Backup]
Promote Secure Backup to users as part of onboarding. The brand’s /docs/matrix-onboarding-checklist entry covers the prompts; the operational reality is that users who don’t enable it will lose access to encrypted history sooner or later, and the support load from “I lost access to my room history” requests is higher than the cost of the onboarding prompt.
Closing — the threat model recap
Per the three attacker categories at the top:
- Registration-spam operators — defended by
registration_requires_token: trueand the admin-API token flow above. - Federation-abuse operators — defended by the federation deny-list, room-version pinning, the rate-limit config block, and a moderation rotation among the homeserver admins.
- AS-level interceptors — partially defended by federation TLS (which is on by default), better defended by federation cert pinning (operationally fragile), best defended by limiting which federation peers you exchange traffic with in the first place (which is the allow-list approach).
For a community-of-friends homeserver, the first two are the entire threat list and the configurations above are sufficient. For a higher-threat-model homeserver, the third joins the list and the trade-offs get sharper. Either way, the threat model is the document that comes first; the configuration is the document that comes second.
// END OF NOTE
$ cd /notes # back to the listing