Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Threat Model — v1

  • Status: v1, written by work package SRV-003 on 2026-10-14; the owner accepted the two ADRs it proposed, ADR-0029 and ADR-0030, on 2026-10-17. It replaces v0 (the planning session, 2026-10-03), keeps every threat number of v0, and is reviewed at every phase gate and whenever a work package changes a trust boundary (plan/07).
  • Owner stream: SECURITY
  • Method: data-flow diagrams analysed with STRIDE, per element (§4.5, §4.6) and per flow (§6). STRIDE names six kinds of threat: Spoofing (pretending to be someone else), Tampering (changing data), Repudiation (denying having done something), Information disclosure, Denial of service and Elevation of privilege (getting more power than allowed).
  • Decisions it relies on: some of the designs analysed here are decided in ADR-0029 (identity keys, device approval and recovery) and ADR-0030 (commit acceptance, departures and group repair), which SRV-003 proposed and the owner accepted on 2026-10-17, ADR-0030 together with its three departures from the owner’s earlier direction (§7.2).
  • Related: SEC-01…SEC-13, COL-01…COL-14, OPS-01…OPS-09, ADR-0006, ADR-0008, ADR-0012, ADR-0014, ADR-0015, ADR-0016, ADR-0017, ADR-0023, ADR-0026, ADR-0028, the engine protocol, and the report of the MLS spike SRV-002, spikes/mls/REPORT.md in bayan-server.

Summary for the owner

  • What BayanDocs protects. The content of shared documents (text, comments, media, edit history, titles and folder structure) is readable only on the devices of the people a document is shared with. Whoever runs the server cannot read it, change it unnoticed, or give anyone a role, with the two exceptions in the next point but one (§3.2).

  • What a hostile server can do anyway. It can refuse service, delay or withhold messages, split a group into parts that no longer see each other’s edits, and see the metadata listed field by field in §9: who works with whom, when, how much, and from where.

  • The two exceptions, and the largest remaining risks. First, when you share with someone for the first time, your app has to ask the server who that person is. A hostile server could answer with a fake identity of its own (a “ghost user”, T23) and then read what you share with them. Comparing safety numbers prevents this; key transparency, a later addition, would make it detectable without effort. Second, the web app runs code that the server delivers, so a hostile server could deliver code that steals the keys of people using BayanDocs in a browser (T11). The desktop app does not have this weakness. But an account that is also used in a browser carries the risk into everything shared with that account, until its browser devices are revoked. The same weakness limits link sharing and passkey unlock in the browser.

  • Signing in is not the same as having the keys. Someone who takes over your sign-in (through your company’s identity provider, a stolen passkey, or an administrator) gets no keys. They cannot read documents, and any device they add stays powerless until one of your own devices approves it after comparing a code. Passkey unlock is the exception: an opt-in convenience that lets one passkey both sign in and unlock the keys (F10).

  • Four problems in the design, and their fixes. The analysis found four problems in ADR-0016 as it stands after its amendment of 2026-10-07:

    1. A user who loses every device can get their identity back with the recovery kit but cannot rejoin their documents. A document whose only owner loses every device can never change its members again.
    2. A user cannot leave a document unless an owner is online to remove them.
    3. Approving a new device must compare a code or scan a QR code in both directions, or the server could slip in its own device, or its own keys, at the same moment.
    4. Restoring the server from a backup would stall every document that changed since the backup.

    ADR-0029 and ADR-0030 fix them, and you accepted both on 2026-10-17. ADR-0030 also gives the details of the “two-phase acceptance” you chose on 2026-10-09 against members who stall a group (§7). It departs from that choice in three named points, which you accepted too: owners’ commits take effect at once; a commit that no other member checks within 72 hours is dropped without punishing its sender; and quarantine applies to the sender’s whole account.

  • What you decided. On 2026-10-17 you accepted both ADRs, including ADR-0030’s three departures, and decided against an expert cryptography review of ADR-0029’s constructions before Phase 3 builds them; the external audit before collaboration leaves beta (SEC-08) still reviews them (§13.2). On 2026-10-26 you confirmed six small corrections to those ADRs that the AC-4 review found, and accepted the rules of §13.4 for revoked devices when the server’s device list is stale, recorded as an amendment of ADR-0030. The question ADR-0016 left to SRV-003, whether the server may learn which updates are comments, needed no new decision: §7.4 confirms what you decided on 2026-10-09.

1. Scope, method and assumptions

1.1 Scope

v1 analyses the collaboration system end to end: signing in, accounts, devices and identity keys, sharing and roles, link sharing, synchronization online and offline, snapshots for new members, recovery, lost devices, notifications, administration and backups, the delivery of the web app’s code, and the compliance escrow member planned for Phase 4. It covers bayan-server, the collaboration code of bayan-core (bayan-sync, bayan-mls, bayan-crdt), the shells’ transport and key storage, and the services around them: the reverse proxy, the organization’s identity provider, the database, object storage and email.

Parsing documents, rendering, automation, local AI and the release pipeline keep the threats of v0 (T1–T4 and T14–T20 in §11) until their own work packages extend them.

Most of what v1 analyses is built in Phase 1 (SRV-101…SRV-105: identity, storage and relay foundations) and Phase 3 (collaboration); each flow in §6 names its phase. The server endpoints that exist today are only those of SRV-001 (/healthz, /readyz, /version and static files).

1.2 Method

  1. Diagrams. A context diagram of the whole system (§4.2), diagrams of the inside of the server (§4.3) and of a client (§4.4), and a sequence diagram for every flow (§6). In the sequence diagrams each box is a trust zone, so every arrow that leaves a box crosses a trust boundary (§4.1).
  2. STRIDE per element. §4.5 lists every element with the STRIDE categories that apply to its type (external entities: S and R; processes: all six; data stores: T, R, I and D; data flows: T, I and D) and where each is analysed; §4.6 analyses the infrastructure elements that no flow covers in full.
  3. STRIDE per flow. Every flow in §6 has a table with one row per threat: an ID such as F4.2, the category, the element attacked, the threat, its mitigations and what remains. Rows name the principal threat (T-number) they belong to.
  4. Principal threats. §11 keeps the numbers T1–T22 of v0, which other documents cite, and adds T23–T39. Numbers are never reused or renumbered.
  5. Status values. In place (built and tested), Planned (designed here or in an Accepted ADR, not built), Proposed (depends on a Proposed ADR), Residual (accepted in §12).

1.3 Assumptions

#Assumption
A1The reverse proxy that terminates HTTPS runs on the server’s host or its private network and belongs to the same operator (ADR-0028 §4). Between the proxy and the server, traffic is plain HTTP over loopback or a private network that nobody else can reach (§4.6).
A2Clients are BayanDocs Desktop (the native core), BayanDocs Web (the core in a Web Worker), or third-party clients speaking the published protocols. The server never relies on which one it talks to (SEC-13, T21).
A3Real documents use the hybrid post-quantum MLS ciphersuite once its code point is assigned (ADR-0016, amendment 2026-10-04); signatures are Ed25519; every cryptographic primitive comes from MLS and its providers (ADR-0016 Decision 12).
A4Operating systems and browsers keep their own promises: process isolation, keychains, the same-origin policy, and WebAuthn’s binding of passkeys to a domain. Someone who controls a user’s device while it is unlocked can read everything that user can; that is out of scope, as in v0.
A5The server’s clock decides only the server’s own deadlines (expiry, retention, the time limits of §7.2). No security decision depends on clients’ clocks agreeing with it.
A6Self-hosted and hosted deployments run the same software. In a self-hosted deployment the operator, the organization and its administrator are often the same people; that changes who the hostile operator would be, not the analysis.

1.4 Terms

TermMeaning
Account, deviceAn account is a user on one server. A device is one installation of a client for one account (a laptop’s desktop app, a browser profile). MLS members are devices; roles belong to accounts.
Owner, editor, commenter, viewerThe four roles of a document (COL-05). Owners manage members and roles; editors edit; commenters comment; viewers read.
Group, epoch, commitOne MLS group per document. Every change of membership or keys is a commit, which starts a new epoch with new keys (glossary: Epoch (MLS)).
Path secretThe fresh secrets a commit encrypts to the other members, each to their own part of the group; members who cannot decrypt theirs cannot follow the commit.
Tag, MACA message authentication code: a short value computed with a secret key that proves a message was not changed. MLS commits carry a membership tag and a confirmation tag computed with the epoch’s secrets, which the server does not have.
Public state, rosterWhat the server knows about a group without any key: the ratchet tree (every member device’s credential and public keys) and the group context, which holds the roster of accounts and their roles (ADR-0016, amendment 2026-10-07).
ValidatorThe part of the server that follows each group’s public state and refuses commits and updates that break the role policy (SRV-002).
Ed25519, HKDF, AEAD, HPKEThe building blocks MLS’s providers offer: Ed25519 makes and checks signatures; HKDF derives keys from a secret; an AEAD encrypts and authenticates data under a key; HPKE encrypts to someone’s public key. The hybrid post-quantum ciphersuite uses an HPKE whose key exchange combines ML-KEM-768 with X25519.
Identity key, device-signing key, device keyThe three signing keys of an account (ADR-0029, §5.1): the identity key says who the user is, the device-signing key says which devices belong to the user, and each device signs its own messages with its device key.
Device certificate, device listA device certificate is the device-signing key’s signature over a device’s key; the device list is the user’s signed, numbered list of current and revoked devices.
Recovery kit, secret store, key backupThe recovery kit is a random secret the user saves or prints. It opens the secret store, an encrypted copy of the account’s long-term keys kept on the server, and the key backup, the latest snapshot keys of the user’s documents, also encrypted (ADR-0029).
Key package, WelcomeA key package is a device’s one-time public key, published so that others can add the device to a group. A Welcome gives newly added devices the group’s secrets.
SnapshotAn encrypted copy of a document’s current state, for devices that join later and cannot read earlier epochs (ADR-0016 Decision 4).
Witness, two-phase acceptance, quarantineA commit from a member who is not an owner takes effect only after another account’s device (a witness) confirms that it can process it, and no witness refuses it. A refused commit is dropped, and its sender’s account is quarantined: it cannot commit in that group until an owner lifts the quarantine (ADR-0030, §7.2).
Resync, recovery joinTwo ways to enter a group with an MLS external commit, without another member’s help (ADR-0029). A device that fell behind resyncs: it rejoins with its own device key and replaces its own old place in the group (RFC 9420 §12.4.3.2). A new device of a user who has a role makes a recovery join: it joins, then removes the user’s lost devices with ordinary commits.
TOFUTrust on first use: a client remembers (“pins”) the identity key it first saw for a contact and warns if it ever changes.
Safety numberA fingerprint of two users’ identity keys, long enough that nobody can make a key that matches it, which they can compare in person or by QR code to make sure no one sits in between.
PKCE, HSTSPKCE (RFC 7636) ties a sign-in code to the app that started the sign-in, so a stolen code is useless. HSTS tells browsers to use HTTPS only for a site.
Harvest now, decrypt laterRecording encrypted data today in order to decrypt it once a large quantum computer exists (T22).

2. Assets

AssetWhy it mattersWhere it lives
Document content: text, comments, media, edit history, titles, folder structureThe core promise (COL-04)In plaintext only on members’ devices; on the server only as ciphertext
Keys: identity, device-signing, device, MLS epoch secrets, snapshot keys, link keys, recovery secret, key-backup keyWhoever has them can read or impersonateDevices (wrapped at rest, SEC-11); the user’s recovery kit; ciphertext copies on the server (§5.1)
Sign-in credentials and sessions: passkeys, identity-provider accounts, session tokens, invitation tokensAccess to the server and to the account’s metadata, not to contentAuthenticators, identity providers, clients; hashes on the server
Group public state: ratchet trees and rostersDecides who receives keys and what the server enforcesServer and every member device
Collaboration metadataWho works with whom, when and how much (§9)Server, reverse proxy, object storage, email relay
Account data: names, email addresses, organizationPersonal dataServer, identity provider
Security audit logEvidence after an incident; personal dataServer
Software integrity: binaries, the web app’s code, container imagesA compromised build defeats every other control (T11, T16, T17)Release pipeline, the server that serves the web app
Service availabilityTeams depend on it; an operator can always take it awayServer
Fidelity Lab private corpusThird-party documents (T19, unchanged)Lab storage

3. Adversaries and security goals

3.1 Adversaries

AdversaryCapabilitiesIn scope
Curious server operator, or someone who has copied the server’s dataReads everything the server stores and everything that passes the reverse proxyYes: content stays confidential; the metadata it sees is listed in §9
Malicious server operatorAlso changes stored data, sends anything to clients, serves modified web code, controls the reverse proxyYes, for the confidentiality and integrity of content shared between desktop devices of contacts that each client already knows. Accounts that also use the web app are exposed through their browser devices (T11), and first contacts through the directory (T23). Availability cannot be guaranteed
Network attackerObserves and changes traffic between clients and the proxy, and between the server and its servicesYes (TLS, ADR-0028)
Malicious or compromised collaborator, of any roleHolds the keys of the documents shared with them; sends crafted MLS messages, CRDT updates and snapshots, including commits that only some members can process; abuses sharing and linksYes
Former memberHad access and kept local copiesYes: no access to anything written after the removal
Thief of a deviceA locked device: in scope (encryption at rest, SEC-11). An unlocked device: out of scope as endpoint compromise, apart from revocation and post-compromise securityPartly
Sign-in attackerPhished or reused credentials, a compromised organization identity provider or its administrator, a compromised passkey sync account, a malicious server administratorYes: a sign-in alone must not give content or a trusted device (T27)
Holder of a linkAnyone who sees a link’s URLYes: links are bearer secrets by design (F5)
Malicious organization administratorConfigures policies, and from Phase 4 the escrow memberYes: escrow must be visible and bounded (§8)
Supply-chain attacker, malicious document author, malicious script or extensionAs in v0 (T1–T3, T14, T16, T17)Yes
Observer of traffic timing and sizesSees when and how much each client sendsPartly (§9.3)
Other software on a user’s deviceOther local apps, browser extensionsOut of scope as endpoint compromise, except where the protocol can avoid exposure (for example the desktop sign-in’s loopback redirect, F1)

3.2 Security goals

#Goal
G1Confidentiality. Only devices of a document’s current members can read its content, plus holders of a link to it and, where an organization’s policy adds one, its escrow member. The server never can (ADR-0015 Decision 6). Exceptions: ghost users at first contact (T23) and code served to browsers (T11).
G2Integrity and authenticity. Every change is signed by a member’s device. Nobody, the server included, can forge or alter an edit without the members noticing; replays have no effect.
G3Role enforcement. No device can do more than its user’s role allows, as honest clients see it, whatever the server does (ADR-0016 Decision 5).
G4Membership transparency. Nobody joins a group without a commit that every member sees and checks. For a contact whose identity a client already knows, nobody can add a device that the contact’s own native devices or recovery kit did not approve.
G5Forward secrecy and post-compromise security. Removed members and stolen device keys lose access to content written afterwards, as far as MLS provides it. The recovery kit, and passkey unlock where it is on, are the exception: whoever obtains them can read the latest snapshot of each document until the user makes a new kit and rotates the keys it protects (T28, F9).
G6Recoverability. A user who keeps their recovery kit regains their identity and access to their documents after losing every device.
G7Metadata minimization. The server stores only what it needs, only for as long as it needs it, and §9 lists all of it.
G8Availability, best effort. An operator can always deny service. A member must not be able to stall a group: at worst it can delay other members’ commits and leave members who were offline to repair their place in the group (§7).

4. System model

4.1 Trust boundaries

Boundaries 1–9 are those of v0; v1 adds 10–14.

#BoundaryCrossed by
1File bytes → engine parsersOpening documents (T1–T3)
2Engine ↔ shell (C ABI on desktop, worker messages on the web; engine protocol)Every message between interface and engine; transport and key storage requests from the engine (§4.4)
3Shell ↔ operating system or browserClipboard, fonts, URLs, files, keychains, IndexedDB
4Client ↔ server (HTTPS and WebSocket, through the reverse proxy)Every flow in §6
5Server ↔ database and object storageMetadata and ciphertext at rest (§9)
6Engine ↔ automation sandbox and extensionsScripts (T14, Phase 5)
7Engine ↔ AI sidecarLocal AI (T15, Phase 5)
8Build and release pipeline → usersBinaries, packages, containers, web assets (T16, T17)
9Web origin → browserThe web app’s code (T11, F16)
10Server ↔ OpenID Connect identity providerOrganization sign-in (F1)
11Client ↔ authenticatorPasskeys, platform authenticators and the services that sync passkeys (F2, F10)
12Server ↔ email relayNotification email (F14)
13Reverse proxy ↔ serverEvery request after TLS ends (A1)
14Administrator and operator ↔ serverAdmin console, command line, backups and restores (F17)

4.2 Context diagram

Each box is a trust zone; the numbers on the arrows are the boundaries of §4.1.

flowchart LR
  subgraph clientzone["Devices of a user (boundaries 2, 3, 11)"]
    user([User])
    desk["Desktop app<br/>Qt shell + core:<br/>keys, MLS, CRDT"]
    web["Web app<br/>React shell + core<br/>in a Web Worker"]
    store[("Key store and<br/>encrypted cache")]
    authr["Authenticator<br/>(passkey)"]
  end
  subgraph proxyzone["Operator's edge"]
    proxy["Reverse proxy<br/>TLS 1.3, X25519MLKEM768"]
  end
  subgraph serverzone["Server process"]
    srv["bayan-server"]
  end
  subgraph datazone["Operator's storage"]
    db[("Metadata database<br/>SQLite or PostgreSQL")]
    disk[("Blob store on disk")]
  end
  s3[("S3-compatible storage<br/>(optional, may be a third party)")]
  idp["Identity provider<br/>(organization, OIDC)"]
  smtp["Email relay<br/>(optional)"]
  others(["Other members' devices"])
  admin([Administrator's browser])
  operator([Operator's shell on the host])
  linkr([Link recipient])
  user --> desk
  user --> web
  desk --- store
  web --- store
  web -.->|"WebAuthn (11)"| authr
  desk -->|"HTTPS and WebSocket (4)"| proxy
  web -->|"HTTPS and WebSocket (4)"| proxy
  others -->|"HTTPS and WebSocket (4)"| proxy
  linkr -->|"link ID only; the key stays in the fragment (4)"| proxy
  admin -->|"admin console (4, 14)"| proxy
  proxy -->|"HTTP on loopback (13)"| srv
  srv -->|"web app code (9)"| proxy
  operator -->|"command line, backups, metrics (14)"| srv
  srv -->|"(5)"| db
  srv -->|"(5)"| disk
  srv -.->|"TLS (5)"| s3
  srv -.->|"TLS (10)"| idp
  srv -.->|"TLS (12)"| smtp

4.3 Inside the server

The boxes are parts of the one bayan-server binary (ADR-0015); only the middleware and the three /healthz, /readyz and /version routes exist today (SRV-001). Requests cross boundary 13 when they enter the process and boundary 5 when they reach the stores.

flowchart TB
  proxy["Reverse proxy"]
  subgraph process["bayan-server process"]
    mw["Middleware (SRV-001): request ID, content-free log line,<br/>security headers, timeout, body size limit"]
    signin["Sign-in and sessions<br/>OIDC, passkeys, desktop sign-in"]
    apis["HTTP APIs<br/>accounts, devices, directory, recovery data,<br/>key packages, blobs, links, notifications"]
    adminapi["Admin console API"]
    ws["WebSocket gateway<br/>device authentication, backpressure"]
    seq["Sequencer<br/>per-group order, fan-out"]
    val["Validator<br/>public state, role policy,<br/>two-phase acceptance"]
    notif["Notifier"]
    metrics["Metrics on a private listener"]
  end
  subgraph stores["Storage (boundary 5)"]
    db[("Metadata database,<br/>message log, audit log")]
    blobs[("Blob store")]
  end
  oplog[("Operational log, stdout")]
  smtp["Email relay"]
  monitor([Operator's monitoring])
  proxy -->|"boundary 13"| mw
  mw --> signin
  mw --> apis
  mw --> adminapi
  mw --> ws
  ws --> seq
  seq --> val
  signin --> db
  apis --> db
  adminapi --> db
  val --> db
  seq --> db
  apis --> blobs
  apis --> notif
  seq --> notif
  notif -->|"boundary 12"| smtp
  mw --> oplog
  monitor -->|"boundary 14"| metrics

4.4 Inside a client

The engine never opens sockets or reads keychains itself (ADR-0012 Decision 5): it asks the shell through host services. On the web the engine, and with it every key, lives in the Web Worker; the page’s main thread never needs key material (F16).

flowchart LR
  subgraph shellzone["Shell (boundaries 2 and 3)"]
    ui["Interface"]
    net["Transport host service<br/>WebSocket and HTTPS"]
    ks["Key-store host service<br/>OS keychain or IndexedDB"]
  end
  subgraph enginezone["Engine: bayan-core, in a Web Worker on the web"]
    sync["bayan-sync<br/>log, offline queue, snapshots"]
    mls["bayan-mls<br/>groups, credentials, policy"]
    crdt["bayan-crdt<br/>isolated import with limits"]
  end
  cache[("Encrypted local cache<br/>and offline queue")]
  ui <-->|"engine protocol"| sync
  sync <--> mls
  sync <--> crdt
  sync <-->|"host.net.send and net.received:<br/>opaque frames"| net
  mls <-->|"wrapped keys only"| ks
  sync --- cache

4.5 Elements and the STRIDE categories that apply

IDElementTypeCategoriesMain concernAnalysed in
E1UserExternal entityS, RApproving the wrong device; losing the recovery kit; denying actionsF3, F9, F10, F11, T24, T28
E2Other members’ clientsExternal entityS, RCrafted messages; commits only some members can process; denying authorshipF4, F6, F8, F12, T7, T8, T25
E3Identity provider (OIDC)External entityS, RA compromised provider signs in as usersF1, §4.6, T27
E4Authenticator and passkey sync providerExternal entityS, RA synced passkey gives sign-in, and with passkey unlock keysF2, F10, T28
E5Administrator and operatorExternal entityS, RAbusing admin powers; denying itF17, T27
E6Link recipientExternal entityS, RAnyone with the URL readsF5, T31
E7Email relayExternal entityS, RSees who is notified about whomF14, §4.6, T37
E8Escrow officers (Phase 4)External entityS, RReading beyond policyF15, §8
P1Shell (desktop or web)ProcessS, T, R, I, D, ETransport and key storage; on the web, code served by the serverF6, F16, T4, T11
P2Engine: bayan-sync, bayan-mls, bayan-crdtProcessS, T, R, I, D, EEvery client-side check of §7.1; crafted input; keys in memoryF3–F13, T7, T8, T36
P3Reverse proxyProcessS, T, R, I, D, ETLS; forwarded client addresses; access logs that hold paths and addresses§4.6, F1, F16, §9.1, T12, T22
P4Sign-in and sessionsProcessS, T, R, I, D, EAccount takeover; session theftF1, F2, T12, T27, T32
P5Account, device, directory and recovery APIsProcessS, T, R, I, D, EGhost users and devices; stale device listsF3, F4, F9–F11, T23, T24, T30
P6WebSocket gateway and sequencerProcessS, T, R, I, D, EFlooding; forks; device authenticationF6, F7, T13, T26
P7ValidatorProcessS, T, R, I, D, EAccepting what clients refuse, or the reverseF4, F12, §7, T25
P8Blob and link APIsProcessS, T, R, I, D, EStorage abuse; link guessingF5, F8, T13, T31
P9NotifierProcessS, T, R, I, D, ELeaking metadata; phishingF14, T37
P10Admin console, command line and backup toolsProcessS, T, R, I, D, EAdmin takeover; restore rollbackF17, T33
D1Client key store, cache and offline queueData storeT, R, I, DTheft of a locked device; recordingsF7, F11, T20, T36
D2Metadata databaseData storeT, R, I, DMetadata exposure; tampering equals a malicious server§9, F17
D3Message logData storeT, R, I, DCiphertext kept long (T10, T22); readable commits; sender metadataF6, F7, §9.1
D4Blob storeData storeT, R, I, DSubstitution; deletion; sizesF5, F8
D5Security audit logData storeT, R, I, DPersonal data; evidence integrityF17, §9.1
D6Operational logs and metricsData storeT, R, I, DIdentifiers leaking into logs or metric labels§9.1, T20
D7BackupsData storeT, R, I, DTheft; restore rollbackF17, T33
D8Reverse-proxy and object-storage logsData storeT, R, I, DAddresses, paths and link IDs outside the server’s control§4.6, §9.1
DF1Client ↔ reverse proxyData flowT, I, DTLS, harvest now decrypt laterF1–F16, T12, T22
DF2Reverse proxy ↔ serverData flowT, I, DPlain HTTP on a private network§4.6, A1
DF3Server ↔ identity providerData flowT, I, DToken exchange, the provider’s signing keysF1, §4.6
DF4Server ↔ database and object storageData flowT, I, DTLS with verification (ADR-0028); a third-party object store§4.6, F17
DF5Server ↔ email relayData flowT, I, DContent-free notices; TLS to the relay§4.6, F14
DF6Engine ↔ shellData flowT, I, DMalformed messages; key material crossing to the shell§4.6, T4, T36
DF7Client ↔ authenticatorData flowT, I, DPRF output exposureF2, F10
DF8Delivery of the web app’s codeData flowT, I, DModified codeF16, T11

4.6 Infrastructure elements

No flow covers these elements in full, so their threats are listed here.

#STRIDEElementThreatMitigationRemaining risk
IE1SP3, DF2A client fakes its address with a forwarded-for header, defeating the rate limits per address (F1.10, F5.3, F5.10) and the audit log’s optional addressThe server reads forwarded headers only from the configured proxy’s address and ignores them from anywhere else; the proxy overwrites, never appends to, the header it sets—
IE2S, IDF2Another host on a shared private network reaches the server’s plain-HTTP port directlyThe server listens on loopback when the proxy is on the same host, or on a network only the proxy can reach; the deployment guide says so (SRV-105)A misconfigured network
IE3IP3, D8The proxy’s access logs keep client addresses, user agents and paths, which may hold link IDs and blob IDsLogs switched off or kept without paths and addresses (§9.1); identifiers in request bodies and frames rather than paths (SRV-104)Depends on the operator
IE4T, IDF1, P3Weak TLS settings at the proxy, or no HSTSTLS 1.3 with X25519MLKEM768 and HSTS, as the deployment guide requires (ADR-0028 Decision 4)—
IE5DP3The proxy does not pass WebSocket upgrades, or closes idle connections the wrong way roundThe proxy requirements of ADR-0028 Decision 4 and the deployment guide—
IE6T, DDF3The identity provider’s signing keys change, or the provider is unreachableKeys fetched over verified TLS, cached for a bounded time, fetched again when a token names an unknown key; sign-in fails closed while the provider is downOrganization sign-in depends on the provider’s availability
IE7T, IDF4Database or object-storage traffic read or changed on the networkTLS with certificate and host-name verification (ADR-0028); credentials from files; a database user with only the rights it needs—
IE8I, DDF4An S3-compatible store run by a third party sees blob IDs, sizes and times, and can delete or withhold blobsBlobs are ciphertext with random IDs; members detect substituted blobs (F8.1); operators choose the providerThe provider’s availability and the metadata of §9.2
IE9T, IDF5The connection to the email relay is downgraded to plain text, or the relay is not authenticatedTLS required to the relay (implicit TLS, or STARTTLS that fails without TLS) with certificate verification; relay credentials from files—
IE10T, DDF6A compromised shell component or script sends the engine malformed messagesSchema checks, limits and no panics across the boundary (T4, CORE-007)—
IE11IDF6Key material crosses from the engine to the shell or into recordingsKeys leave the engine only wrapped, through the key-store host service; recordings exclude MLS state (T36, §13.3)—

5. Keys, credentials and sessions

The flows in §6 rely on the key design below. ADR-0016 Decisions 6 and 7 fix its principles; the details are decided in ADR-0029.

5.1 Keys

KeyWhat it isPrivate part kept byChanges whenProtects
Identity keyEd25519 signing key, one per accountOnly the secret store, and briefly the memory of a device that uses itOnly if lost or exposed; every contact then sees a key changeWho the user is: it certifies the device-signing key
Device-signing keyEd25519 signing key, one per account, certified by the identity keyThe user’s native (desktop) devices, wrapped at rest (SEC-11), and the secret store; never a browser deviceAfter a device that held it is lost, and after any suspected exposure (rotating it needs the recovery kit or passkey unlock)Which devices belong to the user: it certifies device keys and signs the device list
Device keyEd25519 signing key, one per device, certified by the device-signing keyThat device onlyNever; a new device gets a new keyThe device’s MLS signatures, and, under a separate signing label, proof of the device to the server
MLS keysLeaf, path and init keys (HPKE, with the hybrid post-quantum KEM in production) and each epoch’s secretsThe device, inside the engineEvery commitDocument content (ADR-0016)
Snapshot keyOne key per snapshot for the group’s AEAD (ADR-0016, amendment 2026-10-07, design detail 2)The members who received itEach snapshotOne snapshot
Link key and link signing keyA random 256-bit AEAD key and an Ed25519 key pair, per linkThe devices of the owner who created the link (in that owner’s encrypted settings), and the link’s URL fragment, which holds the AEAD key and the public signing keyEach linkThe snapshot keys wrapped for that link, and their authenticity (F5)
Recovery secretRandom 256-bit value, shown as the recovery kit (words and a QR code)The user, on paper or in a password manager; never a device or the serverWhen the user makes a new kitThe secret store
Secret-store keyDerived from the recovery secret with HKDFDerived when needed, then erasedWith the recovery secretIdentity key, device-signing key and key-backup key, sealed with an AEAD
Key-backup keyHPKE key pair with the production suite’s hybrid KEMPrivate part: secret store. Public part: every device of the userWith every new recovery kit, and after any suspected exposureThe latest snapshot key of each of the user’s documents (F8, F9)
Passkey unlock keyDerived with HKDF from a passkey’s PRF output (opt-in)Derived when neededPer passkeyA copy of the recovery secret wrapped for that passkey (F10)
Session tokenRandom 256-bit secretThe client; the server stores only a hashEach sign-in; rotatedServer access for one device

All of this uses primitives that OpenMLS’s provider already offers (Ed25519, HKDF, AEAD, HPKE including the hybrid KEM), so it adds no new primitive (ADR-0016 Decision 12); the constructions that combine them (certificates, the secret store, the key backup, passkey wrapping, the approval exchange, link signing and the device’s proof of possession) are reviewed by the external audit before collaboration leaves beta (SEC-08); the owner decided on 2026-10-17 against a separate expert review before Phase 3 builds them (ADR-0029). Nothing is protected by a password or passphrase: that would need a password-hashing function such as Argon2id, which MLS’s providers do not include, so the recovery kit is always a generated secret. The account’s root secrets (secret store, passkey wraps) and link keys use 256-bit AEAD keys, a margin for keys that guard everything else for years; document data, snapshots and backup entries use the AEAD of the group’s ciphersuite, as ADR-0016 decided (AES-128-GCM in the hybrid suite, which quantum computers do not meaningfully weaken).

After an exposure. Whoever obtains the recovery kit, or a passkey with passkey unlock, can open the secret store and, with the server’s stored data alone, the latest snapshot of every document, including later ones, until the user acts (T28). The response is: a new recovery kit, a new key-backup key (devices seal the current snapshot keys to it again), a new device-signing key with the remaining devices certified again, and, if a hostile party may have kept an older secret store, a new identity key, which contacts see as a change. Passkey unlock is switched off for an exposed passkey. A new kit and a new key-backup key after every recovery are good practice, because a recovery may run on a device or in a browser the user does not fully trust.

5.2 Credentials and the directory

  • Credentials. Every MLS leaf carries a credential that names the account and the device and carries the chain that proves it: the device certificate (signed by a device-signing key), that device-signing key’s certificate (signed by the identity key) and the identity public key. A chain is valid when it leads to the account’s identity key and names a device that the account’s latest signed device list lists as current; a certificate made by an earlier device-signing key stays valid for a device that the latest list still lists, so a rotation does not break existing places in groups. Devices replace their credential in each group with their next self-update. The server’s validator and every client check the chain whenever a leaf appears: in key packages, commits, Welcomes, resyncs and recovery joins. The encoding, a private-use credential type or X.509, is left to the Phase-3 work package (ADR-0029). The spike’s basic credentials naming user/device (SRV-002) are replaced.
  • Directory. For each account the server publishes the identity public key, the current device-signing certificate and the signed device list, which carries a version number, the current devices and the revoked ones, and records which device made each change. Clients pin a contact’s identity key the first time they see it (TOFU), refuse a device list older than one they have already seen, and stop with a warning before sending anything new to a contact whose identity key has changed. Users can compare safety numbers or scan each other’s QR codes and then see the contact as verified (ADR-0016 Decision 6).
  • Watching one’s own devices. Every device of a user shows each change of the user’s device list, who made it and when. A device that finds a change attributed to itself that it did not make raises an alarm and starts the response of F11, because only a stolen device-signing key can produce one.
  • Personal data in groups. A user’s document index (titles and folders) and settings (pins, verifications and device names) are end-to-end encrypted groups of the user’s own devices, like documents with one account. A new or recovered device therefore inherits the user’s pins and does not start trusting the directory afresh.
  • What the directory can and cannot do. The server can withhold or delay a newer device list, and it can answer a lookup for someone a client has never seen with an identity of its own (T23). It cannot add a device to an identity a client has pinned or verified (G4).

5.3 Sessions and device authentication

  • Every session belongs to one device. On the web the session is a cookie with the __Host- prefix, Secure, HttpOnly and SameSite=Strict. The desktop app signs in through the system browser and receives its token over a loopback redirect protected by PKCE (RFC 8252, RFC 7636), never through an embedded browser view.
  • Proof of possession. Each WebSocket connection, and each security-relevant HTTP request (revoking, deleting, approving), carries a signature by the device key over a fresh challenge from the server, under a signing label of its own, as DPoP (RFC 9449) does for OAuth tokens. A stolen token without the device key cannot act as the device (T32).
  • Unapproved devices are powerless. A device that has signed in but is not yet approved (F3) can only finish its own approval or a recovery (F9). It cannot read group data, claim key packages, publish anything, end other sessions or delete anything. This is what keeps a sign-in takeover from becoming more (T27).
  • Re-authentication. Adding a sign-in method, changing the email address, deleting the account, recovery joins and every admin action need a sign-in from the last few minutes; the server checks this, because clients cannot.

6. Flows

Every flow has a sequence diagram and a STRIDE table. Paths such as /auth/oidc/start are illustrative; SRV-105 publishes the real API in specs/protocols/server-api.md.

6.1 F1: Signing in with OpenID Connect

Phase 1 (SRV-101); used by collaboration from Phase 3. Organizations sign in through their own identity provider. The server is an OpenID Connect relying party using the authorization code flow with PKCE. The web app follows the redirects in the browser. The desktop app opens the system browser and receives the result on a loopback address (RFC 8252); embedded browser views are not used. A sign-in proves who the user is to the server; it never unlocks keys (ADR-0015 Decision 4).

The state value must be bound to the browser that started the sign-in. The provider’s redirect back is a navigation from another site, on which browsers do not send SameSite=Strict cookies. The binding therefore uses a short-lived pre-sign-in cookie of its own (__Host- prefix, Secure, HttpOnly, SameSite=Lax, 10 minutes), and the provider returns the code in the query of a plain redirect (response_mode=query).

sequenceDiagram
  autonumber
  box Device of the user
    participant App as Desktop app
    participant Br as Browser
  end
  box Server host
    participant S as bayan-server
    participant DB as Metadata database
  end
  box Organization
    participant IdP as Identity provider
  end
  App->>Br: desktop only: open the server's sign-in page with a loopback address and a PKCE challenge
  Br->>S: GET /auth/oidc/start
  S->>DB: keep state, nonce and PKCE verifier (single use, 10 minutes)
  S-->>Br: pre-sign-in cookie (SameSite=Lax), redirect to the identity provider (exact redirect URI, state, nonce, code challenge)
  Br->>IdP: the user signs in with the provider's own factors
  IdP-->>Br: redirect back with code, state and iss in the query
  Br->>S: GET /auth/oidc/callback with the pre-sign-in cookie
  S->>S: check state against the cookie, and iss. Single use
  S->>IdP: exchange the code (TLS, client authentication, PKCE verifier)
  IdP-->>S: ID token
  S->>S: check signature, algorithm, issuer, audience, expiry, nonce
  S->>DB: find the account by issuer and subject, create a session for this device
  S-->>Br: web: session cookie. Desktop: redirect to 127.0.0.1 with a one-time code
  Br->>App: desktop only: one-time code on the loopback address
  App->>S: desktop only: code, PKCE verifier and the device's public key
  S-->>App: device session token
#STRIDEElementThreatMitigationRemaining risk
F1.1SP4, E3Someone signs in as the user by phishing them at the identity provider, or by controlling the provider or its administration (T27)Sign-in gives no keys; a new device stays powerless until one of the user’s devices approves it (F3, §5.3); the user’s devices are told about new sessions; audit logThe attacker sees the metadata the account can see and can ask the user’s devices for an approval the user might wrongly give
F1.2SP4Account linking by email address: an account at another provider, or an unverified address, takes over an existing accountAccounts are found by issuer and subject, never by email address; linking a second provider needs a session of the account—
F1.3S, TP4Login cross-site request forgery, or an attacker’s code injected into the victim’s sessionstate bound to the browser by the pre-sign-in cookie and single use; PKCE; codes single use—
F1.4SP4Mix-up between several configured identity providersA redirect URI per provider and the iss check of RFC 9207, as RFC 9700 recommends—
F1.5TP4Forged or replayed ID tokenSignature checked against the provider’s published keys, fetched over verified TLS (ADR-0028, §4.6 IE6); an allowlist of algorithms (no none); nonce, audience, issuer and expiry checkedA compromised provider can sign anything (F1.1)
F1.6SP4Open redirect: a return address sends the browser and its code elsewhereOnly relative return paths, and for the desktop only loopback addresses—
F1.7SDF1, P1Desktop: another local program listens on the loopback port or intercepts the redirectPKCE binds the code to the app that started the sign-in; a fresh port each time; the code is useless without the verifier (RFC 8252 §8.1)Malware on the device is out of scope
F1.8ID6Codes, tokens or identity claims end up in logsContent-free log lines with route templates only (SRV-001); the ID token is not stored, only issuer, subject, display name and email address—
F1.9RD5A user, or an administrator, denies a sign-in or a sessionSecurity audit log: sign-ins, failures, sessions created and ended (§9.1)—
F1.10DP4Flooding the sign-in endpoints or the store of pending sign-insRate limits per address (with forwarded addresses trusted only from the proxy, §4.6 IE1) and per account; pending sign-ins expire and are boundedDistributed floods need the proxy’s own limits
F1.11EP4Group claims of the identity provider turned into server administration rightsServer roles are assigned in the server, or from one documented, explicitly configured claim; document roles live only in MLS rosters—
F1.12IP1The session cookie is stolen by script injection or on the network (T32)HttpOnly, Secure, SameSite=Strict, __Host- prefix; CSP and Trusted Types (SEC-10); TLS, and HSTS set at the proxy—
F1.13SD1The desktop token is copied from the device (T32)Kept in the OS keychain; useless without the device key (§5.3)An unlocked, compromised device is out of scope
F1.14EP4Session fixationA new session identifier after every sign-in; the old one ends—

6.2 F2: Passkey registration and sign-in

Phase 1 (SRV-101). Accounts that do not use an organization’s provider sign in with passkeys (WebAuthn). New self-hosted servers keep registration closed: a new account starts from an invitation (OPS-09). The invitation link carries its token in the URL’s fragment, which browsers never send to servers, so it reaches neither the proxy’s logs nor email scanners that open links; the page reads it and spends it only in a POST request. On the desktop, passkeys are used through the system browser, because a platform’s passkey interface for apps may refuse a self-hosted server’s domain: on Apple’s platforms, an app can use passkeys only for domains associated with it (Apple’s “Supporting passkeys” documentation).

sequenceDiagram
  autonumber
  box Device of the user
    participant Br as Browser
    participant A as Authenticator
  end
  box Server host
    participant S as bayan-server
    participant DB as Metadata database
  end
  Note over Br,DB: Registration, only with an invitation (OPS-09)
  Br->>Br: read the invitation token from the link's fragment
  Br->>S: POST registration options with the token
  S->>DB: token hash known, unused, not expired. Keep a challenge
  S-->>Br: options: RP ID is the server's domain, challenge, user verification required, no attestation
  Br->>A: create a passkey (the user verifies with PIN or biometrics)
  A-->>Br: public key and credential ID
  Br->>S: registration result
  S->>S: check challenge, origin, RP ID hash, user-verified flag
  S->>DB: create the account, store the credential, mark the invitation used
  Note over Br,DB: Sign-in
  Br->>S: sign-in options
  S-->>Br: challenge (single use, 5 minutes)
  Br->>A: get an assertion (the user verifies)
  A-->>Br: signature over the challenge
  Br->>S: assertion
  S->>S: check signature, challenge, origin, RP ID, user-verified flag, counter
  S->>DB: session for this device
  S-->>Br: session cookie, or a loopback code for the desktop app (F1)
#STRIDEElementThreatMitigationRemaining risk
F2.1SP4Phishing and relaying of sign-insPasskeys are bound to the RP ID; the browser checks the origin; the server checks origin and RP ID hash—
F2.2SP4, E1Someone intercepts an invitation (for example in email) and registers in place of the inviteeInvitation tokens are single use, expire after 7 days, travel in the fragment and are stored only as hashes; the inviter sees who accepted; collaborators verify newcomers’ safety numbers (T23)Without verification, an intercepted invitation creates a ghost user (T23)
F2.3SP4Replayed assertionSingle-use challenges with expiry; the signature counter is checked where the authenticator keeps one (synced passkeys usually report 0)—
F2.4EP4Registration without an invitation on a closed serverThe server refuses (OPS-09)—
F2.5ID2Attestation reveals the user’s authenticator modelAttestation “none”; the authenticator model (AAGUID) is not stored—
F2.6SP4, E5All passkeys lost: sign-in recoveryAn administrator issues a new invitation bound to the existing account; the event is audited and shown on the user’s devices; keys are recovered separately (F9)A malicious administrator can do the same and take over the sign-in, but not the keys (T27)
F2.7IP4Probing which accounts existDiscoverable credentials and the same answer whether or not an account exists—
F2.8RD5Denying a registration or sign-inAudit log—
F2.9EP4Weak authenticators without user verificationThe user-verified flag is required—
F2.10DE4The user loses their only passkeyEncourage a second passkey; administrator re-invitation (F2.6)—
F2.11IDF7A hostile server’s page asks the authenticator for the PRF output (F10)Only matters for users who enabled passkey unlock (F10, T28)See F10
F2.12DP4A link scanner opens an invitation link and uses it upThe token is in the fragment and spent only by the page’s POST request—

6.3 F3: Device enrollment, approval and cross-signing

Server groundwork in Phase 1 (SRV-101); cross-signing in Phase 3; ADR-0029. A new device makes its own device key and a one-time transfer key, signs in and registers as pending. One of the user’s approved native (desktop) devices then approves it, and the approval must be authenticated in both directions:

  • The approving device must approve this device, not one the server slipped in at the same moment.
  • The new device must accept its user’s identity key and key-backup key only from that approving device, not from the server.

So the two devices first run an out-of-band step. Either the user scans, with the approving device, a QR code that the new device shows, holding the fingerprint of the new device’s keys and a one-time secret. Or, where no camera helps, both devices show the same short code produced by a commit-then-reveal exchange between them. The approving device then certifies the new device key with the device-signing key and sends the approval: the certificate, the new device list, the identity key and the key-backup public key. It seals the approval to the transfer key with HPKE using the hybrid post-quantum KEM, and authenticates it with an AEAD key derived from a secret the server cannot know: in the QR path the one-time secret in the QR code; in the short-code path the secret that the HPKE encapsulation establishes, which the short code authenticates because both devices compute the code over the whole exchange (both devices’ public keys, the commitments and the encapsulation), so that a server in the middle produces different codes on the two devices. Such a server gets one guess per exchange: a six-digit code gives it one chance in a million, and the devices stop after a few failed comparisons. Only a native device receives the device-signing key in that sealed approval.

Browser devices never hold the device-signing key, so web code that a hostile server serves once (T11) cannot certify devices or sign device lists. A user without a native device approves new devices with the recovery kit instead (F9). Administrators can additionally require their own approval (COL-13); that is a gate on the server, not a certificate. Once approved, the new device joins the user’s groups: the user’s other devices add it with own-device adds, or it makes recovery joins itself (F9). The first device of an account creates the identity key, the device-signing key, the key-backup key and the recovery kit (F9).

sequenceDiagram
  autonumber
  box New device
    participant N as New device's engine
  end
  box Server host
    participant S as bayan-server
  end
  box Approved native device of the same user
    participant O as Approved device's engine
  end
  N->>N: create a device key and a one-time transfer key (hybrid HPKE)
  N->>S: sign in (F1 or F2), register as pending: public keys, declared client application
  S->>S: pending: no group data, no key packages, no deletions
  S-->>O: content-free notice: a device is waiting for approval
  Note over N,O: Out of band: O scans N's QR code (fingerprint of N's keys and a one-time secret), or both show the same short code, computed over the whole exchange
  O->>S: fetch the pending device's public keys
  O->>O: compare them with what was scanned or compared, refuse on any difference
  O->>O: certify N's device key, add N to a new version of the device list
  O->>S: approval sealed to N's transfer key and authenticated with the QR code's secret or the secret of the exchange the short code covers: certificate, device list, identity key, key-backup public key, and for a native N the device-signing key
  S->>S: check signatures against the user's identity key, apply the administrator approval policy (COL-13)
  S-->>N: the sealed approval
  N->>N: open it, check its authentication, then check the chain up to that identity key
  O->>S: own-device adds of N to the user's groups, or N makes recovery joins (F9)
#STRIDEElementThreatMitigationRemaining risk
F3.1SP5, E1The server registers its own pending device and the user approves it (T24)Approval always compares the new device’s keys out of band, by QR code or short code; a bare “Approve?” button is never offered (ADR-0029); the short code covers the whole exchange, so a server in the middle gets one guess per attempt (one in a million for six digits), and the devices stop after a few failed comparisonsA user who approves without comparing, or who scans a code shown by someone else’s device, approves the wrong device
F3.2SP5The server hands the new device its own identity key and key-backup key, so that the device trusts the server’s identity and seals its snapshot keys to the serverThe approval is authenticated with a key the server cannot know (the QR code’s one-time secret, or the secret of the exchange that the short code covers), and the new device accepts its user’s identity key and key-backup key only from it (HPKE alone would not do: anyone can seal to a public key)—
F3.3SP4, P5A sign-in attacker (F1.1) registers a pending device (T27)Pending devices are powerless; the user’s devices show each request with its client application and time; pending requests expire after 24 hours; administrators can require their approval too—
F3.4TDF1The server changes the certificate, the device list or the sealed approvalSignatures by the device-signing key; the approval’s authentication; the new device checks the chainThe server can withhold them (availability)
F3.5IDF1, D1The device-signing key leaks in transit or at rest, or is recorded for decryption later (T22)Sealed with HPKE using the hybrid post-quantum KEM to the new device’s one-time key; wrapped at rest with a keychain key (SEC-11); kept only on native devices—
F3.6EP5A device of one account certifies a device for another accountCertificates name the account; chains must end at that account’s identity key; the validator and every client check—
F3.7SP5A stale device list makes a revoked device look valid (T30)Device lists carry versions; clients refuse an older version than one they have seen; removing a device from groups is done with MLS commits (F11), which do not depend on the listThe server can delay a newer list from reaching clients that have not seen it
F3.8RD5Nobody can tell which device approved whichThe device list records the approving device and the time; audit log—
F3.9DP5Flooding a user’s devices with approval requestsRate limits per account; requests expire—
F3.10EP5The administrator’s restriction of client applications (COL-13) is relied on as a security controlDocumented as a policy for well-behaved software only; authorization never depends on the client software (SEC-13)—
F3.11ID2Device names and declared client applications reveal the user’s device inventoryDevice names are end-to-end encrypted in the user’s settings; the server stores only the declared client application and coarse dates (§9.1)The declared client application is visible to the server
F3.12ED1A stolen native device holds the device-signing key and certifies more devices, or signs device lists that undo its revocation (T39)After a loss, rotate the device-signing key (F11), which needs the recovery kit or passkey unlock; every device alarms on changes attributed to itself that it did not make (§5.2); members accept new devices only if the latest list names themThe window between the theft and the rotation
F3.13EDF8Web code that a hostile server serves once takes the keys of a browser device (T11)The browser device holds no device-signing key, so it cannot certify devices or sign lists, and revoking it from another device ends its accessUntil it is revoked, a stolen browser device’s keys read what that device can read, including later shares to its user (§12)

6.4 F4: Sharing, role changes, removal and leaving

Phase 3. To share, an owner’s client looks the recipient up (by exact email address or account ID), checks the recipient’s identity against its pin, claims one key package per recipient device, and sends one commit that adds the devices and gives the recipient a role in the roster. The validator checks it (ADR-0016, amendment 2026-10-07, design detail 3) and relays it; the new devices receive a Welcome, fetch the ratchet tree from the server and join in one storage transaction. An editor’s client then sends them a snapshot (F8). A role change is an owner’s commit with a new roster; a removal is an owner’s commit that removes every device of the user and their roster entry. To leave, a user sends a leave request inside the group from one of their devices. Members authenticate it like any MLS message; its kind is visible to the server, which accepts it from every role, viewers included, stops delivering to all of the user’s devices at once and accepts nothing more from that user in that group. An owner’s client then commits the removal; the last owner must first make another member an owner (ADR-0030). Sharing with someone who has no account yet records a pending share inside the document and sends an invitation (F2).

sequenceDiagram
  autonumber
  box Owner's device
    participant O as Owner's engine
  end
  box Server host
    participant S as bayan-server
  end
  box Recipient's devices
    participant R as Recipient's engine
  end
  box Other members
    participant M as Members' engines
  end
  O->>S: look up the recipient by exact email address or account ID
  S-->>O: identity key, device-signing certificate, signed device list
  O->>O: check the chain, compare the identity with the pinned or verified key, warn at first contact or on a change
  O->>S: claim one key package per recipient device
  S-->>O: key packages
  O->>O: check that each key package's credential chains to the recipient's identity
  O->>S: commit adding the devices with the recipient's role in the roster, and a Welcome without the tree
  S->>S: validator: committer is an owner, roster rules, public framing, current epoch
  S-->>M: commit
  M->>M: check the commit against the policy, merge it
  S-->>R: Welcome
  R->>S: fetch the ratchet tree
  R->>R: check the tree hash, that an owner added it and the roster, then join in one transaction
  Note over O,M: Role change: an owner's commit with a new roster. Removal: an owner's commit removing every device of the user. Leaving: a leave request in the group, accepted from every role, then an owner's commit
#STRIDEElementThreatMitigationRemaining risk
F4.1SP5Ghost user: the server answers the lookup with an identity it controls, and then reads what is shared with it (T23)Pinned identities (TOFU) with a blocking warning on any change; safety numbers and QR codes; a visible “verified” markAt first contact without verification the server can impersonate the recipient and read what is shared; key transparency is to be evaluated at the Phase-3 gate (§13.3)
F4.2SP5The server hands out a key package of a device it controlsKey packages are signed by device keys whose certificates must chain to the recipient’s identity; the owner’s client checksOnly a ghost user (F4.1) gets past
F4.3EP7A non-owner adds another user, changes roles, or adds an external senderRefused by the validator and by every client (ADR-0016 design detail 3; tested in SRV-002)—
F4.4EP7The server grants a role or adds a member by itselfImpossible without an owner’s signature on a commit; every client checks every commit and Welcome—
F4.5TP6The server tampers with the ratchet tree given to a newcomerThe newcomer checks the tree against the hash in the signed group information of the Welcome; the join is one transaction, so a bad tree cannot use up the key package (ADR-0016 requirement 1)The server can keep sending bad trees (availability)
F4.6DP6The server withholds the Welcome or the commitThe owner sees no join and no snapshot request; retryAvailability
F4.7ID2, D3The server learns the social graph: who shares with whom, with which role, and when; it reads every commit, which is in public framing (T9)Unavoidable for role enforcement (ADR-0016 Decision 9); listed in §9Residual
F4.8DP5Someone claims all of a user’s key packages so that nobody can add them (T34)Only approved devices may claim; limits per claimer and per target; key packages expire; devices top upA determined member can delay additions
F4.9IP5The lookup is used to find out which email addresses have accounts (T38)Exact-match lookups only, by approved devices, with rate limits; optionally limited to the organization; no listing or prefix searchSomeone who already knows an address can test it
F4.10EP2A removed member keeps readingMLS removal moves the group to keys the removed devices do not have; the server stops delivering to them; later snapshots use new keys (tested in SRV-002)What they already held stays with them (v0 residual)
F4.11DP7A user cannot leave without an owner, because a member cannot commit its own removal (RFC 9420 §12.2) and standalone proposals are refusedA leave request, authenticated to members and accepted by the server from every role, viewers included; the server stops delivery to the user’s devices at once; an owner’s client commits the removal; the standard self-removal proposal is evaluated once its specification is final (ADR-0030)Until an owner acts, the leaving device’s leaf stays in the tree
F4.12RE2An owner denies having shared or removed someoneCommits are signed and visible to every member; the log keeps them for its retention period—
F4.13EP7The last owner is removed or demotedRefused: every commit must leave at least one owner (design detail 3)—
F4.14DP7The only owner disappears for good, so membership can never change again (T29)Recovery joins give a returning owner their access back (F9); clients suggest a second ownerIf the owner and their recovery kit are both gone, membership stays frozen; content is not lost
F4.15IE7A pending share’s invitation email tells the email provider who shares with whomContent-free invitations; no document names (F14)Residual

Phase 3; design detail for ADR-0016 Decision 8, decided in Phase 3. An owner’s client creates a random link key, a link signing key pair and a random link ID, and keeps the link key and the private signing key in that owner’s own encrypted settings (§5.2), not in the document’s group. It wraps the latest snapshot key for the link and signs the wrapped key, and stores both on the server under the link ID. The URL carries the link ID in its path; its fragment (after #), which browsers never send to servers, carries the link key and the public signing key. The owner’s devices wrap and sign each new snapshot for the link as they make snapshots. A recipient’s app reads the fragment, removes it from the address bar, fetches the wrapped key and the snapshot by link ID, checks the signature, and shows the document read-only.

Link readers are not MLS members, which is also how very large read-only audiences are served (ADR-0016, amendment 2026-10-07, design detail 5). Because only the creating owner’s devices hold the link’s keys, no other member, link reader or server can show link readers forged content. The cost is that the link’s view advances only when that owner’s devices make snapshots, and stops when that owner leaves the document. This document recommends that links stay read-only in Phase 3.

sequenceDiagram
  autonumber
  box Owner's device
    participant O as Owner's engine
  end
  box Server host
    participant S as bayan-server
  end
  box Recipient's device
    participant L as Link recipient's app
  end
  O->>O: new link key (256 bits), link signing key pair and link ID (128 bits), all random
  O->>O: keep the link key and private signing key in the owner's encrypted settings
  O->>O: wrap the latest snapshot key (AEAD bound to link ID and group ID), sign the wrapped key
  O->>S: create the link: link ID, signed wrapped key, blob ID, expiry. The server checks the owner role
  Note over O,L: The owner sends the URL over any channel: link ID in its path, link key and public signing key in its fragment
  L->>L: read the fragment and remove it from the address bar
  L->>S: fetch the link record by link ID (the fragment is never sent)
  S-->>L: signed wrapped snapshot key and the snapshot blob
  L->>L: check the signature, unwrap, check the blob hash, decrypt, import read-only with limits
  Note over O,S: Updating: the owner's devices add a signed wrapped key for each new snapshot. Revoking: the owner deletes the record
#STRIDEElementThreatMitigationRemaining risk
F5.1IE6The URL leaks: forwarded, kept in history or bookmarks, shown in screenshots or chat previews (T31)A link is a bearer secret by design: expiry, revocation, read-only; the app removes the fragment from the address bar as soon as it opensWhoever sees the URL can read until revocation
F5.2IDF8A hostile server serves web code that sends it the fragment (T11, T31)The desktop app can open links; reproducible web builds (T11)Residual for links opened in a browser
F5.3SP8Guessing link IDs128-bit random IDs; rate limits per address; the same answer for unknown and revoked links; the ID alone gives only ciphertext—
F5.4TP8The server swaps the snapshot blobThe signed wrapped key carries the blob’s hash; AEAD binds the blob to the group and snapshot—
F5.5TP8The server serves an older wrapped key, showing a stale documentSigned wrapped keys carry the snapshot’s creation time, shown to the readerReaders cannot prove they see the newest state
F5.6EP8A link grants more than readingRead-only in Phase 3; editing needs an account and membership—
F5.7EP8A non-owner creates or updates a linkThe server accepts link records only from owners’ devices, and updates only from the creating owner’s devices; readers accept only what the link’s signing key signed—
F5.8IP8, D8The server learns when a link is used and from which addressNo per-access records on the server (§9.1); operators keep proxy logs minimal (§4.6 IE3)The server sees each fetch as it happens
F5.9EP8Revocation does not undo access already obtainedDocumented; the server deletes wrapped keys at once; nothing new is wrapped for a revoked linkReaders keep what they already fetched
F5.10DP8A popular or abused link floods the serverRate limits per link and per address; download bandwidth limits—
F5.11TE2, P8Another member, a link reader or the server shows link readers forged contentOnly the creating owner’s devices hold the link key and the private signing key; readers check the signature with the public key from the fragmentThe owner who created the link can show readers anything, as an owner could edit the document anyway
F5.12DP2A crafted snapshot crashes or exhausts the reader (T8)Isolated import with time and memory budgets, as for any untrusted CRDT input (ADR-0008)—
F5.13IE6Password-protected links would invite weak passwords that the server could guess offlineNot offered: they need a password-hashing function, which MLS’s providers do not include, and so an ADR (ADR-0016 Decision 12)—
F5.14DP2The link’s view stops advancing when its owner’s devices make no snapshots or the owner leavesClients warn owners about links they created before they leave; another owner can create a new linkLink views can be stale (§12)

6.6 F6: Synchronizing while online

Phase 3 (SRV-103 relay, SRV-104 protocol). Each device keeps one WebSocket connection to the server. The connection is authenticated by the session and by the device key’s signature on a challenge (§5.3). Edits become CRDT updates, which the engine batches, encrypts as MLS application messages (private framing, so even the sender is hidden inside) and hands to the shell as opaque frames. The server checks what it can without keys and fans the message out; receivers decrypt and check the sender’s role themselves. Commits take the path of §7. Presence (cursors and selections, COL-02) travels as MLS messages marked ephemeral, which the server relays but never stores.

sequenceDiagram
  autonumber
  box Device A
    participant EA as Engine A
    participant HA as Shell A
  end
  box Server host
    participant G as WebSocket gateway
    participant V as Validator and sequencer
    participant LOG as Message log
  end
  box Device B
    participant HB as Shell B
    participant EB as Engine B
  end
  HA->>G: WebSocket upgrade with the session (Origin checked)
  G-->>HA: challenge
  HA->>EA: host service: sign the challenge
  EA-->>HA: signature by the device key
  HA->>G: signature, subscribe to groups from their last sequence numbers
  G->>G: device approved and not revoked, member of each group
  G-->>HA: messages and outcomes since those sequence numbers
  EA->>EA: batch edits into a CRDT update, MLS-encrypt it (private framing, padded)
  EA->>HA: host.net.send with an opaque frame
  HA->>G: application message for group and epoch
  G->>V: device is a member whose role may send updates, epoch E or E-1, size and rate
  V->>LOG: append: sequence number, epoch, size, time, ciphertext. The sender is not stored
  V-->>HB: fan-out to the group's online devices
  HB->>EB: net.received
  EB->>EB: decrypt, check the sender's role for the update's kind in both epochs, import with limits
  EB-->>G: acknowledge the sequence number
#STRIDEElementThreatMitigationRemaining risk
F6.1SP6A stolen session token is used to connect as a device (T32)The device key’s signature on a fresh challenge (§5.3)—
F6.2SP6Cross-site WebSocket hijacking: another site opens a connection that carries the user’s cookieThe gateway checks Origin against the server’s own; SameSite=Strict; the device signature—
F6.3EP6, P2A viewer or a non-member sends updates (T7)The server refuses them using the roster and the authenticated device; clients refuse them too; the only message a viewer may send is a leave request (F4)—
F6.4EP2A commenter sends editsThe server cannot tell edits from comments (§7.4); every client refuses them after decryptingClients spend effort decrypting and discarding them; rate limits bound it
F6.5TP6The server drops, delays, reorders or replays messagesMLS authenticates every message; CRDT updates commute and importing one twice changes nothing; MLS refuses replays; sequence numbers and acknowledgements reveal gapsWithholding is possible (availability)
F6.6TP6The server forks the group, delivering different commits to different members (T26)Members end up in different epochs and stop seeing each other’s edits and presence; an optional group code (from MLS’s epoch authenticator) that members can compare; repair by an owner’s move (F13), after which CRDT merges both sides’ editsMembers notice only indirectly unless they compare codes
F6.7IP6, D3The server learns who edits which document, when, how much, and the rhythm of typing (T9)The sender is checked and then forgotten, not stored; updates are batched and MLS padding hides exact sizes (both configurable); §9.2Timing and volume per device stay visible
F6.8DP6Flooding: large or many messages, many subscriptions, fan-out in large groupsSize limits per message kind (at most the 16 MiB of SRV-002’s limit, lower for updates); rate limits per device and per group; quotas; bounded queues with backpressure; connection limits per account and addressA member can still consume their share
F6.9DP2A crafted CRDT update crashes or exhausts the receiving engine (T8)Import isolated with time and memory budgets, and the engine restarted if it aborts (ADR-0008, amendment 2026-10-08); fuzzed import—
F6.10RE2A member denies an editMLS application messages are signed by the sender’s device key, which other members verify after decryptingMembers can also prove authorship to others: there is no deniability (§12)
F6.11IP6Presence reveals what someone is looking atPresence is MLS-encrypted; the server sees only that ephemeral messages flow, and stores noneTheir rate shows activity
F6.12EP7, P2A writer demoted by the last commit sends late updates from the previous epochThe server and clients check the role in both epochs (SRV-002)—
F6.13DP6A slow client makes the server buffer without limitBounded per-connection queues; slow clients are disconnected and catch up from the log—
F6.14SP6The server attributes a message to the wrong deviceFor commits the validator checks that the signer is the authenticated device; for updates, clients learn the real sender from the decrypted, signed content—

6.7 F7: Synchronizing after being offline

Phase 3 (SRV-104). While offline, a client keeps its edits as CRDT updates in an encrypted local queue. It does not keep MLS ciphertext, because members keep keys for at most one past epoch (ADR-0016, amendment 2026-10-07, requirement 3) and would not be able to read updates encrypted many epochs ago. When it reconnects, it fetches everything since its last sequence numbers, processes commits in order, and only then encrypts and sends its queued updates in the current epoch.

sequenceDiagram
  autonumber
  box Device
    participant E as Engine
    participant Q as Encrypted offline queue
  end
  box Server host
    participant S as bayan-server
  end
  E->>Q: while offline: keep CRDT updates, encrypted with a local key, not MLS ciphertext
  E->>S: reconnect (F6) and subscribe from the last sequence numbers
  S-->>E: commits with their outcomes, and messages since then
  E->>E: process the commits in order, decrypt each message in its own epoch
  alt the log no longer reaches back far enough
    E->>S: resync with its own device key (F9) and fetch the latest snapshot (F8)
  end
  E->>E: encrypt the queued updates in the current epoch
  E->>S: send them
  S->>S: check the device's role now
  alt the role no longer allows edits
    S-->>E: refused
    E->>E: keep the edits locally, tell the user, offer a copy of the document
  end
#STRIDEElementThreatMitigationRemaining risk
F7.1ID1The queue or cache is read on a lost, locked device or by another local userEncrypted at rest under a key protected by the OS keychain on desktop, or by a non-extractable WebCrypto key on the web (SEC-11)An unlocked device is out of scope
F7.2DP2Updates encrypted long ago can no longer be read by anyoneThe queue holds CRDT updates; MLS encryption happens when they are sent—
F7.3EP7, P2Edits made offline by a user who was demoted or removed in the meantimeThe server and clients refuse them; the user is told and keeps a local copy (COL-03: nobody else’s work is overwritten)The user’s offline edits do not reach the shared document
F7.4TP6The server withholds part of the logSequence numbers reveal gaps; CRDT version vectors reveal missing updates; the client asks againAvailability
F7.5DD3The log was pruned before the device came back (§9.1 retention)Resync with the device’s own key and the latest snapshot (ADR-0029)Without resyncs, owners must re-add the device
F7.6ID1A device that stays offline keeps old epoch secrets until it catches upAt most one past epoch is kept; secrets are encrypted at rest; they are deleted as the device processes commits—
F7.7SP6A device revoked while it was offline reconnectsThe server refuses the connection; its queued edits are not accepted; the user’s other devices have removed it from the groups (F11)—
F7.8TP6The server presents an older state of a group to a returning deviceThe device knows its own last epoch and never goes backAvailability

6.8 F8: Snapshots and new members

Phase 3; the construction is ADR-0016’s amendment of 2026-10-07, design detail 2, tested in SRV-002. MLS’s forward secrecy means a device that joins cannot read anything sent before. An editor’s client therefore encrypts a snapshot of the current document under a fresh key and uploads it as an opaque blob, then sends the key, the snapshot’s ID and the blob’s hash inside the group. Every member’s device that receives a snapshot key seals it to its own account’s key backup (F9), so that each user can recover their documents. A recovery restores only the latest snapshot, so editors’ clients also make snapshots at a regular cadence, not only for newcomers.

sequenceDiagram
  autonumber
  box Editor's device
    participant ED as Editor's engine
  end
  box Server host
    participant S as bayan-server
    participant BL as Blob store
  end
  box Newcomer's device
    participant N as Newcomer's engine
  end
  Note over ED,N: After the commit that added the newcomer (F4), or at the snapshot cadence
  ED->>ED: snapshot of the current state, without edit history unless the sharer chooses otherwise
  ED->>ED: encrypt under a fresh key with the group's AEAD. Associated data: label, group ID, snapshot ID
  ED->>S: upload the blob and the log position it covers. The server checks the owner or editor role and the quota
  S->>BL: store it under a random blob ID with group and size
  ED->>S: MLS application message: snapshot key, snapshot ID, blob hash
  ED->>S: key-backup entry: the snapshot key sealed to the editor's own account's backup key
  S-->>N: the key message, in an epoch the newcomer can read
  N->>N: check that the sender may edit, in both epochs
  N->>S: fetch the blob. The server checks that the device is a member
  S-->>N: blob
  N->>N: check the hash, decrypt, import with limits
  N->>S: key-backup entry: the snapshot key sealed to its own account's backup key
  N->>N: compare version vectors and ask for a newer snapshot if updates are missing
#STRIDEElementThreatMitigationRemaining risk
F8.1TP8, D4The server swaps or changes the blobThe hash inside the MLS-authenticated key message, and the AEAD with its associated data (SRV-002 tampered_or_swapped_blobs_are_refused)—
F8.2EP2, P8A commenter or viewer shares a snapshot key, or uploads blobsClients refuse the key message (SRV-002 only_members_who_may_edit_can_share_snapshots); the server refuses blob uploads from roles below editor—
F8.3IP2A snapshot carries edit history the sharer did not mean to share (T10)Snapshots hold the current state only, unless the sharer chooses to include history; “inspect document” removes history—
F8.4ID4Removed members keep the keys of older snapshotsThey could read those snapshots before; later snapshots use new keys sent only to current members (SRV-002 removed_members_get_no_later_snapshot_keys); the server deletes superseded snapshots and serves blobs only to current membersOld snapshot keys stay valid for blobs someone kept
F8.5DP6, P8The server withholds the blob or the key messageThe newcomer sees nothing to read and asks again; any other editor can send a fresh snapshotAvailability
F8.6DP2A crafted snapshot crashes or exhausts the newcomer (T8)Isolated import with time and memory budgets—
F8.7TE2A stale snapshot leaves out updates sent before the newcomer joinedVersion vectors show what is missing; the newcomer asks for a newer snapshot—
F8.8ID4Snapshot sizes and times reveal the document’s size and growth (T9)Optional padding of blobs to size classesResidual
F8.9DD4Snapshot uploads fill the storageQuotas per account and per group; only the latest few snapshots are kept—
F8.10RE2Who shared a snapshot is disputedKey messages are signed by the sender’s device—
F8.11ID2Key-backup entriesSealed with HPKE using the hybrid post-quantum KEM to the account’s key-backup key; the server learns only that an entry exists for a group (T22)Whoever holds the recovery kit opens them (F9.14)

6.9 F9: Recovery with the recovery kit

Phase 3; ADR-0029. When an account is created, its first device makes the recovery secret, shows it as the recovery kit (words and a QR code to print or save), checks that the user saved it, seals the identity key, the device-signing key and the key-backup key into the secret store on the server, and erases the secret. After losing every device, the user signs in on a new device, enters the recovery kit, and gets their identity back.

The new device then rejoins each group where the user still has a role, without anyone’s help:

  • Recovery join. An MLS external commit that adds the new device and removes no one. The new device then removes the user’s lost devices with ordinary own-device removals. OpenMLS 0.9.0 can build an external commit that removes only a leaf with the joiner’s own signature key, so a new device cannot remove another device in the same commit.
  • Resync. A device that kept its own key, because it fell behind the log’s retention (F7) or could not follow a commit (§7.2), rejoins with an external commit that replaces its own old leaf, as RFC 9420 §12.4.3.2 describes and OpenMLS supports.

Without recovery joins, a user who lost every device could rejoin only if an owner re-added them, and a document whose only owner lost every device could never change its members again (T29). Because a recovery may run on a device or in a browser the user does not fully trust, it ends with a new recovery kit and a new key-backup key (§5.1).

sequenceDiagram
  autonumber
  box New device
    participant U as User
    participant N as New device's engine
  end
  box Server host
    participant S as bayan-server
  end
  box Members of a document
    participant M as Members' engines
  end
  N->>S: sign in (F1 or F2), register as a pending device
  S-->>N: the secret store and the key-backup entries (ciphertext)
  U->>N: enter the recovery kit
  N->>N: derive the secret-store key, open the secret store
  N->>N: new device-signing key, certify its own device key
  N->>S: new signed device list that revokes the lost devices, and certificates
  S->>S: check against the identity key, approve the device, end the lost devices' sessions
  S-->>N: the groups where the account has a role, with their group information
  loop each such group
    N->>S: recovery join: external commit adding N, removing no one
    S->>S: validator: chain to the user's identity, latest device list, role in the roster, recent sign-in
    S-->>M: the commit
    M->>M: the same checks except the sign-in, then show that the user rejoined after a recovery
    N->>S: own-device removals of the user's lost devices
    N->>N: open the latest snapshot key from the key backup, fetch and decrypt the snapshot
  end
  N->>N: make a new recovery kit and a new key-backup key, seal the current snapshot keys again
#STRIDEElementThreatMitigationRemaining risk
F9.1SE1, P5Someone with the recovery kit and a sign-in takes over the identity and rejoins every group (T28)The kit is the root of the account and stays offline with the user; recovery also needs a recent sign-in, which the server checks; recovery joins are visible to every member and to the user’s remaining devices, which alert; audit logWhoever has both owns the account
F9.2ID2The server tries to open the secret store by guessingThe recovery secret is 256 random bits, never a passphrase; the store is sealed with a 256-bit AEAD key—
F9.3ID2Harvest now, decrypt later on the key backup (T22)Sealed with HPKE using the hybrid post-quantum KEM—
F9.4DE1The user loses every device and the recovery kit (T29)Clear guidance to keep the kit; optional passkey unlock (F10); owners can share documents again with the user’s new identityThe identity and documents nobody else holds are lost
F9.5DP7The only owner of a document loses every device (T29)Recovery joins restore the owner’s devices without anyone’s helpIf the kit is lost too, the membership stays frozen; the content is not lost
F9.6EP7A stolen device, or someone holding the identity key, uses a recovery join or resync to get back into groups after being removed (T39)The joining device must be in the latest signed device list the server holds, and after a restore the server takes devices’ newest lists before any commit; the user’s own other devices in the group, which know every revocation, can refuse the join, as witnesses for a user who is not an owner (§7.2) and within a limit of 10 minutes for an owner (§13.4); their refusal blocks only the joining device, not the account; joins are shown to everyone; owners can remove; the device-signing key is rotated after a lossA hostile server can withhold the newer list (T30) and ignore the refusals; after a restore, until a device that knows the revocation reconnects, and when the user’s other devices stay offline beyond the limit, a revoked owner device can still rejoin (§13.4)
F9.7TD2The server changes the secret store or backup entriesAEAD and HPKE authenticate them; a changed store does not openAvailability
F9.8DP7The server, or a member, publishes group information with a wrong external key, so joins failWitnesses check the group information against the epoch they derived (§7.2); any member can publish a fresh copyAvailability
F9.9RD5A recovery is denied or goes unnoticedAudit log; members and the user’s devices see recovery joins—
F9.10DP7A recovery join by a non-owner waits for a witness (§7.2)Witnessing runs in the background on any member device that connects; meanwhile the key backup already gives the user the latest snapshot to readQuiet groups wait longer; after 72 hours without a witness the join is dropped and retried
F9.11IE1The kit is seen on screen or found on paperNever sent to the server, never stored on a device; guidance on keeping it—
F9.12SE1, DF8Phishing for the kit: a fake prompt, or a hostile server’s web code (T11)The kit is entered only in the app, best the desktop app; BayanDocs never asks for it by email; a new kit after every recoveryResidual in the browser
F9.13DP2The pinned OpenMLS cannot remove another device in the same external commitJoin first, then own-device removals; until they are done the lost devices keep their leaves, which the server already blocksA short window in which lost devices remain members
F9.14ID2A hostile operator who once obtains the kit, for example from web code during a recovery in a browser, opens the secret store and the latest snapshot of every document from its own stored data, without signing in, and keeps reading later snapshotsA new kit and a new key-backup key after every recovery and any suspected exposure; a new identity key if the operator may have kept an older secret store (§5.1)Until the user makes the new kit (T28)

6.10 F10: Passkey unlock (PRF)

Phase 3, opt-in; ADR-0029 and ADR-0016 Decision 7. The WebAuthn PRF extension lets an authenticator compute a secret from a passkey and a fixed input. With passkey unlock switched on, the client uses that secret to wrap a copy of the recovery secret and stores the wrapped copy on the server. On a new device, signing in with the passkey then also unlocks the account, instead of typing the recovery kit. Switching it on needs the recovery kit once, because no device keeps the recovery secret.

sequenceDiagram
  autonumber
  box Device
    participant U as User
    participant C as Client
    participant A as Authenticator
  end
  box Server host
    participant S as bayan-server
  end
  Note over U,S: Switching it on: opt-in, with the recovery kit once
  U->>C: switch on passkey unlock, enter the recovery kit
  C->>A: assertion with the PRF extension and the fixed BayanDocs input
  A-->>C: PRF output, only after user verification
  C->>C: derive the unlock key (HKDF), wrap the recovery secret (AEAD bound to the credential ID and account)
  C->>S: store the wrapped secret for this passkey
  Note over U,S: Unlocking on a new device
  C->>A: sign-in assertion with the PRF extension
  A-->>C: signature and PRF output
  C->>S: sign in (F2), fetch the wrapped secret
  C->>C: unwrap the recovery secret and continue as in F9
#STRIDEElementThreatMitigationRemaining risk
F10.1SE4One factor now gives everything: whoever can use the passkey, also through a compromised passkey sync account, gets the sign-in and the keys (T28)Opt-in, with an explanation; administrators can switch it off for their organization; the recovery kit remainsResidual for users who switch it on
F10.2IDF7, DF8In a browser, the PRF output and the recovery secret pass through code the server serves (T11)The desktop app uses the platform’s passkey interface directly where the platform allows it for the server’s domain (on Apple’s platforms only for associated domains, F2)Residual in the web app, and on desktop platforms that need the browser for a self-hosted domain
F10.3ID2The wrapped secret is stored on the serverWrapped with an AEAD key derived from a 256-bit PRF output; useless without the passkey—
F10.4SDF7Another site asks the authenticator for the PRF outputPasskeys are bound to the server’s domain; PRF outputs differ per domain and input—
F10.5DE4The authenticator or platform has no PRFThe recovery kit, which is always available (ADR-0016 Decision 7)—
F10.6EP4The server pretends PRF is unavailableAffects only the convenience—
F10.7RD5Use of passkey unlock is denied or goes unnoticedAudit log; the user’s devices are told—
F10.8TD2The server swaps wrapped secrets between passkeys or accountsThe AEAD binds the credential ID and the account—
F10.9IE4A passkey or its sync account is compromised; the wrap holds the recovery secret itself, so the exposure lastsRemove the passkey and its wrap, then respond as to an exposed kit: a new kit, a new key-backup key and device-signing key, and a new identity key if a hostile party may hold an older secret store (§5.1)Until the user responds

6.11 F11: Losing a device, and revocation

Phase 3; ADR-0029. From any of their remaining devices, the user marks a device as lost. The server ends the lost device’s sessions at once; the user’s devices then remove it from every group with MLS commits, which is what takes its keys away for good. If the lost device was a native device, it held the device-signing key, so the user rotates that key with the recovery kit or passkey unlock, and the lost device can no longer certify devices. Existing places of the remaining devices in groups stay valid, because a chain is checked against the latest device list (§5.2).

sequenceDiagram
  autonumber
  box Remaining device of the user
    participant D2 as Remaining device's engine
  end
  box Server host
    participant S as bayan-server
  end
  box Lost device
    participant D1 as Lost device
  end
  box Members
    participant M as Members' engines
  end
  D2->>S: new signed device list revoking D1
  S->>S: check the signature, end D1's sessions, refuse its connections, delete its key packages
  S--x D1: connections closed
  loop each group D1 is in
    D2->>S: commit removing D1 (own-device removal, §7)
    S-->>M: the commit
    M->>M: check it and merge it, D1 has no keys for the new epoch
  end
  opt D1 was a native device that held the device-signing key
    D2->>D2: recovery kit or passkey unlock, new device-signing key certified by the identity key
    D2->>S: new device list signed with the new key, listing the remaining devices
  end
  Note over D2,M: Without any remaining device: recovery (F9), whose recovery joins and removals replace the lost devices
#STRIDEElementThreatMitigationRemaining risk
F11.1ID1The thief reads what is stored on the deviceLocked: encrypted at rest (SEC-11). Unlocked: out of scope—
F11.2EP6The thief keeps synchronizing until the user noticesRevocation from any device or the web app ends the sessions at once; the MLS removals follow, after which post-compromise security holdsThe time until the user notices
F11.3ED1The thief certifies new devices with the device-signing key, or signs a device list that undoes the revocation (T39)Rotation of the device-signing key; members accept a new device only if the latest list, signed by the current key, names it; the user’s devices alarm on changes attributed to them that they did not make (§5.2)Until the rotation
F11.4TP6A malicious server keeps serving the revoked deviceThe MLS removal commits, not the server’s block, remove access cryptographically; members see themA server that withholds the removal commits causes a stall the user notices
F11.5DP7Removals by a user who is not an owner wait for a witness (§7.2)The server’s block is immediate in the meantime—
F11.6SP5A sign-in attacker or a hostile server revokes the user’s devicesRevocations must be signed by the device-signing key; ending other devices’ sessions needs an approved device (§5.3)A hostile server can always refuse service
F11.7RD5Disputed revocationsThe device list records them; audit log—
F11.8DP7The lost device was the only owner’s only device (T29)Recovery joins (F9)See F9.5
F11.9DP7Rotating the device-signing key breaks the remaining devices’ places in groupsA certificate made by an earlier device-signing key stays valid for a device the latest list names (§5.2); devices replace it with their next self-update—

6.12 F12: Accepting a commit that only members can check

Phase 3 (SRV-103 and bayan-mls). Direction chosen by the owner (ADR-0016, amendment 2026-10-07, requirement 6); details in ADR-0030 and §7.2. The server’s validator cannot check the parts of a commit that are keyed with the epoch’s secrets: its membership and confirmation tags, and the path secrets it encrypts to the other members (SRV-002 report §5). Each member decrypts only the path secret meant for its own part of the group. A committer can therefore send a commit that the server accepts and that every other member refuses. It can also make a commit work for some members, for example the owners or whoever is online, and fail for the rest. With two-phase acceptance, a commit from a member who is not an owner takes effect only once a device of another account confirms that it can process it and no such device refuses it; nobody merges it before.

sequenceDiagram
  autonumber
  box Committer, not an owner
    participant C as Committer's engine
  end
  box Server host
    participant V as Validator
  end
  box Witnesses: devices of other accounts in the group
    participant W as Witness engines
  end
  box Owners
    participant O as Owners' engines
  end
  C->>V: commit for epoch E (a self-update, a change to the user's own devices, a resync or recovery join)
  V->>V: public checks: signature, sender, policy, framing, epoch
  V->>V: pending: keep the public state of epoch E and the staged state of E+1
  V-->>W: the commit, marked pending
  W->>W: process it without merging, using the secrets of epoch E, and check its group information
  W->>V: report: OK, or refused with a reason class
  alt at least one OK and no refusal by the end of the grace period
    V-->>C: confirmed
    V-->>W: confirmed, everyone merges
  else any refusal
    V-->>C: dropped, the committer's account quarantined in this group
    V-->>W: dropped, everyone discards it
    V-->>O: notice with the reports, for review
  else no report within 72 hours
    V-->>C: dropped without quarantine, retry later
  end
  Note over C,O: A device that later cannot process a confirmed commit reports it: the committer's account is quarantined and the device resyncs
#STRIDEElementThreatMitigationRemaining risk
F12.1DP7A member stalls the group with a commit only members can check (T25)Two-phase acceptance: the commit is dropped as soon as one witness refuses it, and the committer’s account is quarantined in the group (ADR-0030)—
F12.2DE2Selective encryption: the commit works for some members (owners, or whoever is online) and fails for othersAny refusal drops the commit, and an owner’s OK carries no special weight; if only members who were offline got bad secrets, they report the confirmed commit as stuck, the committer’s account is quarantined afterwards, and they resync (§7.2)Members who were offline must resync once
F12.3DE2A lying witness refuses honest commits, or reports confirmed ones as stuckThe honest committer’s account is quarantined until an owner, who sees every report, lifts it; a single report does not show whether the witness lied or the committer encrypted a bad secret to it, so owners go on patterns (one witness refusing several committers, or several witnesses refusing one committer) before removing anyone; owners’ commits are not affected by reportsA liar can delay the commits of members who are not owners until an owner acts (FM12)
F12.4DP7No witness is onlineBackground witnessing by any member device that connects; after 72 hours without any report the commit is dropped without quarantine and the committer retriesMembers who are not owners cannot change keys or devices in a group nobody else visits
F12.5EP7A removed user refuses their own removal to block itOnly accounts that remain in the group are witnesses; owners’ commits, which remove other users, take one phase—
F12.6TP7The server lies about the outcomeA member that could not process a commit cannot merge it whatever the server says; the server can only stall, as it always couldAvailability
F12.7SP6Reports from devices that are not witnessesOnly reports from authenticated devices of witness accounts count; for resyncs and recovery joins, the joining account’s other devices may also refuse, but their OK does not count—
F12.8RD5Which device refused which commit is disputedContent-free records of reports, kept for owners until the commit is decided and 30 days after—
F12.9DP7Server state grows with pending commitsOne pending commit per group; it costs one more copy of the public state (up to 1.34 MiB at 1,000 members with the hybrid suite, SRV-002). OpenMLS’s public group cannot be copied, so the server keeps the pending commit staged and stages it again after a restart—
F12.10IP6Reports reveal which members are online and activeThe server sees connections anyway (§9.2)Residual
F12.11EP5A quarantined member enrolls a new device to start againQuarantine applies to the account in the group, covering all its devices and its recovery joins—

6.13 F13: Moving a document to a new group

Phase 3; ADR-0016, amendment 2026-10-07, design detail 4. An owner moves a document to a new group to change its ciphersuite (until OpenMLS implements MLS’s re-initialization) or, as the last resort after a stall, to leave out the users who caused it (requirement 6). The owner announces the move in the old group with an update that only owners may send, and every member checks the new group against the announcement before switching.

sequenceDiagram
  autonumber
  box Owner's device
    participant O as Owner's engine
  end
  box Server host
    participant S as bayan-server
  end
  box Members
    participant M as Members' engines
  end
  O->>S: claim fresh key packages for every member device, for the new ciphersuite
  O->>S: register the new group (only the owner), then one commit adding everyone with the same roster
  O->>S: in the old group: a group-moved update (owners only) naming the new group, its suite, and any users left out
  S-->>M: the update, and a Welcome to the new group
  M->>M: check that an owner sent it, and that the new group has exactly the announced members, roles and suite
  M->>M: join the new group, send there the CRDT updates not yet acknowledged, stop using the old group
#STRIDEElementThreatMitigationRemaining risk
F13.1SP2A fake move announcementOnly owners may send the group-moved kind; MLS authenticates the sender; members compare the new group with the announcement—
F13.2EP2A move used to add or drop members unnoticedMembers compare the new group with the old roster and the announcement; any users left out must be listed and are shown—
F13.3DP6The server refuses to register the new group or withholds WelcomesAvailabilityResidual
F13.4ID2The server links the old and the new groupExpected; metadata (§9)Residual
F13.5DP6Size and cost of moving a large group (2.58 MiB commit for 1,000 members with the hybrid suite, SRV-002)Rate limits and size limits that allow it; the move is rare—
F13.6TP2Edits made during the move are lostMembers resend unacknowledged CRDT updates in the new group, where they merge—

6.14 F14: Notifications without content (COL-10)

Phase 3. The server can tell a user that something happened (a document was shared with them, a device is waiting for approval, a security event) but never what a document says or what it is called: it does not know. Notices go to the user’s devices over the sync connection; email is optional, switched on by the operator and the user. Push services of browser and phone vendors are not used, because they are third-party network calls at runtime, which the project’s rules exclude (AGENTS.md §6).

sequenceDiagram
  autonumber
  box Server host
    participant S as bayan-server
    participant NT as Notifier
  end
  box Optional, run by the operator
    participant MAIL as Email relay
  end
  box Recipient's device
    participant C as Client
  end
  S->>NT: event: shared with you, a device waits for approval, a security notice
  NT->>NT: build a content-free notice: event type, opaque reference, time, and the actor's display name if enabled
  NT-->>C: notice over the sync connection
  opt email switched on by the operator and by the user
    NT->>MAIL: plain-text email over TLS, without titles, content or remote images, with one link to the server
  end
  C->>C: fetch the details through sync, decrypt, show the title on the device
#STRIDEElementThreatMitigationRemaining risk
F14.1IP9Content or titles in notificationsThe server has neither; notices hold only an event type, a time, an opaque reference and optionally a display name; clients never send titles to the server—
F14.2IE7The email provider learns who works with whom (T37)Email is opt-in; display names in email are optional; TLS to the relay (§4.6 IE9)Residual for users who choose email
F14.3SE7Phishing: look-alike emails, or attacker-chosen display names in real onesPlain text; display names escaped and shortened; no document names; one fixed link to the server’s own address; the app says that BayanDocs never asks for the recovery kit by emailUsers can still be fooled by imitations
F14.4DP9Notification floods, for example by sharing many documentsRate limits per sender and per recipient; muting—
F14.5IP9Push services would learn timing and identifiersNot used by default; any push channel needs an ADR—
F14.6IP9Mentions in comments reveal who mentioned whomMentions travel only inside the encrypted group; at most an opt-in, generic “new activity” notice—
F14.7RD2Delivery is disputedDelivery records, kept 7 days after delivery (§9.1)—
F14.8SP9A fake in-app “device waiting” notice lures the user into approving a deviceApproval requires comparing keys out of band (F3)—

6.15 F15: Compliance escrow member

Phase 4 (E4-ENTERPRISE); ADR-0016 Decision 10. The design and its abuse cases are in §8. An organization’s policy adds a passive escrow member, whose key the organization keeps offline, to the documents its accounts own. Every member sees it.

sequenceDiagram
  autonumber
  box Organization
    participant ADM as Organization administrator
    participant ESC as Escrow officers, key offline under a quorum
  end
  box Server host
    participant S as bayan-server
  end
  box Member devices
    participant O as Owner's engine, organization account
    participant M as Members' engines
  end
  ADM->>S: escrow policy: escrow identity, a stock of its key packages, scope
  O->>O: managed configuration pins the organization's escrow identity
  O->>S: new organization document: group, then a commit adding the escrow member as viewer, with its Welcome, and the server keeps the tree of that epoch too (§8.1)
  S->>S: refuse updates in organization groups without the escrow member, and commits removing it
  S-->>M: the commit shows the escrow member
  M->>M: show that the organization's compliance can read it, warn external users before they join
  Note over ESC,S: Lawful access: officers fetch the retained log and the escrow member's Welcome, process them offline, every access is logged
#STRIDEElementThreatMitigationRemaining risk
F15.1EP7The server or an administrator adds escrow to groups silentlyAdding is an owner’s commit that every member sees; clients add escrow only when their organization’s managed configuration says so (EA1 in §8.2)An organization that controls its employees’ clients controls this
F15.2IE8The organization reads more than its policy allowsPassive member with an offline key under a quorum of officers; audit of every access (§8.2)Collusion of the quorum
F15.3SP5The server substitutes its own key for the escrow member’sThe escrow identity is pinned through the organization’s managed configuration, not taken from the server aloneUnmanaged clients rely on TOFU
F15.4EE8The escrow member writesViewer role; clients refuse anything it sends—
F15.5DE2Members remove the escrow member to evade the policyThe server refuses such commits; owners’ clients refuse to make themMembers can still copy content: escrow is not data-loss prevention
F15.6ID3Retaining logs for escrow contradicts minimizationA separate retention period for escrowed groups, stated in the policy shown to membersResidual by policy

6.16 F16: Delivering and starting the web app

Phase 1 (WEB-101, WEB-103) and every phase after; ADR-0014 and its amendment of 2026-10-12. The web app is code the server sends to the browser every time it is loaded. Everything the app protects therefore depends on that code (T11). The app’s own checks cover what browsers allow (strict CSP, Trusted Types, Subresource Integrity enforced with Integrity-Policy, and a check of the engine’s files against hashes built into the app), with one gap that WEB-002 found: the browser does not bind the worker’s own requests to the bytes the app checked (bayan-web issue 8).

sequenceDiagram
  autonumber
  box Browser
    participant P as Page, main thread
    participant W as Engine worker
    participant IDB as IndexedDB
  end
  box Server host
    participant S as bayan-server, static files
  end
  P->>S: GET / through the proxy (HTTPS)
  S-->>P: index.html with CSP, Trusted Types, Integrity-Policy, cross-origin isolation
  P->>S: the scripts named in index.html
  S-->>P: scripts, checked by the browser against their integrity hashes
  P->>S: fetch the engine's three files
  P->>P: compare with SHA-384 hashes built into the app, start nothing if any differs
  P->>W: start the worker through the Trusted Types policy bayan-script-url
  W->>S: load the engine. The browser does not bind these bytes to the ones checked
  W->>IDB: open the key store, wrapped with a non-extractable key
  Note over P,S: A hostile or compromised server can serve different code at every step (T11)
#STRIDEElementThreatMitigationRemaining risk
F16.1TDF8A hostile or compromised server serves modified code that steals keys (T11)The desktop app as the high-assurance client; browser devices hold no device-signing key (F3.13); reproducible web builds with published hashes; web code transparency (such as WAICT) once browsers ship it; self-hosting removes trust in third partiesResidual for web users, and for what is shared with accounts that use the web app, until their browser devices are revoked
F16.2TDF1Code changed on the networkTLS 1.3; HSTS at the proxy—
F16.3EP1Script injection into the pageStrict CSP without inline scripts, Trusted Types, no dangerouslySetInnerHTML (SEC-10, ADR-0014)—
F16.4TDF8The worker’s files are swapped after the app checked themClosing the gap is tracked in bayan-web issue 8; it must be closed before collaboration runs in browsers (§13.3)Residual until then
F16.5IP1Key material reaches the page’s main thread, where more code runsKeys stay in the worker; the protocol never sends key material to the page (§13.3)—
F16.6TP1A malicious service worker of the installable app (WEB-103) persists after the server is fixedIt is registered through the same single Trusted Types policy; it is updated like any script and checked against the build’s hashesResidual while web code transparency is missing
F16.7IP1Third-party origins at runtimeNone (ADR-0014 Decision 9)—
F16.8SE5An operator runs a modified web appThe “Source code” link to the exact source running (OPS-08) lets users and auditors compareUsers rarely check
F16.9DP1Poisoned caches or content delivery networksNo CDN; Cache-Control rules; integrity hashes—

6.17 F17: Administration, backups and restores

Phase 1 (SRV-101 admin bootstrap, SRV-105 operations) and Phase 3 (admin console, OPS-06). Administrators manage accounts, quotas and policies through the admin console; operators run the command line inside the container and make and restore backups. None of them can read documents or add devices or members: those need keys that only users’ devices hold.

sequenceDiagram
  autonumber
  box Operator's side
    participant AD as Administrator's browser
    participant OP as Operator's shell on the host
  end
  box Server host
    participant S as bayan-server
    participant DB as Metadata database
    participant BL as Blob store
  end
  box Backup storage
    participant BK as Backups
  end
  AD->>S: admin console through the proxy: passkey sign-in, recent re-authentication
  S->>DB: accounts, devices, quotas, policies: metadata only
  S->>DB: audit entry for every admin action
  OP->>S: bayan-server admin command inside the container
  OP->>BK: backup: a consistent copy of the database and the blob store
  OP->>S: restore from a backup
  S->>S: after a restore: end every session, mark key packages and pending states stale, enter catch-up for every group
  Note over S,BK: Members then upload the commits and signed device lists the backup lacks, under the catch-up rules of §7.5 (ADR-0030)
#STRIDEElementThreatMitigationRemaining risk
F17.1SP10An administrator account is taken overPasskey-only administrator sign-in; re-authentication; audit; actions that affect users are shown to them (T27)—
F17.2EP10An administrator reads documentsImpossible: the server has no keys (ADR-0015 Decision 6)—
F17.3EP10An administrator adds a device to a user or a member to a groupImpossible: certificates come only from the user’s devices, membership only from owners’ commitsThe administrator approval gate (COL-13) can only block
F17.4DP10An administrator deletes data or disables accountsAudit log; backups; users notifiedResidual: operators control availability
F17.5ID7Backups are stolenThey hold the metadata of §9 and ciphertext; secret stores and backup entries stay sealed; encrypted backups and restricted access recommendedMetadata exposure
F17.6TD7A restore rolls group state back, brings back sessions revoked after the backup, and offers key packages already used (T33)After a restore the server ends every session, asks devices for new key packages, and lets members catch it up under the rules of §7.5 (ADR-0030)Until members reconnect, groups changed since the backup wait
F17.7RD5An administrator denies an actionAudit log, which the server only appends to; operators export it regularlyAn operator with database access can edit it
F17.8IP10The console shows more metadata than needed, for example who shares with whomConsole views show counts and sizes; anything more is a deliberate, audited support action—
F17.9ID6The first administrator’s bootstrap token appears in the log streamThe command line prints it to the operator’s terminal only, never to the logs; the server keeps only its hash, until it is used—
F17.10EE5Command-line access means access to the hostThe operator’s responsibility; documented in the deployment guide—

7. Role enforcement: design details and failure modes

Roles are enforced twice: by the server, so that what it refuses never reaches anyone, and by every client, so that a misbehaving server cannot give anyone more than their role (ADR-0016 Decision 5). §7.1 lists who checks what; §7.2 gives the details of two-phase acceptance; §7.3 lists what happens when the server and the clients disagree; §7.4 confirms that the server is not told whether an update is an edit or a comment; §7.5 says how groups catch up after a server restore.

7.1 Who enforces what

RuleServer (validator)ClientsSource
Only commits change a group; standalone proposals and unused proposal types are refusedYesYesADR-0016, amendment 2026-10-07, design detail 3 (below: “design detail 3”)
Only owners add or remove other users’ devices, change roles, or change anything in the group contextYesYesDesign detail 3
A user’s devices may add and remove each otherYesYesDesign detail 3
A resync replaces the joining device’s own earlier place, with the same device key, and needs no fresh sign-inYesYesADR-0029
A recovery join adds an approved device of a user who has a role and removes no one; the user’s lost devices are removed afterwards by own-device removalsYes, and it requires a recent sign-inYes, except the sign-in, which only the server can seeADR-0029
After every commit: one role per user with a device, none for anyone else, at least one ownerYesYesDesign detail 3
The group context holds exactly the roster and its required capabilities (no external senders); the required credential type is the one ADR-0029 chooses instead of basic credentialsYesYesDesign detail 3, changed by ADR-0029
Every credential chains to its account’s identity key and names a device that the latest device list lists as currentYes, against the directoryYes, against pinned identitiesADR-0029
A commit is signed by the device whose connection sent it (except when catching up after a restore, §7.5)YesYes, by its signatureSRV-002
Updates come only from devices whose user is an owner, editor or commenter; the one exception is a leave request, which any member device may send, a viewer’s includedYes, from the connection and the leave request’s visible kindYes, from the decrypted, signed senderDesign detail 3; ADR-0030
Viewers’ devices send no application messages except leave requests (they still commit self-updates and changes to their own devices, and report as witnesses); their ephemeral (presence) messages are refused too, so other members do not see viewers; showing them would need an amendment of design detail 3YesYes, by discarding themDesign detail 3
Edits need owner or editor; comments commenter or above; snapshot keys editor or above; group moves ownerNo: the kind is encrypted (§7.4)YesDesign detail 3
Late updates are judged by the roles of both epochsYesYesDesign detail 3
Commits by users who are not owners take effect only after a witness confirms them and none refuses themYesYes: nobody merges beforeADR-0030
Blob uploads need owner or editor; blob downloads need a current member device or a valid linkYesClients check hashes and keysThis document
Only owners create links, and only the creating owner’s devices update themYesLink readers check the link’s signatureThis document (F5)
Organization groups keep their escrow member (Phase 4)YesYes, and show it§8
Server and clients use the same version of the policyYes, refuses unknown versionsYes, refuses unknown versionsADR-0030

7.2 Two-phase acceptance

The owner chose this direction on 2026-10-09 (ADR-0016, amendment 2026-10-07, requirement 6): the server keeps the previous epoch’s public state until a device of another user confirms the new epoch, and otherwise drops the commit and quarantines its sender. The rules below are the details SRV-003 was asked for, decided in ADR-0030. They depart from the chosen direction in three points, which the owner accepted explicitly on 2026-10-17:

  1. Owners’ commits take effect at once. The direction covers every commit; ADR-0030 applies two phases only to commits by members who are not owners. Owners can already remove anyone. Two phases for owners would let one lying member drop every owner’s commit except its own removal, which it cannot witness, stopping shares and role changes until an owner removed it; every share would wait until another member’s device comes online; and nobody could witness the first share of a document that only its owner belongs to.
  2. A commit that nobody checks within 72 hours is dropped without quarantine. The direction says “otherwise drops the commit and quarantines its sender”; a time-out is not evidence of misbehavior, so ADR-0030 drops such a commit but does not punish its sender, who retries.
  3. Quarantine covers the sender’s account in that group, not one device, so that a quarantined member cannot start again with a new device.
stateDiagram-v2
  [*] --> Pending: valid commit by a member who is not an owner
  Pending --> Confirmed: OK from a witness and no refusal within the grace period
  Pending --> Dropped: a refusal from any witness, sender's account quarantined
  Pending --> Expired: no report within 72 hours
  Pending --> Superseded: an owner commits for the same epoch
  Confirmed --> [*]
  Dropped --> [*]
  Expired --> [*]
  Superseded --> [*]
  1. Which commits. A commit takes two phases when the committer’s account is not an owner in the epoch before it: self-updates, changes to the committer’s own devices, and resyncs and recovery joins by users who are not owners. Owners’ commits and group registrations take effect at once, as today.

  2. Pending. The validator checks the commit exactly as the SRV-002 validator does. If it passes and no other commit is pending in the group, it becomes the group’s pending commit. The server keeps the commit staged next to the current state; OpenMLS’s public group cannot be copied (see the spike’s validator.rs), so the server keeps the pending commit in the database and stages it again after a restart. It relays the commit marked as pending and holds any Welcome it carries until the commit is confirmed. While a commit is pending, the server accepts updates for the current and the previous epoch as today, but none for the pending epoch. It refuses other commits by users who are not owners, asking them to retry. It accepts an owner’s commit for the current epoch, which drops the pending commit as superseded, without quarantine.

  3. Witnesses and reports. A witness is a device of an account other than the committer’s that is a member both before and after the commit. For resyncs and recovery joins, the joining account’s own other devices, which know every revocation, may also refuse, but their OK does not count; they may refuse an owner’s resync or recovery join too, within a limit (§13.4). Every witness device that receives the pending commit processes it without merging it, checks the group information the committer published with it, and reports either OK or a refusal with a reason class:

    • tag: a membership or confirmation tag is wrong;
    • path: its path secret does not decrypt or does not fit;
    • group-info: the published group information does not match the epoch it derived;
    • policy: its own policy check refuses what the validator accepted;
    • credential: a chain or a revocation is wrong;
    • malformed.

    Reports travel over the device’s authenticated connection. The server keeps the first report of each device and counts accounts, not devices. Devices witness the pending commits of all their groups in the background when they connect, not only those of documents that are open.

  4. Decision. The first report starts a grace period, 10 seconds by default. At its end, the commit is confirmed if at least one witness account reported OK and none refused. A single refusal drops it: a commit made honestly can be processed by every member, so a refusal means either a bad commit or a lying witness, and the second is the smaller harm. An owner’s report counts like anyone else’s, because a committer can make a commit work for the owners’ devices alone. If no witness reports at all within 72 hours, the commit expires: it is dropped without quarantine and its committer may retry.

  5. Outcomes. Confirmed: the new epoch becomes current, the old one is kept as the previous epoch, held Welcomes are delivered, and every device merges the commit (the committer merges its pending commit). Dropped or expired: the server discards the new state and every device discards the commit (the committer clears its pending commit). After a drop, the committer’s account is quarantined in the group: the server refuses commits by any of its devices, including resyncs and recovery joins, but not its updates, until an owner lifts the quarantine or removes the account. The exception is a resync or recovery join that a device of the joining account refuses with reason credential: only the joining device is blocked in the group (§13.4). Owners are told about every drop, quarantine and stuck report, with the reasons, without content.

  6. After confirmation. A witness device that cannot process a commit that was confirmed while it was offline reports it as stuck, with a reason class. A stuck report with reason tag, path or group-info quarantines the committer’s account, because the commit was bad for some members, and owners are told. The stuck device resyncs with its own device key, using group information that a member who processed the epoch published, or an owner’s client re-adds it.

  7. The committer meanwhile. The committer keeps sending updates in the current epoch while its commit is pending: OpenMLS 0.9.0 lets a member create application messages while its own commit is pending, as long as the commit’s proposals are carried inline, as this design’s always are (checked in its source, MlsGroup::create_message).

ParameterProposed default (SRV-103 measures and may change it)Why
Grace period10 secondsLets a refusal from another online witness arrive before the server confirms
Expiry without any report72 hoursFrees the group’s single pending slot; the committer retries
Pending commits per group1Bounds the server’s state: one more copy of the public state, at most 1.34 MiB at 1,000 members with the hybrid suite (SRV-002)
Report recordsUntil decided, then 30 daysOwners can review who refused what
QuarantineUntil an owner lifts it or removes the accountStops a member who sent a bad commit from trying again

What it costs. A commit by a user who is not an owner takes effect one round trip to another user’s online device plus the grace period later; owners’ commits are not delayed. Witnessing a commit costs a device what processing it costs: in the worst case measured by SRV-002, a first self-update after many members were added at once, about 42 ms natively and 100 ms in Chromium at 1,000 members with the hybrid suite.

What it does not solve. A server can still withhold or delay anything (availability). An owner’s commit, which takes one phase, that members cannot process stalls the group; that needs a compromised or broken owner device, and another owner’s move (F13) repairs it. A lying witness can drop honest commits of members who are not owners, or report them as stuck after confirmation, and so get their accounts quarantined until an owner acts (FM12); owners cannot tell from a single report whether the witness lied or the committer encrypted a bad secret to it, and can only go on patterns across reports. In a group that no other member visits, members who are not owners cannot change their keys or devices until someone does. A commit that works for every member who happens to be online, and fails for those who are offline, is confirmed; the members it fails for resync, and its sender is quarantined afterwards.

7.3 When the server and the clients disagree

#DisagreementTypical causeWhat happensHow it is noticedRepair
FM1The server accepts a commit that members cannot process (tags, path secrets, group information)A malicious or broken member (T25)Two-phase acceptance drops it at the first refusal; the committer’s account is quarantinedWitness reportsAutomatic; an owner lifts the quarantine or removes the account
FM2The server accepts a commit that clients’ policy refusesA validator bug, different policy versions, a compromised serverClients refuse it and report policy. A commit by a user who is not an owner is dropped; an owner’s commit leaves members behindA policy refusal is logged as a security eventFix the server; the owner commits again; policy versions (ADR-0030) prevent version mismatches
FM3The server refuses a commit clients would acceptA validator bug, an outdated server, a hostile serverThe committer cannot change the groupThe committer sees the reason classFix or upgrade the server; nothing to repair in the group
FM4The server relays updates that clients refuse (a viewer’s update, a commenter’s edit)A misbehaving member, a server bugClients discard themClients count discarded updates and may report the count, without contentAn owner removes the member
FM5The server refuses updates that clients would acceptA bug, a hostile serverEdits stay queued on the deviceThe user sees that edits are not deliveredFix the server
FM6The server delivers different commits to different members (a fork)A hostile server (T26)Members continue in separate epochs and stop seeing each other’s editsCollaborators’ edits and presence vanish; optional group codes differAn owner’s move (F13); CRDT merges both sides’ edits afterwards
FM7The server withholds commits or messages from some membersA hostile server, a bugThose members fall behindSequence numbers and missing acknowledgementsFetch again; past the log’s retention, a resync
FM8The server is behind its membersA restore from a backup (T33)The server refuses members’ commits as being for the wrong epochEpoch mismatch errorsCatch-up (§7.5)
FM9The server’s device lists are older or newer than a client’sA withheld list, a restoreThe server may accept a revoked device or refuse a valid oneClients compare list versionsClients upload the newest signed list they have, before any commit in catch-up; the server then refuses anything from revoked devices, and the user’s own devices can stop a revoked device’s join (§13.4)
FM10An epoch is confirmed that some members cannot processA commit that worked only for the members who were online, or a bugThose devices are stuckTheir stuck report; the committer’s account is quarantinedThe stuck devices resync, or an owner re-adds them
FM11The server’s roster differs from the clients’Not possible while both follow the same commits; otherwise it is FM2 or FM8———
FM12An honest commit is dropped, or its committer quarantined after confirmation, because a lying witness refused it or reported it as stuckA malicious memberThat account’s commits are refusedOwners see every refusal and stuck reportAn owner lifts the quarantine; one report cannot tell a liar from selective encryption, so owners judge by patterns before removing anyone
FM13The state the validator’s MLS library reaches after a commit differs from the state its policy approvedA bug in the validator or the libraryThe server quarantines the whole group and accepts nothing more for it (the safety net of the SRV-002 validator)The group stops; owners are toldFix the server; an owner’s move if needed

7.4 The kind of an update stays encrypted

ADR-0016’s amendment of 2026-10-07 (design detail 3) records the owner’s decision of 2026-10-09 that the server is not told whether an update is an edit or a comment, and asked SRV-003 to confirm it. Confirmed; no change. MLS lets a sender attach authenticated data that the server can read; putting the kind there would let the server also refuse a commenter’s edits, but:

  1. Security does not depend on it: every client refuses a commenter’s edits after decrypting them (SRV-002 clients_reject_updates_whose_sender_lacks_the_role), and the server already refuses viewers’ messages, leave requests aside.
  2. It would tell the server which updates are comments, and with sizes and timing, how much each person comments and when (T9), contrary to ADR-0016 Decision 9.
  3. It would let a hostile server suppress comments selectively, which it cannot do now without suppressing everything.
  4. A commenter who floods the group with edits only costs others some decryption; the server’s per-device rate limits and quotas bound that (F6.4).

Two distinctions stay visible. The server must know what to store: whether a message is ephemeral (presence, relayed and never stored) or durable. And it must recognize a leave request, which it accepts from every role and which stops delivery to the leaving user (F4, ADR-0030); that tells it nothing the leaving device would not tell it anyway. Snapshot-key messages, group-moved notices and link notices are durable updates like any other and look the same to the server.

7.5 Catching up after a restore

A server restored from a backup is behind every group that changed since the backup (FM8). Members still hold those commits, so they bring the server up to date (ADR-0030):

  1. After a restore the server ends every session, marks stored key packages and pending commits as stale, and puts every group in catch-up.
  2. Member devices upload the commits they merged after the server’s restored epoch, oldest first. Each is checked against the public state and the policy as usual, with two differences: its signature, not the uploading connection, shows who made it, and it is not witnessed again, because its members already merged it.
  3. A commit is accepted only if a device of another account uploads it, or an owner’s device does, or the group has only one account. A member therefore cannot slip in its own commit that was dropped before the restore.
  4. If devices upload different commits for the same epoch, the server stops catching up that group and tells its owners; the chain an owner’s device uploads decides, and members on the other branch resync.
  5. Devices also upload their newest signed device lists, which the server accepts when they are newer and correctly signed, and replace their key packages. Lost updates and snapshots are sent again from members’ CRDT state through the normal sync protocol (SRV-104).
  6. Reports and quarantines recorded after the backup are lost. A quarantined account can then commit again; if it sends another bad commit, witnesses drop it and its account is quarantined again. Owners, who were told of the first quarantine, can remove the account.

Revoked devices and conflicting chains (ADR-0030, amendment 2026-10-26; §13.4). Devices upload their newest signed device lists before any commit, and the server checks every uploaded commit, resync and recovery join against the newest list it then holds, refusing anything signed by a revoked device. It then drops uploaded chains that contain commits from revoked devices. If owners’ devices still upload conflicting chains, for example after a fork that a hostile server caused before the restore (FM6), the server accepts neither, keeps the group stopped and tells its owners, who move it to a new group (F13). Until a device that knows a revocation has reconnected, the server still checks against the restored, older list (FM9).

8. Compliance escrow member

Phase 4 (E4-ENTERPRISE in plan/05); ADR-0016 Decision 10. This section is the design recommendation for the Phase-4 work package and the abuse cases it must handle. The flow is F15.

8.1 Design

  1. A member, not a back door. The escrow member is an ordinary MLS member: one device of a dedicated escrow account of the organization, with the role viewer, certified like any device (§5.2). Everything about it is visible in the group: it can be added only by a commit and every member’s client shows it.
  2. Passive, with an offline key. The escrow device never commits and never sends. Its private keys stay offline, in a hardware security module whose own quorum feature requires several officers (k of n) to approve each use; splitting the key in software instead would need secret sharing, which MLS’s providers do not include and which would need its own ADR (ADR-0016 Decision 12). The organization uploads a stock of the escrow member’s key packages, made offline in advance. A member must process every commit in order to read later epochs, so lawful access means fetching the group’s retained log and the escrow member’s Welcome and processing them offline, from the escrow member’s join onwards. The escrow member therefore needs the ratchet tree of the epoch it joined. Carrying the tree in its Welcome would depart from ADR-0016 design detail 1 (“Welcomes carry no ratchet tree”) and need an amendment of ADR-0016 in the Phase-4 work package; this section recommends instead that the server keep the tree of the join epoch, with the Welcome and the log, for the escrow retention period, which leaves design detail 1 as it is. No online service ever decrypts.
  3. Scope. The policy covers only groups whose owners are accounts of the organization. Clients add the escrow member only when the organization’s managed configuration says so, and they pin the organization’s escrow identity from that configuration rather than from the server alone. The server enforces presence: it refuses updates in a group of the organization until the escrow member is in it, and refuses commits that remove it.
  4. Visibility to outsiders. Before a user from outside the organization joins an escrowed document, their client tells them that the organization can read it, and they can refuse.
  5. Retention. Escrow needs the log kept, which contradicts minimization (§9). Escrowed groups therefore have a retention period of their own, stated in the policy and shown to members.
  6. Zero knowledge stays. The server never holds the escrow key (ADR-0015 Decision 6).
  7. Alternative for Phase 4 to weigh. Editors could instead seal each snapshot key to an escrow public key, without an escrow member: the escrow then sees only snapshots and the log need not be kept, but neither the server nor members could check that a modified client really does it. The member design is checkable by everyone, which is why this section recommends it.

8.2 Abuse cases

#AbuseBy whomEffectMitigationRemaining risk
EA1Escrow added to groups outside the policy, unnoticedServer operator, organization administratorReads documentsAdding is an owner’s commit shown to every member; clients add escrow only from the organization’s managed configurationAn organization that manages its employees’ clients can push such a configuration
EA2Escrow on documents of people outside the organizationOrganization administratorReads outsiders’ contentOnly groups owned by the organization’s accounts; outsiders are warned before joining and can refuseOutsiders who ignore the warning
EA3The escrow key is used without due processEscrow officersReads documentsThe hardware module’s quorum (k of n), an offline key, every access logged; notice to members afterwards where the law allowsCollusion of k officers
EA4The escrow key is stolenExternal attackerEvery escrowed document retained so far is exposedOffline hardware storage; periodic rotation (a new escrow member replaces the old one); minimal retentionCiphertext retained until the theft
EA5A fake escrow identityServer operatorThe server could read organization documentsThe escrow identity is pinned through the organization’s managed configuration; administrators compare its safety numberUnmanaged clients rely on TOFU (T23)
EA6Members remove escrow to evade the policyMemberPolicy breachThe server refuses such commits; owners’ clients refuse to make themMembers can always copy content: escrow is not data-loss prevention
EA7The escrow member sends contentAnyone holding its keyForged edits or commentsViewer role: clients refuse its updates; it never commits—
EA8Escrow used for real-time surveillanceOrganizationContinuous readingPassive design: no online decryption; access only by processing the retained log; access frequency auditedAn organization could run the key online anyway; a matter of policy and law
EA9Logs kept indefinitelyOrganization, operatorMore exposure to future key theft and to harvest now, decrypt laterRetention stated in the policy; the hybrid suite; deletion after the period—
EA10The escrow member never updates its keys, so its leaf has no post-compromise securityDesignA stolen escrow key reads until the member is replacedPeriodic replacement of the escrow memberThe window until replacement

9. Metadata inventory

This is the field-by-field list that v0 §6 item 2 asked for: what the server stores (§9.1), what it sees without storing (§9.2), and what that lets an operator learn (§9.3). Retention periods are proposed defaults that SRV-102 and SRV-105 make configurable; “operator” means whoever runs the server. SRV-102’s schema must contain nothing that is not listed here (§13.1).

9.1 What the server stores

Accounts (SRV-101, SRV-102)

FieldWhy it is neededWho can read itRetention
Account ID (random, 128 bits)Key of every other record; appears in credentials and rostersServer, administrators, members of the account’s groupsAccount’s lifetime, then 30 days
Display nameShown to people who look the user up and in documents’ member listsAnyone who looks the user up; members; administratorsAccount’s lifetime; the user can change it
Email addressInvitations, exact-match look-up by collaborators, optional email noticesAdministrators; look-ups only confirm a matchAccount’s lifetime
Identity provider: issuer and subjectFinding the account at sign-in (F1.2)Server, administratorsWhile the link exists
Status, created, disabled (dates)AdministrationAdministratorsAccount’s lifetime
Server administrator flagAdmin consoleAdministratorsAccount’s lifetime
Quota and usage countersStorage limits (T13)Administrators, the userAccount’s lifetime
Notification preferences: email notices on or off, muted groupsF14The userAccount’s lifetime
Organization and policies (Phase 4)Escrow, device approval, client restrictions (COL-13)Organization administratorsAccount’s lifetime
Last sign-in (day only)Handling inactive accountsAdministrators, the userOverwritten

Sign-in

FieldWhy it is neededWho can read itRetention
Passkey credential ID, public key, signature counter, transportsWebAuthn sign-in (F2)Server, the userUntil the passkey is removed
Passkey label chosen by the user, created and last used (day)Lets the user manage passkeysThe user, administratorsUntil removed
Wrapped recovery secret per passkey (opt-in)Passkey unlock (F10)Server, as ciphertextUntil switched off or the passkey is removed
Pending OIDC sign-ins: state, nonce, PKCE verifier, return path, the pre-sign-in cookie’s identifierF1Server10 minutes, or until used
Pending desktop sign-ins: loopback address, PKCE challenge, one-time codeF1Server10 minutes; the code 60 seconds, single use
WebAuthn challengesF2Server5 minutes, or until used
First administrator’s bootstrap token, as a hashAdmin bootstrap (F17)ServerUntil used, at most 24 hours
Not stored: attestation statements, authenticator model (AAGUID)———

Sessions

FieldWhy it is neededWho can read itRetention
Hash of the session token, account ID, device IDAuthenticationServerUntil expiry or sign-out, then deleted
Created, last used (coarse), expires, sign-in methodExpiry, the user’s session listServer, the userSame
Not stored: IP address, user agent———

Devices

FieldWhy it is neededWho can read itRetention
Device ID (random), account IDIdentifies the device’s recordsServer, the user, administratorsUntil the device is removed, then 90 days
Device certificate and public keysChecking chains; authorizing connections (§5.2, §5.3)Server, members of the device’s groups, the userSame
Declared client application and versionCOL-13: users see it, administrators may restrict itThe user, administratorsSame
Approval state, approving device, administrator approval, datesCOL-13; auditThe user, administratorsSame
Pending approval: the new device’s transfer key, and the sealed approval waiting to be collected (ciphertext)F3Server; the approval only as ciphertextUntil collected, at most 24 hours
Revoked at, reason classRevocation (F11)The user, administrators90 days after revocation
Last activity (day)COL-13 shows itThe user, administratorsOverwritten
Not stored in clear: device names (encrypted in the user’s settings)———

Directory and recovery data (Phase 3)

FieldWhy it is neededWho can read itRetention
Identity public key, device-signing certificateOthers verify the user’s devices (§5.2)Approved devices that look the user upCurrent, and the previous ones for 90 days
Signed device list with its versionOthers check which devices are currentSameCurrent version; earlier versions 90 days
Secret store (ciphertext) and its versionRecovery (F9)Server, as ciphertext; the account’s devicesCurrent version only; older versions are deleted when replaced
Key-backup entries: group ID, snapshot ID, sealed snapshot key, createdRecovering documents (F8, F9)Server, as ciphertext; the account’s devicesLatest entry per group; deleted when the user leaves the group

Key packages (SRV-103)

FieldWhy it is neededWho can read itRetention
Device ID, ciphersuite, key package (public keys, credential, lifetime), uploadedAdding the device to groupsServer; approved devices that claim itUntil claimed or expired
Claim counters per claimer and target, in memoryRate limits (T34)ServerMinutes
Not stored: who claimed whose key package, beyond the counters———

Groups and their public state (SRV-103)

FieldWhy it is neededWho can read itRetention
Group ID (random, chosen by its creator), ciphersuite, epochRouting and validationServer, membersGroup’s lifetime
Ratchet tree: every member device’s credential and public keysValidation; newcomers fetch it (ADR-0016 design detail 1)Server, members, devices being addedCurrent epoch, and the previous one while needed
Group context: roster (account IDs and roles), required capabilities, policy versionRole enforcementServer, membersGroup’s lifetime
Group information with the external public key, signed by a memberResyncs and recovery joins (F9)Server; devices of accounts in the rosterCurrent epoch only
Pending commit, reports, quarantined accounts, blocked devices, catch-up state (§7.2, §7.5, §13.4)Two-phase acceptance and catch-upServer; owners see reports, quarantines and blocksUntil decided, then 30 days; quarantine until lifted; a block until lifted or the device is removed
Created, last commit (dates)Retention, operationsServer, administratorsGroup’s lifetime

A user’s document index and settings are groups like any other, with one account; the server sees only that (§5.2).

Message log and delivery (SRV-103, SRV-104)

FieldWhy it is neededWho can read itRetention
Group ID, sequence number, kind (commit, update, leave request, outcome), epoch, size, received atOrdering, catch-up, retentionServerAs the payload
Commit payloads, in public framing: who committed, which devices were added or removed with their credentials, every roster and role changeDelivery, catch-up, validationReadable by the server; membersUntil every current member device has acknowledged them, at most 90 days; devices behind that resync
Update payloads, in private framing (ciphertext)Delivery and catch-upServer, as ciphertext; membersUntil a later snapshot covers them, at least 7 days and at most 90 days
Outcome records of two-phase acceptanceDevices that were offline learn whether to mergeServer, membersAs the commit they decide
Last acknowledged sequence number, per device and groupCatch-up; deciding when commits can be deletedServerWhile the device is a member
Stop-delivery flags of leave requests, per device and groupLeaving (F4)ServerUntil the removal commit
Welcome (ciphertext), its target deviceJoiningServer, the target deviceUntil acknowledged, at most 30 days; an escrow member’s Welcome and the ratchet tree of its join epoch for the escrow retention period (§8)
Not stored: which device sent an update; presence messages———

Blobs (SRV-102)

FieldWhy it is neededWho can read itRetention
Blob ID (random), group ID, size, created, kind (snapshot or other)Storage, quotas, retention of superseded snapshotsServerUntil members delete it, a newer snapshot supersedes it, or the group is deleted
For snapshots: the log position the snapshot covers, as its uploader states itDeciding when updates may be deletedServerAs the blob
Payload (ciphertext), in the blob storeSnapshots, media, original packagesServer, as ciphertext; membersSame
Not stored: who uploaded it (usage counters are updated instead), a content hash (no deduplication across groups, which would reveal equal content)———

Links (Phase 3)

FieldWhy it is neededWho can read itRetention
Link ID (random, 128 bits), group ID, created by (account ID), created, expires, revokedLink sharing (F5)Server; owners of the groupUntil expiry or revocation, then 7 days
Signed wrapped snapshot keys (ciphertext), and the blob IDs of those snapshotsLink reading; which blobs the link may fetchAnyone with the link ID, as ciphertext; the server for the blob IDsDeleted at revocation or expiry
Not stored: who opened a link, when, from where———

Invitations, notifications, audit

FieldWhy it is neededWho can read itRetention
Invitation: token hash, invited email address, inviting account, kind, created, expires, usedClosed registration (OPS-09), pending sharesServer, administrators, the inviterUntil used or expired, then 30 days
Notification: recipient, event type, opaque reference, created, deliveredIn-app and email notices (F14)Server, the recipientUntil delivered, then 7 days
Security audit log: event type, account ID, device ID, time, outcome; optionally a truncated IP address if the operator turns it onInvestigating incidents; the user’s own account historyAdministrators; the user for their own events90 days

Audit events: sign-ins and failures; sessions created and ended; passkeys added and removed; devices registered, approved and revoked; recoveries and passkey unlocks; administrator actions and policy changes; quarantines and stuck reports; restores. The audit log is stored data with a retention period, not part of the operational log, which must never contain account identifiers (AGENTS.md §6).

Operations

FieldWhy it is neededWho can read itRetention
Operational log lines: request ID, method, route template, status, duration (SRV-001, in place)OperationsOperatorThe operator’s log system; 14 days recommended
Metrics with labels of low cardinality: route template, status class, message kindOperations (OPS-03)OperatorOperator’s choice
Rate-limit counters per address, account, device and group, in memoryAbuse prevention (T13)ServerMinutes
Backups: copies of everything above and of the blob storeRecovery from disastersOperatorOperator’s policy; encrypted, 30 days recommended
Not in logs or metric labels: account, device, group, blob or link IDs; IP addresses; paths; query strings; bodies; header values; tokens (tested by bayan-server’s tests/logging_*.rs)———

Outside the server, under the operator’s control: the reverse proxy’s access logs (client IP address, time, method, path, which may contain link IDs and blob IDs, status, size, user agent), the object store’s request logs, the email relay’s logs, and the organization’s identity provider’s logs. The deployment guide (SRV-105) should recommend switching the proxy’s access logs off or logging without paths and addresses (§4.6 IE3), and the API should carry group and blob IDs in request bodies or WebSocket frames rather than paths where it can.

9.2 What the server sees without storing it

  • IP addresses and ports, through the proxy’s forwarded headers, and user agents: where and with what people connect.
  • When each device is connected, and which groups it subscribes to.
  • For every update: which device sent it, how large it is and when, which reveals who edits which document, when, how much, and the rhythm of typing.
  • The rate of ephemeral (presence) messages: who is looking at a document right now.
  • Snapshot and media sizes: each document’s size and growth and how many images it holds.
  • Key-package claims: who is about to share with whom, before the commit.
  • Link fetches: when a link is used and from which address.
  • Look-ups: which email addresses someone looks up.

9.3 What this lets an operator learn

From §9.1 and §9.2 together, an operator can learn the social graph (who shares with whom, with which role, from the readable commits), activity (who works on which document when, and how much), the documents’ sizes and growth, each user’s devices and client applications, approximate locations from IP addresses, the organization’s structure, and typing rhythms that may identify a person. It cannot learn content, titles, folder structure, comments or who mentions whom. Mitigations already designed: titles and structure encrypted (ADR-0016 Decision 9); the sender of updates not stored; no IP addresses stored by default; coarse dates; the retention limits above. Options for later, each with a cost in bandwidth or latency: batching updates at fixed intervals and padding them (MLS supports padding), padding blobs to size classes, and decoy traffic. This is threat T9, accepted as residual in §12.

10. Security requirements per server endpoint

v0 §6 item 6 asked for a checklist per planned endpoint. §10.1 applies to every endpoint; §10.2 adds what each one needs. The paths are illustrative: SRV-105 publishes the real API in specs/protocols/server-api.md, and each Phase-1 server work package proves these requirements for the endpoints it builds (§13.1).

10.1 Requirements for every endpoint

#Requirement
B1Transport. Reached only through the reverse proxy over TLS 1.3 with X25519MLKEM768 offered (ADR-0028); HSTS sent by the proxy; the server listens only on loopback or a private network that nobody else can reach (A1, §4.6).
B2Authentication is required unless §10.2 says “none”. Sessions belong to devices; security-relevant requests carry the device key’s signature on a fresh challenge (§5.3).
B3Authorization is checked by the server on every request against its current state (role, membership, device approval, revocation, quarantine), never against what the client claims, and never depends on which client software is connecting (SEC-13).
B4Input. A size limit per endpoint and per message kind; strict parsing with depth and count limits; every decoder fuzzed (SEC-13, plan/07). Unknown fields are ignored, but count towards the size.
B5Rate limits and quotas per address for unauthenticated requests, and per account, device and group for authenticated ones (T13).
B6Logging. One content-free line per request with the route template only (SRV-001); security events go to the audit log (§9.1), never to the operational log.
B7Same origin only. No CORS. State-changing requests authenticated by cookie check Origin; session cookies are SameSite=Strict; the one exception is the short-lived pre-sign-in cookie of F1, which must be SameSite=Lax to come back with the identity provider’s redirect and grants nothing by itself. WebSocket upgrades check Origin too.
B8Responses carry Cache-Control: no-store when they are not public files, the security headers of ADR-0014 (already on every response, SRV-001), and binary payloads as application/octet-stream.
B9Errors are generic and content-free, and do not reveal whether an account, invitation or link exists.
B10Time limits: the header-read and request time limits of SRV-001; idle and ping time limits on WebSockets; bounded queues with backpressure.
B11Identifiers are random and at least 128 bits long, so they reveal no counts (as request IDs already do, SRV-001); secrets such as tokens are 256 bits and stored only as hashes.
B12Challenges, codes and tokens are single use and expire, and secrets never travel in URL paths or queries that proxies log: invitation and link secrets travel in fragments, sign-in codes are single use within seconds.
B13Tests. Every endpoint is in the authorization matrix that runs with a hostile test client (SEC-13, SRV-103).
B14Clocks. Times sent by clients are hints, never inputs to a security decision (A5).
B15Client addresses. Forwarded-address headers are believed only from the configured proxy (§4.6 IE1).

10.2 Endpoints

Endpoint (illustrative)Work packageAuthenticationWho may use itSpecific requirements
GET /healthz, /readyz, /versionSRV-001 (in place)NoneAnyoneStatus and version only; no store; the proxy may restrict them to monitoring
Static files of the web appSRV-001 (in place), WEB-103NoneAnyoneADR-0014 headers; integrity hashes; nothing outside the web folder; no directory listings (F16)
/auth/oidc/start, /auth/oidc/callbackSRV-101None; creates a sessionAnyoneState bound to the pre-sign-in cookie, nonce and PKCE; response_mode=query; the iss check; exact redirect URIs; ID-token checks; accounts by issuer and subject; relative return paths only; a new session identifier (F1)
/auth/native/start, /auth/native/token (desktop sign-in)SRV-101Browser session, then a device tokenAnyone signed inLoopback redirects only, to 127.0.0.1 or [::1] on any port (RFC 8252); PKCE with S256 required; codes single use for 60 seconds; the token bound to the device key
Passkey registration: options, finishSRV-101Invitation token, or a recent sign-in when adding a passkeyInvitees; the account’s userRP ID is the configured domain; origin checked; user verification required; attestation “none”; challenges single use for 5 minutes; invitations single use (F2)
Passkey sign-in: options, finishSRV-101NoneAnyoneAs above; no account enumeration; the signature counter checked (F2)
Sessions: list, end, sign outSRV-101SessionThe account’s own sessions; administrators any, auditedEnding another device’s session needs an approved device (§5.3)
Invitations: create, acceptSRV-101Session to create; none to acceptAdministrators, or users the policy allows256-bit tokens stored as hashes, carried in the link’s fragment and spent only by POST; 7-day expiry; single use; the same answer for unknown and used tokens (F2)
Account: read, change profile, email or notification preferences, deleteSRV-101Session of an approved deviceThe account itselfRe-authentication for email changes and deletion; deletion after a grace period, announced to the user’s devices
Devices: register as pending, listSRV-101 (groundwork), Phase 3Session; a pending device may only register itselfThe account’s own devicesDeclared client application recorded; pending requests expire after 24 hours; rate limits (F3)
Devices: approve, revokePhase 3Approved device, with a device-key signatureThe account’s own devices; the administrator approval gate (COL-13)Certificates and device lists signed and checked against the identity key; device-list versions only increase; the sealed approval stored only until collected (F3, F11)
Directory: publish identity key, device-signing certificate, device listPhase 3Approved device, or a recovering device with a recent sign-inThe account itselfSignatures checked; versions only increase; only the identity key may replace the device-signing certificate
Directory: look up a user, fetch an identityPhase 3Approved deviceAny approved device; optionally only within the organizationExact match on email address or account ID only; rate limits; the same answer for no match; no listing (T38)
Recovery: secret store, key backup, passkey-unlock wrapsPhase 3Session; a pending device may only read its account’s secret store, key-backup entries and wrapsThe account itselfBounded sizes; versions only increase; reads during a recovery are audited (F9, F10)
Key packages: upload, count, claimSRV-103Approved deviceUpload: for the device itself. Claim: any approved deviceEvery package checked (signature, lifetime, accepted suite, credential naming the uploading device and chaining to its account, roster capability); at most 100 stored per device (SRV-002); claims limited per claimer and per target (T34)
Groups: registerSRV-103Approved deviceThe creator, as the only member and owner (SRV-002)Unique group IDs; only accepted suites, never provisional code points for real documents (ADR-0016)
Groups: fetch tree, fetch group informationSRV-103, Phase 3Approved deviceTree: members and devices named in a held Welcome. Group information: devices of accounts in the rosterSize limits; no store; recovery joins also need a recent sign-in (F9)
Sync WebSocket (/sync)SRV-103, SRV-104Session plus a device-key signature on a challenge; Origin checkedMember devices, per groupPer message: membership, role, epoch, framing, with leave requests accepted from every role, after which nothing more from that user in that group; commits through the validator and §7.2; size limits per kind; rate limits per device and group; ephemeral messages never stored; reports counted only from witnesses; resumption by sequence numbers; catch-up after a restore (§7.5); nothing that identifies users in logs (F6)
Blobs: upload, download, delete, list for clean-upSRV-102Approved device, or a valid link ID for downloadUpload: owners and editors. Download: current member devices, or link readers for that link’s blobs. Delete: ownersRandom blob IDs; at most 256 MiB per snapshot (SRV-002); quotas; streamed, never held whole in memory; served as application/octet-stream with nosniff (F8)
Links: create, update, revokePhase 3Approved deviceCreate: owners of the group. Update and revoke: the creating owner’s devices; revoke also any owner128-bit random link IDs; expiry; wrapped keys deleted at revocation (F5)
Links: fetchPhase 3None (the link ID)Anyone with the linkRate limits per address and per link; the same answer for unknown, expired and revoked links; no access records; noindex (F5)
Notifications: list, acknowledgePhase 3Approved deviceThe account itselfContent-free payloads (F14)
Metrics (/metrics)SRV-105A separate listener on a private address, or a token from a fileOperatorNo identifiers as labels; never routed through the public proxy
Admin console APISRV-105, Phase 3 (OPS-06)Administrator session with a passkey and recent re-authenticationAdministratorsEvery action audited; counts and sizes rather than memberships; protection against cross-site requests; a separate path prefix that the proxy can restrict (F17)
Command line, backup and restoreSRV-101, SRV-105Access to the hostOperatorBootstrap tokens printed to the terminal, never logged, kept only as hashes; consistent backups; the restore procedure and catch-up of §7.5 (F17)
“Source code” link (OPS-08)SRV-105NoneAnyoneConfigurable by operators of modified versions; static
SCIM provisioningPhase 4Bearer token from a file, compared in constant timeThe organization’s identity providerDeprovisioning ends sessions and blocks devices at once and asks owners’ clients to remove the user from groups (§13.3)
Escrow policyPhase 4Organization administratorThe organizationChanges audited and shown to members (§8)

11. Principal threats

The register keeps the numbers of v0. Rows T1–T22 are v0’s, with their status and mitigations brought up to date; T23–T39 are new in v1. “Where” points to the analysis.

#BoundarySTRIDEThreatMitigationsStatusWhere
T11E, DMemory-corruption exploit in a parser (ZIP, XML, fonts, images, EMF)Rust with forbid(unsafe_code); no C parsers; fuzzing; resource limits (ADR-0006)In place for bayan-opc (CORE-005); planned for the restv0
T21E, IActive content: macros, DDE, OLE, remote templates, external links, dangerous URL schemes, UNC credential leaksNever auto-run or auto-fetch; consent per document (ADR-0023, CORE-130)Plannedv0
T31DDecompression bombs, entity expansion, deep nesting, giant tablesDTDs rejected; limits on sizes, ratios, depth, counts; cancellable layoutIn place for bayan-opc; planned for the restv0
T42E, DMalformed messages from a compromised shell component or web page scriptSchema validation at the boundary; no panics across it; strict CSP and Trusted Types on the webIn place in the engine skeleton (CORE-007)§4.6
T54IThe server reads document contentMLS with the hybrid post-quantum suite; zero-knowledge invariant (ADR-0015, ADR-0016); validated by SRV-002; the exceptions are T11 and T23Planned§6, G1
T64S, EThe server adds a device or member to read documents (“ghost member”)Membership changes are commits every member checks; devices are cross-signed and checked against pinned identities (§5.2); for identities seen for the first time, see T23Planned (ADR-0016 Decision 6, details in ADR-0029)F3, F4
T74T, EA collaborator without write role, or the server, injects editsRoster in the group context; the server refuses viewers’ messages except leave requests; clients check every update’s sender and kindPlanned (tested in SRV-002)F6, §7
T84D, ECrafted CRDT updates or snapshots crash or exhaust a clientAuthenticated before import; limits; fuzzed import; isolation with time and memory budgets and restart (ADR-0008, amendment 2026-10-08)PlannedF6, F8, F5
T94IMetadata leakage: membership, roles, readable commits, timing, sizes, senders of updates, typing rhythmTitles and structure encrypted; the inventory of §9; senders and addresses not stored; retention limits; batching and padding optionsPartly mitigated; residual§9
T104IDeleted content persists in collaboration history and leaks to new membersSnapshots for newcomers without history by default; log retention (§9.1); “inspect document”; exported .docx without CRDT history unless the user opts inPlannedF8
T119T, IA malicious server serves modified web code to steal keysDesktop as the high-assurance client; browser devices hold no device-signing key and can be revoked (F3.13); SRI enforced with Integrity-Policy; engine files checked against built-in hashes (WEB-002) with one remaining gap (bayan-web issue 8); reproducible builds with published hashes; web code transparency when browsers ship it. It reaches what is shared with any account that uses the web app, and limits link sharing (T31) and passkey unlock (T28) in browsersResidualF16, F3
T124, 5SAccount takeover through weak sign-inPasskeys; hardened OIDC; device-bound sessions; rate limits; sign-in never gives keysPlannedF1, F2
T134, 5DAbuse: update floods, storage exhaustionQuotas; rate limits per device and group; size limits per message kind; bounded queues; role revocationPlannedF6, F8, §10
T146E, IA script or extension escapes the sandbox or leaks contentPure-Rust engine without ambient authority; capability prompts; WebAssembly components; limitsPlanned (Phase 5)v0
T157ECompromised AI sidecar or model fileSeparate process; models checked by hash or signature; no network by defaultPlanned (Phase 5)v0
T168TMalicious dependency or CI compromiseADR-0017: 24-hour minimum age, exact pins, audits, SHA-pinned actions, minimal permissions, provenance, signing; the workflow conventions of AGENTS.mdIn place (X-002, X-003)v0
T178TMalicious update delivered to usersSigned releases; signed update metadata with rollback protection; immutable releases (ADR-0026)Plannedv0
T183IClipboard or URL handling leaks content to other apps or sitesExplicit user actions only; confirmation before opening URLs; no automatic fetchingPlannedv0
T19labIThe private corpus leaksPrivate storage; aggregate-only reports; isolated reference machinePlannedv0
T202, logsIContent leaks through logs, crash reports or recordingsNo content in logs (tested in SRV-001 and CORE-007); opt-in, user-reviewed crash reports; recordings only on explicit request; for collaboration see T36In place for the server’s logs and the engine skeleton§9.1
T214S, T, D, EThird-party or modified clients, buggy or malicious, connect with a user’s valid credentialsThe server treats every client as untrusted; new devices need approval from a trusted device and optionally an administrator; clients check every update; device lists with revocation; a public conformance suite (SEC-13, COL-13, COL-14)PlannedF3, §7, §10
T224, 5IHarvest now, decrypt laterHybrid post-quantum key exchange in TLS (ADR-0028) and in MLS (ADR-0016, amendment 2026-10-04); key backups and device approvals sealed with the hybrid KEM; the account’s root secrets under 256-bit symmetric keysPlannedF3, F8, F9
T234S, IGhost user: at first contact, or by intercepting an invitation, the server hands out an identity it controls, and then reads what is shared with itPinned identities with blocking warnings on change; safety numbers and QR codes; verified marks; pins carried to new devices; key transparency to evaluate at the Phase-3 gateResidualF2, F4, §5.2
T244, 11S, EDuring an approval, the server slips in its own device, or hands the new device its own identity key and key-backup keyApproval authenticated out of band in both directions; no bare approval button; pending devices powerlessPlanned (ADR-0029)F3
T254DA member stalls a group with a commit that only members can check, or that works only for some membersTwo-phase acceptance: any refusal drops it, the sender’s account is quarantined, members it failed for resyncPlanned (ADR-0030)F12, §7.2
T264T, DThe server forks a group, giving different commits to different membersMembers stop at different epochs; optional group codes to compare; an owner’s move, after which CRDT merges both sidesResidualF6, §7.3
T274, 10, 11, 14S, E, DA sign-in taken over (identity provider, passkey, administrator) is used for more than signing inSign-in never gives keys; pending devices powerless; destructive and security actions need an approved device; notices to the user’s devices; auditPlannedF1, F2, F11, F17
T2811S, I, EThe recovery kit, or a passkey with passkey unlock, is compromised: the account follows, and with the server’s stored data the latest snapshot of every document can be read, now and later, until the user respondsThe kit is generated, kept offline and never sent; recovery joins need a sign-in and are visible; passkey unlock is opt-in and can be switched off by administrators; a new kit and new key-backup and device-signing keys after every recovery and any suspected exposure (§5.1)Residual by designF9, F10
T294DAccess cannot be recovered, or a document’s membership is frozen for goodRecovery joins; key backup; encouraging a second ownerPlanned (ADR-0029); residual if the kit is also lostF4, F9, F11
T304S, EDevice lists or revocations withheld, or old ones served againVersioned, signed device lists that only move forward, taken first in catch-up after a restore; removal from groups by MLS commits, independent of the lists; the user’s own devices refuse revoked devices’ joins, owners’ joins within a limit (F9.6, §13.4)Planned (ADR-0030, amendment 2026-10-26); residual against a hostile server that withholds lists and ignores refusalsF3, F9, F11
T314, 9IA link’s key is exposed: shared too widely, kept in history, or read by served web codeFragment never sent and removed on open; expiry; revocation; read-only; the desktop app opens linksResidualF5
T324SA session token or sign-in code is stolenCookie attributes; tokens in keychains; device-key proof of possession; PKCE; single-use codesPlannedF1, F6
T335, 14T, D, ERestoring a backup rolls state back: groups stall, revoked sessions return, used key packages are offered againAfter a restore: all sessions end, key packages are replaced, members catch the server up (§7.5)Planned (ADR-0030)F17, §7.5
T344D, IKey packages claimed to block additions, or to learn who is about to share with whomClaims only by approved devices; limits per claimer and target; expiry; claims not storedPlannedF4
T354I, EThe compliance escrow is abused: silent addition, scope creep, key theft, surveillanceVisible member; managed configuration; passive offline key under a hardware quorum; audit; retention policyPlanned (Phase 4)§8, F15
T362, 3IEngine recordings or diagnostics capture keys or decrypted content of collaborative documentsRecordings of collaborative sessions contain no MLS state or key material and are never uploaded (§13.3); keys never cross to the shell except wrappedPlanned§4.6, §13.3
T3712I, SNotifications leak metadata or carry phishingContent-free notices; email opt-in over TLS; no push services; plain-text emails with escaped namesPlannedF14
T384IThe directory is used to find out who has an accountExact-match look-ups by approved devices only, rate limited, the same answer for no matchPlannedF4
T394S, EA stolen device or a stolen device-signing or identity key is used for resyncs, recovery joins, to certify devices or to sign device listsDevice must be in the latest signed list the server holds; the user’s own devices refuse revoked devices’ joins, owners’ within a limit, and their refusal blocks only the joining device (F9.6, §13.4); they alarm on list changes attributed to them; joins are shown to all; the device-signing key is rotated after a loss and never held by browser devicesPlanned (ADR-0029; ADR-0030, amendment 2026-10-26)F3, F9, F11

12. Accepted residual risks (v1)

  1. An operator can deny service, and can observe the metadata listed in §9 (T9).
  2. People who use the web app trust the origin that serves its code (T11), and so, until that account’s browser devices are revoked, does everyone who shares with an account that uses the web app. The same holds for link recipients in a browser (T31) and for passkey unlock in a browser (T28). The desktop app removes this trust for accounts that do not use the web app; self-hosting removes third parties.
  3. At first contact, a user who does not compare safety numbers trusts the server’s directory (T23), until key transparency is adopted.
  4. A collaborator who can read can copy; a former member keeps what they already had, including snapshots they received.
  5. Compromised endpoints are out of scope; revocation and post-compromise security limit what happens afterwards.
  6. A user can run any client with their own account; it can do only what that user may do.
  7. A hostile server can fork a group; members notice only indirectly unless they compare group codes (T26).
  8. Whoever holds the recovery kit owns the account, and, with the server’s stored data alone, reads the latest snapshot of every document until the user makes a new kit and rotates the keys it protects; with passkey unlock, the passkey alone does the same (T28).
  9. Losing every device and the recovery kit loses the identity and the documents nobody else holds; a document whose only owner is gone keeps working but cannot change its members (T29).
  10. Links are bearer secrets, and a link’s view can be out of date.
  11. A lying member can drop the commits of members who are not owners, or report them as stuck, and get their accounts quarantined until an owner acts, and an owner cannot tell a liar from selective encryption by a single report; a commit that works only for the members who are online is confirmed, and the others resync; a compromised owner device can stall a group until another owner moves it (§7.2).
  12. Updates are signed: members can prove to one another, and to anyone who trusts their keys, who wrote what. There is no deniability.
  13. In escrowed documents (Phase 4), the organization can read what its policy allows.
  14. After a restore from backup, groups that changed since the backup wait until their members’ clients reconnect and catch the server up (§7.5).
  15. A hostile server that withholds a newer device list (T30) and ignores the refusals it receives can let a revoked device rejoin for members who have not seen the newer list. After a restore, until a device that knows a revocation reconnects, and when the user’s other devices stay offline beyond the limit of §13.4, a revoked owner device can still rejoin.

13. Recommendations

13.1 Phase-1 server work packages

This change updates the drafts in workpackages/phase-1/server.md; the Phase-0 gate review refines them into Ready briefs.

Work packageRecommended additions
SRV-101Sessions bound to devices with proof of possession of the device key; the desktop app signs in through the system browser with a loopback redirect and PKCE; the OIDC checks of F1, including the SameSite=Lax pre-sign-in cookie, and the passkey checks of F2; pending devices without powers; invitations as hashed, single-use tokens carried in fragments; re-authentication for sensitive actions; the security audit log of §9.1 with its retention; no IP addresses or user agents stored by default; forwarded addresses trusted only from the proxy; the first administrator’s bootstrap token printed only to the operator’s terminal; evidence for every requirement of §10 that its endpoints touch
SRV-102The metadata schema of §9.1, with nothing that the inventory does not list and its retention jobs; random identifiers; no uploader per blob and no deduplication across groups; quotas per account and group; object keys made only of random IDs
SRV-103The server side of two-phase acceptance (§7.2, ADR-0030), with its state persisted, and the rules of §13.4 for revoked devices; key-package checks and claim limits; group information kept for resyncs and recovery joins; device authentication and Origin checks on the WebSocket; size limits per message kind, rate limits per device and group, backpressure; the sender of updates never stored and ephemeral messages never stored; the retention of §9.1; the failure modes of §7.3 in the hostile-client suite
SRV-104In the sync protocol: sequence numbers and catch-up, outcome, report and stuck messages for two-phase acceptance, queuing CRDT updates rather than ciphertext while offline, version vectors to find missing updates, leave requests accepted from every role (ADR-0030), catch-up after a server restore (§7.5), with device lists before commits, options for batching and padding, and group IDs in frames rather than paths
SRV-105Metrics without identifiers on a private listener; the audit log’s storage and export; backups with the restore procedure of §7.5, device lists first (§13.4), and a test of it; deployment guidance on HSTS at the proxy, on forwarded headers, on the proxy reaching the server only over loopback or a closed network, and on switching the proxy’s access logs off or logging without paths and addresses; this document’s §10 as the checklist for server-api.md

13.2 Decisions

  • ADR-0029 (accepted on 2026-10-17): identity keys, device approval and account recovery. It makes ADR-0016 Decisions 6 and 7 concrete (§5) and fixes three of the problems found: recovery joins and resyncs so that a user who lost every device can rejoin their documents (T29); a key backup so that documents only the user held survive (T29); and device approval authenticated in both directions, with no device-signing key on browser devices (T24, T11). It also states plainly what holds at first contact (T23) and what the recovery kit and passkey unlock expose (T28). It changes design detail 3 of ADR-0016’s amendment of 2026-10-07 in two points: groups accept resyncs and recovery joins, which are external commits; and the required credential type is the one ADR-0029 chooses instead of basic credentials.
  • ADR-0030 (accepted on 2026-10-17): commit acceptance, departures and group repair. It gives the details of two-phase acceptance (§7.2), with three departures from the owner’s earlier direction, which the owner accepted. It also lets users leave without waiting for an owner’s goodwill (F4.11), adds a policy version, and defines how groups catch up after a server restore (§7.5, T33). It confirms the owner’s decision that the kind of an update stays encrypted (§7.4).
  • Both needed the owner’s decision (ADR-0001 Decision 5: they change security design). The owner accepted both on 2026-10-17, and decided against the expert review of ADR-0029’s constructions before Phase 3 that the proposal made a condition. ADR-0029 combines signatures, key derivation and encryption into constructions of its own: device certificates and device lists, the secret store, the key backup, passkey wrapping, the authenticated approval exchange (QR code or short code), link signing and the device’s proof of possession. Their first review from outside the project is therefore the external audit before collaboration leaves beta (SEC-08), which must cover both ADRs (risks R-06 and R-11).

13.3 Later phases and other repositories

  1. Core, Phase 3 (bayan-mls, bayan-sync). The client side of §7.2 (holding staged commits, reports, background witnessing, stuck reports); credential chains, device lists and pinned identities (§5.2); resyncs, recovery joins and the key backup (F9); an offline queue of CRDT updates (F7); version vectors (F8); a snapshot cadence (F8); batching and padding options (§9.3). OpenMLS 0.9.0 can remove, in an external commit, only a leaf with the joiner’s own signature key; asking upstream for an external commit that removes another leaf the application accepts would let a recovery join replace a lost device in one commit.
  2. Engine boundary. OpenMLS 0.9.0 draws randomness from the platform (through getrandom, with its js feature in WebAssembly, as SRV-002 used it) and reads the platform clock (SystemTime::now(), from web_time in WebAssembly): to set and check key-package lifetimes (key_packages/lifetime.rs; checks can take the time from the caller through validate_with_time, but creating a key package still reads the clock) and to timestamp every epoch’s message secrets (group/mls_group/past_secrets.rs). ADR-0012 Decision 5 and the determinism rules forbid both in the engine, and ADR-0012’s amendment of 2026-10-08 excepts only Loro’s randomness and clock, so bayan-mls needs its own amendment of ADR-0012, or randomness and time supplied by the host, before it joins the engine.
  3. Engine protocol. A key-store host service that only ever sees wrapped keys, next to the transport host service of §4.4; no key material in messages to the shell. Recordings: diag.record of a collaborative session must not capture MLS state, keys or the user’s identity (its initial_state holds the engine’s session state), and never uploads; replays of collaborative sessions replay decrypted CRDT updates on the developer’s own machine (T36).
  4. Web. Close the gap in the integrity of the worker’s files (bayan-web issue 8) before collaboration runs in browsers; keys only in the worker, wrapped in IndexedDB with a non-extractable key; no device-signing key in the browser; remove a link’s fragment from the address bar as soon as it is read (F5, F16).
  5. Desktop. Open BayanDocs links in the desktop app; keep tokens and wrapped keys in the OS keychain; hold the device-signing key; use the platform’s passkey interface for passkey unlock where the platform allows it for the server’s domain (F10).
  6. Phase-3 gate. Decide on key transparency, whose IETF protocol (draft-ietf-keytrans-protocol) was still a draft in April 2026 (T23); on HTTPS in the server itself (ADR-0028 Decision 4); on padding; and confirm that no push service is used (F14).
  7. Phase 4. Escrow as in §8; SCIM deprovisioning that blocks a user’s devices at once and asks owners’ clients to remove them from groups; managed configuration that pins an organization’s identities (EA5).
  8. Risk register. The Phase-0 gate review adds T23 (ghost users), T26 (forks), T28 (recovery kit and passkey unlock) and T29 (unrecoverable access) to risks R-06 and R-21 of plan/08.
  9. External audit (SEC-08). Its scope includes ADR-0029, ADR-0030, the snapshot construction (ADR-0016, amendment 2026-10-07), link sharing (F5) and this document.

13.4 Revoked devices when the server’s device list is stale

The AC-4 review of SRV-003 found three gaps that ADR-0029 and ADR-0030 left together, all when the server’s device list is stale, because a hostile server withholds the newer list (T30) or an honest server was restored from a backup older than the revocation (FM9):

  1. Owners’ joins took effect before anyone could refuse them. An owner’s resync or recovery join takes one phase (ADR-0030 Decision 1), so a revoked owner device’s join took effect at once, and the user’s own devices, which know the revocation, could not block it.
  2. The victim’s account was quarantined. For a user who is not an owner, a refusal by the user’s own device dropped the join, but ADR-0030 Decision 5 then quarantined the committer’s account, which is the victim’s own.
  3. Catch-up had no answer for conflicting owners’ chains. §7.5 rule 4 lets the chain that an owner’s device uploads decide, but not between owners’ devices, and a revoked owner device that the restored list still names counted as an owner’s device.

The owner accepted these rules on 2026-10-26, recorded in ADR-0030’s amendment of 2026-10-26:

  • Device lists first. During catch-up, every device uploads the newest signed device lists it holds before any commit, and the server checks every uploaded commit, resync and recovery join against the newest list it then holds, refusing anything signed by a device that a newer list revokes.
  • Block the device, not the victim. A refusal with reason credential by a device of the joining account drops a resync or recovery join and blocks only the joining device in that group: the server accepts nothing more from it there, and the account is not quarantined. Owners and the user’s devices are told.
  • The user’s own devices may stop an owner’s join. When the joining account has other devices in the group, an owner’s resync or recovery join waits like a two-phase commit, but only those devices’ refusals count. It takes effect when the grace period after their first report ends without a refusal, or after a limit, proposed at 10 minutes, if none of them reports; SRV-103 measures the limit and may change it. A recovery after losing every device has no other device in the group and takes effect at once.
  • Conflicting owners’ chains stop the group. Once the lists are applied, the server drops uploaded chains that contain commits from revoked devices. If owners’ devices still upload conflicting chains, the server accepts neither, keeps the group stopped and tells its owners, who repair it with a move to a new group (F13), as for any fork (FM6).

What remains: a hostile server can still withhold a newer list from members who have not seen it (T30) and ignore the refusals it receives, so against a hostile server only members who know the newer list protect themselves, by refusing the join and falling behind. Against an honest server that was restored, the rules close the gap once one device that knows the revocation has reconnected; until then, and when the user’s other devices stay offline beyond the limit, a revoked owner device can still rejoin.

14. Traceability

14.1 The open items of v0

v0 §6 open itemWhere v1 addresses it
1. Full data-flow diagrams for login, device enrollment, sharing, link sharing, sync, snapshotting and recoveryContext and component diagrams §4.2–§4.4; flows F1 (OIDC), F2 (passkeys), F3 (device enrollment and cross-signing), F4 (sharing and roles), F5 (links), F6 and F7 (sync online and offline), F8 (snapshots and new members), F9 and F10 (recovery), F11 (device loss), plus F12–F17
2. Exact metadata stored by the server, field by field, with justification§9
3. Role enforcement design details and failure modes (server and client disagree)§7, F12, ADR-0030
4. Recovery kit and passkey PRF flows; lost-device and revocation scenarios§5.1, F9, F10, F11, ADR-0029
5. Compliance escrow member design and its abuse cases§8, F15
6. Security requirements checklist per server endpoint§10

14.2 Requirements of SRV-003

RequirementWhere
SEC-12: threat model maintained per componentThis document; §15
COL-04: zero-knowledge E2EE of content, titles, comments, media, history and folder structureG1, §6, §7.4, §9, T5, T9
COL-05: roles, link sharing, revocationF4, F5, §7
COL-06: multiple devices, cross-signing, account recovery§5, F3, F9, F10, F11, ADR-0029
COL-10: notifications without contentF14

15. Maintenance and review

  • When it changes. At every phase gate, and in any work package that changes a trust boundary or adds an endpoint. Phase-1 server work packages record, for each endpoint they build, the evidence for §10’s requirements, and update §9 when they store a new field.
  • Numbers. Threat numbers (T1–T39), flow numbers (F1–F17), infrastructure rows (IE1–IE11), failure-mode numbers (FM1–FM13) and escrow abuse cases (EA1–EA10) are never reused or renumbered; new ones are added at the end.
  • Review. A separate reviewer session reviews each version (SRV-003 AC-4); the external audit before collaboration leaves beta reviews §5–§8 (SEC-08).
  • History. v0, 2026-10-03, planning session. v1, 2026-10-14, SRV-003: system and flow diagrams, STRIDE per element and per flow, metadata inventory, role enforcement, escrow, endpoint requirements, T23–T39, ADR-0029 and ADR-0030 proposed, and accepted by the owner on 2026-10-17. 2026-10-26: the owner confirmed the AC-4 review’s corrections and accepted the rules of §13.4 (ADR-0030, amendment 2026-10-26).