Architecture Decision Records¶
Accepted Records¶
- 0000 - Record architecture decisions
- 0001 - Use Markdown Architectural Decision Records
- 0002 - Use an owner-tracking mutex for the runtime lock
- 0003 - Drain every recursion level in one release primitive
- 0004 - Release the runtime lock through the mutex, not through a guard
- 0005 - Open the lock release scope at the blocking callee
- 0006 - Extract released regions that produce values into functions
- 0007 - Assert lock behaviour from another thread
- 0008 - Keep reject-unsafe exemptions line-scoped
- 0009 - Serialize inbound PDU ingestion on per-room stripes
- 0010 - Run fire-and-forget work as parked futures
- 0011 - Split strict and general-purpose canonical JSON
- 0012 - Format floats with a portable round-trip search
- 0013 - Require a power level default when local state is absent
- 0014 - Confine cryptographic primitives behind providers
- 0016 - Require a master key rather than storing secrets in plaintext
- 0017 - Never regenerate a signing key silently
- 0019 - Apply rate limiting before authentication
- 0020 - Keep rate limit counters in memory
- 0021 - Deny when a rate limit policy cannot be resolved
- 0022 - Validate the forwarded client address
- 0023 - Give each thread its own outbound HTTP handle
- 0024 - Decode untrusted images in a sandboxed child process
- 0025 - Return not found rather than the original on thumbnail failure
- 0026 - Refuse media uploads at capacity rather than evicting
- 0028 - Drop push deliveries at the concurrency cap
- 0029 - Bound pusher delivery rather than registration
- 0030 - Key the pre-auth key resolution budget on source address
- 0031 - Store narrow purpose tokens in separate tables
- 0032 - Throttle failed logins per claimed account
- 0033 - Leave SSO protocol integration to an operator gateway
- 0034 - Use a longer login token window than the spec suggests
- 0035 - Parse appservice registrations as a bounded YAML subset
- 0036 - Match appservice namespaces against whole identifiers
- 0037 - Detect namespace conflicts by comparing pattern strings
- 0038 - Degrade third party lookups instead of failing them
- 0039 - Populate the full device list on an initial sync
- 0040 - Transfer PostgreSQL ownership with a scoped statement
- 0041 - Refuse to start the federation worker unsandboxed
- 0042 - Spawn the federation worker with a minimal environment
- 0043 - Base64 encode IPC response bodies
- 0044 - Wipe secrets with an optimisation barrier
- 0045 - Ship a static musl tarball as the portability fallback
- 0046 - Request FORTIFY_SOURCE only on optimised builds
- 0047 - Keep project phase out of the version string
- 0048 - Treat soak and load runs as release evidence not CI
- 0049 - Treat the specification as the conformance authority
- 0050 - Use real dependencies in integration tests
- 0051 - Document inert configuration keys rather than removing them
- 0052 - Revocation of credentials is one-way
- 0053 - The thumbnail decoder gets its own syscall profile
- 0054 - TLS sockets stay non-blocking for the life of the connection
- 0055 - Filter all diagnostics and preserve audits
- 0056 - Use a thumbnail-specific rate limit
- 0057 - UIAA sessions are in-memory and single-stage
- 0058 - A suspended account may still refresh its access token
- 0059 - Serialise migrations per backend, not per abstraction
- 0060 - Show an uploaded key signature only to the audience its uploader has
- 0061 - Proxy remote cross-signing keys rather than caching them
- 0062 - Federation worker holds no secret files; secrets arrive over inherited fds
- 0063 - Fail closed when the state-res v2/v2.1 auth-chain walk cannot reach an event
- 0064 - Spec-conformant PDU ingestion with delta state groups
- 0065 - Cap in-flight IPC requests per channel
- 0066 - UI-auth password checks share the /login failed-login counter
- 0067 - Enforce federation PDU size and field-count limits before hashing
- 0068 - Random media IDs and a per-row legacy-endpoint freeze marker
- 0069 - Spec-conformant /state_ids fallback via /event_auth — amended by ADR-0070
- 0070 - Events verified only against their auth_events carry no state
- 0071 - Main re-verifies PDUs relayed by the federation worker
- 0072 - Per-IP connection cap at accept time — amended by ADR-0077 for the worker-share bound
- 0073 - The client address is the rightmost untrusted X-Forwarded-For entry
- 0074 - Refresh tokens retire on first use of their successor
- 0075 - Event signing-key validity is judged at the event's origin_server_ts
- 0076 - Room versions 1 and 2 are not supported
- 0077 - No worker waits on a quiet connection: a dispatcher thread holds idle connections, with a per-client worker share
- 0078 - The federation worker never signs
- 0079 - Client-triggered outbound proxying runs under a bounded in-flight budget, not a separate pool
- 0080 - Rate-cap audit rows for unauthenticated rejections
- 0081 - Failed inbound signatures are never charged to the claimed origin
- 0082 - No thread may start before process hardening; seccomp filters are installed with TSYNC
- 0083 - send_join responses are bound to the joined room; own-domain events are always signature-checked
- 0084 - History visibility is judged on the recorded state at each event, one filter per request, and fails closed
- 0085 - An inbound invite for a room we hold leaves room state to the transaction path
- 0086 - Outbound federation calls own the signing secret
- 0087 - Critical logs are written synchronously
- 0088 - Limit stripped invite and knock state to room summaries
- 0089 - Defer unsolicited PDUs during outbound joins in bounded transient queues
- 0090 - Bound Argon2id work with a process-wide admission semaphore
- 0091 - Bound sync waits before handing off the connection
- 0092 - Normalize known rate-limit routes and evict least-recently-used buckets
- 0093 - Require explicit selection of the test-only memory database
- 0094 - Revive removed media storage identities
- 0095 - Bound conflicted state traversal by distinct auth events
- 0096 - Retry worker spawn failures without waiting on unrelated children
- 0097 - Check outbound socket peers against each request's approved pins
- 0098 - Limit presence status messages by UTF-8 byte length
- 0099 - Default unrecorded directory publication to private
- 0100 - Commit media moderation with audit and blob state
- 0101 - Build the documentation site on every branch, but publish only from main
- 0102 - Escape log control characters once, at the sink
- 0104 - Re-verify the user and password hash after Argon2id
- 0105 - Silently discard sendToDevice to unknown local recipients
- 0106 - to_device next_batch is the last included stream ID
- 0107 - Filter inbound federation direct-to-device EDUs by known local devices
- 0108 - Federation worker stale snapshot fallback to main
- 0109 - Configure operational budgets with shared recovery bounds
- 0110 - Federation worker trusts the main-process verified identity for room-scoped reads
- 0111 - Bound the IPC dispatch queue
- 0112 - Restrict worker signals and resource limits
- 0113 - Default media quotas and a single in-memory blob copy
- 0114 - Cache remote media by origin server and media ID
- 0115 - Canonicalise media server-name comparisons
- 0116 - Verify token revocation state in the auth service
- 0117 - Bound to-device queues by recipient count and age
- 0118 - Refuse E2EE key and filter uploads at per-user caps; store filters canonically
- 0119 - Media bytes live in the database and are read on demand
- 0120 - Cached remote media has its own storage budget
- 0121 - Remote media fetches run on their own pool, one fetch per remote file
- 0122 - Login failures are throttled per source, with an account-wide ceiling
- 0123 - 3PID ownership is proven only by a trusted identity server
- 0124 - Application services assert only registered, active users, and never on Account Management
- 0125 - Redactions are applied once, at the store, and destroy the original content
- 0126 - Local membership is projected once, at the local-event choke point
- 0127 - Room directory and alias changes need canonical-alias power; aliases record their creator
- 0128 - Lock derived-key storage without changing persisted derivations
- 0129 - Restrict main's fchmod to owner-only database mode
- 0130 - Admit only a private startup umask through main's inherited seccomp
- 0131 - Keep malformed-first-PDU transactions inside worker shard zero
- 0132 - Separate cached verification keys from budgeted discovery
- 0133 - Preserve local key-admission denial separately from remote failure
- 0134 - Share key-resolution admission with main's worker-relay verification
- 0135 - Preserve key-admission denial through backfill recovery
- 0136 - Retain path-only, bounded audit targets
- 0137 - Reject NUL before mutation and path decoding
- 0138 - Require explicit remote PostgreSQL TLS policy
- 0139 - Suppress PostgreSQL connection options in diagnostics
- 0140 - Isolate push delivery capacity per recipient
- 0141 - Bound configured token lifetimes without coercion
- 0142 - Bind worker EDU relays to live main-verified requests
- 0143 - Bound thumbnail crop intermediates by the existing pixel budget
- 0144 - Apply the shared media policy before federation media reads
- 0145 - Bind local thumbnail disclosure to source record identity
- 0146 - Open the log file without following links, owner-only, and keep truncating it
- 0147 - Filter the worker's socket families, and restrict them in the systemd unit
- 0148 - Packaging paths pass
--buildtype=releaseexplicitly;default_optionskeeps Meson's default - 0149 - Membership rows commit with their event, and are reconciled from current state at startup
- 0150 - Legacy float power levels are read by the auth rules, but never hashed
- 0151 - Cap pusher registration and break the circuit to failing gateways
- 0152 - Cache thumbnails in memory and cap concurrent decodes per requester
- 0153 - Split the key-resolution budgets across federation worker shards
- 0154 - Pool database write connections up to database.pool_size
- 0155 - Bound send_join key fetches by the shared in-flight cap, waiting for a slot
- 0156 - Stamp v12 create events with a strictly increasing timestamp, stepping past taken room IDs
- 0157 - Never open a SQLite database file outside SQLite while connections may be open
- 0158 - Check out every text file with LF endings on every platform
- 0159 - Retry a journal that only looks hot on read-only SQLite reads, for a bounded time
- 0160 - Main records every completed federation transaction, whichever process ran it
- 0161 - A room reload reads one snapshot; the worker's whole-store load does not
- 0162 - Plan every unveil(2) call before making the first, and make none if one would fail
- 0163 - Appservice delivery is a durable outbox drained by one thread
- 0164 - Send-to-device commits its local messages and its transaction ID together
- 0165 - Retain unresolved federation PDUs and retry them
- 0166 - Derive to-device message IDs per sender and record them only after the messages commit
- 0167 - Use std::thread and StopFlag, never std::jthread or std::stop_token
- 0168 - Refuse an unlocked master key
- 0169 - Read receipts are durable, and a peer's flood never evicts a local user's receipt
- 0170 - A user's own non-state event reads its own thread
- 0171 - Every PDU ingestion path verifies a restricted join's authorising-server signature
- 0172 - Sliding-sync sort keys are computed once per request, and the shape of the lists is capped
- 0173 - Cap a user's global account-data types; room account data stays bounded by membership
- 0174 - Sliding-sync extension room lists share the room_subscriptions cap and are classified through one index
- 0175 - Push glob matching is a hand-written linear matcher, never std::regex
- 0176 - Federation media multipart is parsed under the lock, with a linear delimiter scan
- 0177 - Each outbound destination holds a bounded share of the dispatch queue, and every transaction has its own ID
- 0178 - Remote media cache membership is recorded on the record, not inferred from the owner string
- 0179 - A room's stored version is resolved through one accessor, and a join must match the create event
- 0180 - The outbound client refuses header fields that are not valid HTTP
- 0181 - Third-party lookups take a client outbound proxy slot and keep per-appservice timeouts
- 0182 - A profile change succeeds once stored, and reaches only rooms whose state holds the user joined
- 0183 - Every known, unexpired key's signature of the sender's server must verify
- 0184 - Federation workers relay make_join to main
- 0185 - A resident authorises restricted joins only after checking the allow conditions
- 0190 - Appservice ephemeral data is in memory, bounded, coalesced and best-effort
- 0191 - Cap the events queued for one appservice and refuse the rest — refines ADR-0163
- 0192 - Appservice network directories are persisted per protocol, and instance IDs name the protocol
- 0197 - Room account data needs a membership in the room and is capped per room
- 0198 - A registration without a username gets a random hexadecimal localpart
- 0199 - Typing expires on a periodic job, with a ceiling for clients and a timeout for federation
- 0204 - Keep a user's push rules as their m.push_rules account data, applied over the current defaults
- 0205 - Count unread notifications by the user's push rules at read time, from the start of their join
- 0206 - Retry failed push notifications from a bounded in-memory queue on one job thread
- 0207 - Count the push badge in one bounded walk of the store, and saturate it
- 0211 - Media file names are validated and encoded before they reach a header
- 0212 - Asynchronous uploads reserve media IDs durably and wait for content on the media fetch pool
- 0213 - URL previews are off by default and fetch only through one SSRF gate
- 0214 - The URL preview cache is keyed by URL alone and caches failures
- 0215 - The event table owns its rows and indexes them by event ID and by stream order through positions
- 0216 - The upgrade path is tested from a forward-only schema seam and a database the previous release wrote
- 0217 - Branch coverage excludes the compiler's exception edges
- 0218 - OS-supplied dependencies carry no wrap file
- 0219 - Email pushers mail only the account's own addresses, an unread count, in a delayed digest
- 0220 - SMTP goes through libcurl, with TLS required except to a loopback literal
- 0221 - A grown sliding sync timeline_limit skips the long-poll once per request pos
- 0222 - Library linkage is read from the loader at runtime, not from the build
- 0223 - The documentation site wears the OMG Software theme, dark first, with system fonts
- 0224 - Pre-1.0 migrations collapse into one v1 schema, frozen at 1.0.0, and an unknown ledger is refused
Proposed Records¶
- None
Rejected Records¶
- None
Superseded Records¶
-
0018 - Warn rather than abort when memory locking fails — superseded by ADR-0168; master-key lock failure is fatal
-
0029 - Bound pusher delivery rather than registration — superseded by ADR-0151 for registration: distinct pushers per user are now capped; the per-delivery bound remains
- 0027 - Bound the connection queue but not the IPC queue — superseded by ADR-0065 for the IPC pools only; the connection-queue half remains accepted
Deprecated Records¶
- None
Records with non-standard statuses¶
- None
- 0066 - UI-auth password checks share the /login failed-login counter — Superseded by ADR-0122: UIA password checks have their own (account, device) counter, which login failures never touch
- 0032 - Throttle failed logins per claimed account — Refined by ADR-0122: failures are counted per (account, source) with an account-wide ceiling
- 0079 - Client-triggered outbound proxying runs under a bounded in-flight budget, not a separate pool — Superseded by ADR-0121 for remote media downloads and thumbnails, which run on their own pool; directory lookups keep this budget
- 0015 - Keep the signing secret out of the federation worker — Superseded by ADR-0078 for the signing-oracle part (the
sign_requestframe); the signing secret still never enters the worker - 0126 - Local membership is projected once, at the local-event choke point — Refined by ADR-0149: the projection commits in the event's own transaction, and a row is realigned if resolution leaves another member event current
- 0163 - Appservice delivery is a durable outbox drained by one thread — Refined by ADR-0191: the queue of one appservice is capped at
appservice.max_queued_events, and ADR-0190: ephemeral data rides the same transactions but is not durable - 0173 - Cap a user's global account-data types; room account data stays bounded by membership — Refined by ADR-0197: room account data also needs a membership row and is capped per room
- 0216 - The upgrade path is tested from a forward-only schema seam and a database the previous release wrote — Refined by ADR-0224: the migrations collapsed in 0.12.47, before 1.0.0, and the 0.11.13 fixture is gone; the seam and synthetic scenarios stay