atagen

macltok

public
Star0
Watch0
9eabb6b7587ddbf872d0ee2dfb752650c3f823f9
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: docs/mentions-discovery.md

cbb715e11963atagen2026-10-24 23:24 UTC1
# Persistent mentions discovery
cbb715e11963atagen2026-10-24 23:24 UTC2
cbb715e11963atagen2026-10-24 23:24 UTC3
This records the SDK audit and current persistent mentions implementation.
cbb715e11963atagen2026-10-24 23:24 UTC4
Persistent sessions now commit discovery with Sliding Sync responses, retain an
cbb715e11963atagen2026-10-24 23:24 UTC5
encrypted inbox, and expose it through `ClientHandle` and the mirror.
cbb715e11963atagen2026-10-24 23:24 UTC6
Historical coverage remains incomplete because room-list sync and notification
cbb715e11963atagen2026-10-24 23:24 UTC7
history cannot prove that every past mention was observed.
cbb715e11963atagen2026-10-24 23:24 UTC8
cbb715e11963atagen2026-10-24 23:24 UTC9
The audited dependency is Matrix Rust SDK 0.18.0 at fork revision
cbb715e11963atagen2026-10-24 23:24 UTC10
`cc2b13ea185c0f694523a3a48d8aadfe366dd767`. macltok now pins fork revision
cbb715e11963atagen2026-10-24 23:24 UTC11
`7aace883fbe03d4c3c118b55188fe9519ba8ce48`, which adds the response commit
cbb715e11963atagen2026-10-24 23:24 UTC12
hook described below. SDK paths below are relative to the audited revision.
cbb715e11963atagen2026-10-24 23:24 UTC13
cbb715e11963atagen2026-10-24 23:24 UTC14
## Findings
cbb715e11963atagen2026-10-24 23:24 UTC15
cbb715e11963atagen2026-10-24 23:24 UTC16
The SDK can deliver and decrypt events without opening a UI timeline. macltok
cbb715e11963atagen2026-10-24 23:24 UTC17
records observed events and recovers candidates through notification history,
cbb715e11963atagen2026-10-24 23:24 UTC18
but neither source establishes complete historical coverage.
cbb715e11963atagen2026-10-24 23:24 UTC19
cbb715e11963atagen2026-10-24 23:24 UTC20
The SDK audit found that an awaited event handler can hold back the Sliding
cbb715e11963atagen2026-10-24 23:24 UTC21
Sync position until a macltok transaction commits, while a background
cbb715e11963atagen2026-10-24 23:24 UTC22
broadcast consumer cannot provide that guarantee. Handlers cannot propagate
cbb715e11963atagen2026-10-24 23:24 UTC23
commit errors, and response processing outlives cancellation of the calling
cbb715e11963atagen2026-10-24 23:24 UTC24
sync task. These limits motivated the fallible response-commit hook now
cbb715e11963atagen2026-10-24 23:24 UTC25
implemented in the pinned SDK fork.
cbb715e11963atagen2026-10-24 23:24 UTC26
cbb715e11963atagen2026-10-24 23:24 UTC27
## Sync coverage
cbb715e11963atagen2026-10-24 23:24 UTC28
cbb715e11963atagen2026-10-24 23:24 UTC29
macltok constructs `SyncService` in `src/engine/mod.rs` and uses the SDK room
cbb715e11963atagen2026-10-24 23:24 UTC30
list configuration. The list starts with the selective range `0..=19`. After
cbb715e11963atagen2026-10-24 23:24 UTC31
setup it grows by 100 rooms per response, without an SDK-side maximum, until
cbb715e11963atagen2026-10-24 23:24 UTC32
it reaches the server's room count. Recovery and sufficiently long idle
cbb715e11963atagen2026-10-24 23:24 UTC33
periods can return it to the initial selective range before it grows again.
cbb715e11963atagen2026-10-24 23:24 UTC34
The default list timeline limit is one event per room on initial delivery.
cbb715e11963atagen2026-10-24 23:24 UTC35
Subsequent changes for covered rooms continue arriving through sync.
cbb715e11963atagen2026-10-24 23:24 UTC36
cbb715e11963atagen2026-10-24 23:24 UTC37
Opening a room separately subscribes it with a
cbb715e11963atagen2026-10-24 23:24 UTC38
timeline limit of 20. macltok's `update_room_subscriptions` and
cbb715e11963atagen2026-10-24 23:24 UTC39
`watchers::room_subscriptions` manage those explicit subscriptions for active
cbb715e11963atagen2026-10-24 23:24 UTC40
room actors only. The mentions response hook filters entries to joined rooms.
cbb715e11963atagen2026-10-24 23:24 UTC41
The built-in list still includes invited rooms.
cbb715e11963atagen2026-10-24 23:24 UTC42
cbb715e11963atagen2026-10-24 23:24 UTC43
Sources are `crates/matrix-sdk-ui/src/room_list_service/mod.rs`,
cbb715e11963atagen2026-10-24 23:24 UTC44
`crates/matrix-sdk-ui/src/room_list_service/state.rs`, and
cbb715e11963atagen2026-10-24 23:24 UTC45
`crates/matrix-sdk/src/sliding_sync/list/request_generator.rs`.
cbb715e11963atagen2026-10-24 23:24 UTC46
cbb715e11963atagen2026-10-24 23:24 UTC47
The default one-event initial window cannot recover all earlier pings, including
cbb715e11963atagen2026-10-24 23:24 UTC48
messages sent while the client was offline. Raising
cbb715e11963atagen2026-10-24 23:24 UTC49
`SyncServiceBuilder::with_room_list_timeline_limit` increases the initial
cbb715e11963atagen2026-10-24 23:24 UTC50
window but does not establish continuous history. The response hook processes
cbb715e11963atagen2026-10-24 23:24 UTC51
joined-room events and records limited timelines, initial room coverage,
cbb715e11963atagen2026-10-24 23:24 UTC52
checkpoint discontinuities, and recovery gaps.
cbb715e11963atagen2026-10-24 23:24 UTC53
cbb715e11963atagen2026-10-24 23:24 UTC54
## Observation and durable commit
cbb715e11963atagen2026-10-24 23:24 UTC55
cbb715e11963atagen2026-10-24 23:24 UTC56
`Client::subscribe_to_all_room_updates` returns a bounded broadcast receiver
cbb715e11963atagen2026-10-24 23:24 UTC57
of `RoomUpdates`. Its `joined` map includes processed timeline events with raw
cbb715e11963atagen2026-10-24 23:24 UTC58
decrypted content when available and optional evaluated push actions. It does
cbb715e11963atagen2026-10-24 23:24 UTC59
not require a UI timeline. Lag is an error that requires recovery, not an
cbb715e11963atagen2026-10-24 23:24 UTC60
invitation to skip to the next update.
cbb715e11963atagen2026-10-24 23:24 UTC61
cbb715e11963atagen2026-10-24 23:24 UTC62
`Client::observe_events` retains only the latest observation and can miss
cbb715e11963atagen2026-10-24 23:24 UTC63
intermediate events. It is unsuitable for durable discovery.
cbb715e11963atagen2026-10-24 23:24 UTC64
cbb715e11963atagen2026-10-24 23:24 UTC65
An earlier probe registers `Client::add_event_handler` for
cbb715e11963atagen2026-10-24 23:24 UTC66
`Raw<AnySyncTimelineEvent>` with `Room` context before sync starts. Its awaited
cbb715e11963atagen2026-10-24 23:24 UTC67
handler demonstrates event ordering, but it cannot report commit failures
cbb715e11963atagen2026-10-24 23:24 UTC68
reliably and is not the production ingestion path. Production uses the
cbb715e11963atagen2026-10-24 23:24 UTC69
fallible Sliding Sync response hook described below.
cbb715e11963atagen2026-10-24 23:24 UTC70
cbb715e11963atagen2026-10-24 23:24 UTC71
For Sliding Sync v5, the response processor awaits event handlers before
cbb715e11963atagen2026-10-24 23:24 UTC72
updating and persisting `pos`. The response hook commits the inbox first, so
cbb715e11963atagen2026-10-24 23:24 UTC73
a crash permits replay without skipping committed candidates. Stable event
cbb715e11963atagen2026-10-24 23:24 UTC74
keys make replay idempotent. The hook acknowledgement does not wait for
cbb715e11963atagen2026-10-24 23:24 UTC75
context capture, a UI subscriber, or the engine event stream to drain.
cbb715e11963atagen2026-10-24 23:24 UTC76
cbb715e11963atagen2026-10-24 23:24 UTC77
For ordinary event handlers, the SDK logs and discards handler errors, so
cbb715e11963atagen2026-10-24 23:24 UTC78
returning an error after a failed write still permits `pos` to advance. A
cbb715e11963atagen2026-10-24 23:24 UTC79
pending handler alone also does not provide a reliable shutdown path. The SDK
cbb715e11963atagen2026-10-24 23:24 UTC80
runs response handling in a detached task that outlives cancellation of
cbb715e11963atagen2026-10-24 23:24 UTC81
`sync_once` or its caller. Aborting the engine's sync task cannot prevent a
cbb715e11963atagen2026-10-24 23:24 UTC82
suspended handler from later returning and advancing the position. Registering
cbb715e11963atagen2026-10-24 23:24 UTC83
a handler and placing work on a queue without awaiting its commit does not
cbb715e11963atagen2026-10-24 23:24 UTC84
close the crash window.
cbb715e11963atagen2026-10-24 23:24 UTC85
cbb715e11963atagen2026-10-24 23:24 UTC86
macltok uses shared `pos` through its SDK service and commits each response
cbb715e11963atagen2026-10-24 23:24 UTC87
batch through one serialized mentions actor before the hook acknowledges it.
cbb715e11963atagen2026-10-24 23:24 UTC88
The actor records joined-room events, raw encrypted candidates, limited
cbb715e11963atagen2026-10-24 23:24 UTC89
timeline gaps, and the connection checkpoint in the same inbox revision.
cbb715e11963atagen2026-10-24 23:24 UTC90
Stable room and event keys make replay idempotent. Context capture, mirror
cbb715e11963atagen2026-10-24 23:24 UTC91
publication, and event-stream delivery continue after the commit. The classic
cbb715e11963atagen2026-10-24 23:24 UTC92
sync `next_batch` ordering differs and does not establish this guarantee.
cbb715e11963atagen2026-10-24 23:24 UTC93
Nor does this ordering make arbitrary concurrent sync loops safe.
cbb715e11963atagen2026-10-24 23:24 UTC94
cbb715e11963atagen2026-10-24 23:24 UTC95
Sources are `crates/matrix-sdk/src/event_handler/mod.rs`,
cbb715e11963atagen2026-10-24 23:24 UTC96
`crates/matrix-sdk/src/event_handler/static_events.rs`,
cbb715e11963atagen2026-10-24 23:24 UTC97
`crates/matrix-sdk/src/sliding_sync/client.rs`,
cbb715e11963atagen2026-10-24 23:24 UTC98
`crates/matrix-sdk/src/sliding_sync/mod.rs`, and
cbb715e11963atagen2026-10-24 23:24 UTC99
`crates/matrix-sdk/src/sliding_sync/cache.rs`.
cbb715e11963atagen2026-10-24 23:24 UTC100
cbb715e11963atagen2026-10-24 23:24 UTC101
### SDK commit hook implementation
cbb715e11963atagen2026-10-24 23:24 UTC102
cbb715e11963atagen2026-10-24 23:24 UTC103
The local SDK workspace at `../matrix-rust-sdk-mentions` implements the
cbb715e11963atagen2026-10-24 23:24 UTC104
fallible asynchronous response-commit hook through `ResponseCommitHook` and
cbb715e11963atagen2026-10-24 23:24 UTC105
`ResponseCommitBatch`. `SyncServiceBuilder` forwards it only to its room-list
cbb715e11963atagen2026-10-24 23:24 UTC106
connection, so independent notification lookups are unaffected.
cbb715e11963atagen2026-10-24 23:24 UTC107
cbb715e11963atagen2026-10-24 23:24 UTC108
The hook receives the processed room events, original room payloads, limited
cbb715e11963atagen2026-10-24 23:24 UTC109
flags, pagination tokens, connection identity, and previous and next position
cbb715e11963atagen2026-10-24 23:24 UTC110
identifiers. It runs before response handlers and observers and before `pos`
cbb715e11963atagen2026-10-24 23:24 UTC111
is updated or saved. SDK room state may already have been saved. A successful
cbb715e11963atagen2026-10-24 23:24 UTC112
acknowledgement follows the macltok inbox transaction. A failed or dropped
cbb715e11963atagen2026-10-24 23:24 UTC113
acknowledgement returns a sync error without advancing the position.
cbb715e11963atagen2026-10-24 23:24 UTC114
cbb715e11963atagen2026-10-24 23:24 UTC115
The hook runs outside SDK store locks and supports local futures on wasm and
cbb715e11963atagen2026-10-24 23:24 UTC116
sendable futures natively. The default absent hook preserves existing SDK
cbb715e11963atagen2026-10-24 23:24 UTC117
behavior. Detached response processing observes a dropped store
cbb715e11963atagen2026-10-24 23:24 UTC118
acknowledgement after its caller is cancelled. Checkpoint replay works after
cbb715e11963atagen2026-10-24 23:24 UTC119
SDK room state was saved, and stable event keys deduplicate repeated commits.
cbb715e11963atagen2026-10-24 23:24 UTC120
cbb715e11963atagen2026-10-24 23:24 UTC121
The SDK audit and focused validation covered commit ordering, failed and
cbb715e11963atagen2026-10-24 23:24 UTC122
dropped acknowledgements, caller cancellation, replay after room-state
cbb715e11963atagen2026-10-24 23:24 UTC123
persistence, restoring a previous checkpoint, absent-hook behavior, builder
cbb715e11963atagen2026-10-24 23:24 UTC124
forwarding, and connection isolation. The SDK UI library also received a
cbb715e11963atagen2026-10-24 23:24 UTC125
focused wasm compilation check with its `js` feature. The SDK changes are
cbb715e11963atagen2026-10-24 23:24 UTC126
published to the user's fork under `mentions-commit-hook`, and the manifest
cbb715e11963atagen2026-10-24 23:24 UTC127
and both macltok lockfiles pin revision
cbb715e11963atagen2026-10-24 23:24 UTC128
`7aace883fbe03d4c3c118b55188fe9519ba8ce48`.
cbb715e11963atagen2026-10-24 23:24 UTC129
cbb715e11963atagen2026-10-24 23:24 UTC130
## Event cache and delayed decryption
cbb715e11963atagen2026-10-24 23:24 UTC131
cbb715e11963atagen2026-10-24 23:24 UTC132
`EventCache::subscribe` starts global room-update ingestion. The SDK room list
cbb715e11963atagen2026-10-24 23:24 UTC133
service already calls it during construction. Its generic update subscription
cbb715e11963atagen2026-10-24 23:24 UTC134
identifies the affected room, then `Room::event_cache` and
cbb715e11963atagen2026-10-24 23:24 UTC135
`RoomEventCache::find_event` can retrieve an event. This does not require a
cbb715e11963atagen2026-10-24 23:24 UTC136
`matrix-sdk-ui::Timeline` or a retained subscriber per room.
cbb715e11963atagen2026-10-24 23:24 UTC137
cbb715e11963atagen2026-10-24 23:24 UTC138
`EventCache::subscribe_to_decryption_reports` reports resolved event IDs,
cbb715e11963atagen2026-10-24 23:24 UTC139
lag, and backup availability. `EventCache::request_decryption` accepts room
cbb715e11963atagen2026-10-24 23:24 UTC140
and Megolm session IDs for explicit retries. `Room::decrypt_event` is also
cbb715e11963atagen2026-10-24 23:24 UTC141
public and can decrypt a retained raw event independently of a UI timeline.
cbb715e11963atagen2026-10-24 23:24 UTC142
cbb715e11963atagen2026-10-24 23:24 UTC143
The late-key probe confirms automatic decryption and retrieval of a structured
cbb715e11963atagen2026-10-24 23:24 UTC144
mention through the global cache without a UI timeline. Cached push actions
cbb715e11963atagen2026-10-24 23:24 UTC145
are optional. Re-evaluate them with `Room::event_push_actions` when needed,
cbb715e11963atagen2026-10-24 23:24 UTC146
and treat missing push context as unresolved rather than a negative result.
cbb715e11963atagen2026-10-24 23:24 UTC147
The room's own membership state is needed to build that push context.
cbb715e11963atagen2026-10-24 23:24 UTC148
cbb715e11963atagen2026-10-24 23:24 UTC149
The cache is not a durable ingestion journal. Its room broadcasts and generic
cbb715e11963atagen2026-10-24 23:24 UTC150
updates can lag, and cache retention can remove events. SQLite and IndexedDB
cbb715e11963atagen2026-10-24 23:24 UTC151
builders select persistent SDK event-cache stores, while the default
cbb715e11963atagen2026-10-24 23:24 UTC152
store-free client uses memory. Even with persistence, the asynchronous cache
cbb715e11963atagen2026-10-24 23:24 UTC153
consumer is not awaited before Sliding Sync saves its position.
cbb715e11963atagen2026-10-24 23:24 UTC154
cbb715e11963atagen2026-10-24 23:24 UTC155
The inbox retains unresolved encrypted candidates as encrypted raw events
cbb715e11963atagen2026-10-24 23:24 UTC156
within a configurable bound. It retries due candidates at startup and resets
cbb715e11963atagen2026-10-24 23:24 UTC157
their retry time when decryption reports arrive. Candidate eviction sets the
cbb715e11963atagen2026-10-24 23:24 UTC158
coverage truncation flag. Imported keys and backups cannot reconstruct events
cbb715e11963atagen2026-10-24 23:24 UTC159
already discarded by every cache.
cbb715e11963atagen2026-10-24 23:24 UTC160
cbb715e11963atagen2026-10-24 23:24 UTC161
Sources are `crates/matrix-sdk/src/event_cache/mod.rs`,
cbb715e11963atagen2026-10-24 23:24 UTC162
`crates/matrix-sdk/src/event_cache/redecryptor.rs`,
cbb715e11963atagen2026-10-24 23:24 UTC163
`crates/matrix-sdk/src/event_cache/caches/room/mod.rs`, and
cbb715e11963atagen2026-10-24 23:24 UTC164
`crates/matrix-sdk/src/room/mod.rs`.
cbb715e11963atagen2026-10-24 23:24 UTC165
cbb715e11963atagen2026-10-24 23:24 UTC166
## Mention policy
cbb715e11963atagen2026-10-24 23:24 UTC167
cbb715e11963atagen2026-10-24 23:24 UTC168
Structured `m.mentions.user_ids` and `m.mentions.room` supply semantic
cbb715e11963atagen2026-10-24 23:24 UTC169
candidates. The classifier does not infer pings from rendered bodies or
cbb715e11963atagen2026-10-24 23:24 UTC170
unread counts. A reply relationship alone does not prove that a reply notifies
cbb715e11963atagen2026-10-24 23:24 UTC171
the user, so reply reasons require notification metadata and confirmation that
cbb715e11963atagen2026-10-24 23:24 UTC172
the referenced event belongs to the signed-in user.
cbb715e11963atagen2026-10-24 23:24 UTC173
cbb715e11963atagen2026-10-24 23:24 UTC174
`Room::event_push_actions` evaluates push rules but does not independently
cbb715e11963atagen2026-10-24 23:24 UTC175
filter ignored senders. The classifier calls `Client::is_user_ignored` and
cbb715e11963atagen2026-10-24 23:24 UTC176
rechecks entries when the ignore list changes.
cbb715e11963atagen2026-10-24 23:24 UTC177
cbb715e11963atagen2026-10-24 23:24 UTC178
A room mute can suppress evaluated notification actions while structured
cbb715e11963atagen2026-10-24 23:24 UTC179
mention metadata remains present. The classifier retains explicit user
cbb715e11963atagen2026-10-24 23:24 UTC180
mentions in muted rooms and marks notification delivery as suppressed. Reply
cbb715e11963atagen2026-10-24 23:24 UTC181
reasons require both a reply to the user and a notifying highlight. Room-wide
cbb715e11963atagen2026-10-24 23:24 UTC182
reasons require the relevant push conditions and sender power level. Missing
cbb715e11963atagen2026-10-24 23:24 UTC183
authorization context remains unresolved for reevaluation. Keyword-only
cbb715e11963atagen2026-10-24 23:24 UTC184
notifications are outside the inbox. The classifier excludes the signed-in
cbb715e11963atagen2026-10-24 23:24 UTC185
user's own events even when they contain explicit mention metadata.
cbb715e11963atagen2026-10-24 23:24 UTC186
cbb715e11963atagen2026-10-24 23:24 UTC187
The existing `NotificationClient` combines notification evaluation with
cbb715e11963atagen2026-10-24 23:24 UTC188
ignored-sender filtering. Its public lookup takes known room and event IDs.
cbb715e11963atagen2026-10-24 23:24 UTC189
It is useful for resolving a candidate, but filtered results erase the
cbb715e11963atagen2026-10-24 23:24 UTC190
distinction between mute policy and an absent semantic mention. It is not the
cbb715e11963atagen2026-10-24 23:24 UTC191
sole classifier for this inbox.
cbb715e11963atagen2026-10-24 23:24 UTC192
cbb715e11963atagen2026-10-24 23:24 UTC193
## Historical candidates and gaps
cbb715e11963atagen2026-10-24 23:24 UTC194
cbb715e11963atagen2026-10-24 23:24 UTC195
Ruma exposes
cbb715e11963atagen2026-10-24 23:24 UTC196
`api::client::push::get_notifications::v3::Request`, sent with `Client::send`.
cbb715e11963atagen2026-10-24 23:24 UTC197
It accepts opaque `from`, optional `limit`, and optional `only` fields.
cbb715e11963atagen2026-10-24 23:24 UTC198
Responses contain notifications and an optional `next_token`. macltok requests
cbb715e11963atagen2026-10-24 23:24 UTC199
50 notifications per page. Entries include raw events, room IDs, timestamps,
cbb715e11963atagen2026-10-24 23:24 UTC200
actions, and server notification read state, which does not initialize or
cbb715e11963atagen2026-10-24 23:24 UTC201
replace local inbox read state.
cbb715e11963atagen2026-10-24 23:24 UTC202
cbb715e11963atagen2026-10-24 23:24 UTC203
The service uses this endpoint as a bounded candidate source, validates
cbb715e11963atagen2026-10-24 23:24 UTC204
events after decryption, and applies the live mention policy. Each page's
cbb715e11963atagen2026-10-24 23:24 UTC205
observed candidates and cursor are committed together in the encrypted inbox,
cbb715e11963atagen2026-10-24 23:24 UTC206
including unresolved encrypted candidates. A failed commit leaves the prior
cbb715e11963atagen2026-10-24 23:24 UTC207
cursor available for replay.
cbb715e11963atagen2026-10-24 23:24 UTC208
cbb715e11963atagen2026-10-24 23:24 UTC209
An exhausted notification list does not mean complete room history. Muted-room
cbb715e11963atagen2026-10-24 23:24 UTC210
pings, encrypted mentions not recognized by the server, expired notifications,
cbb715e11963atagen2026-10-24 23:24 UTC211
and server-specific retention can be absent. Recovery inspects at most 20 pages
cbb715e11963atagen2026-10-24 23:24 UTC212
of 50 notifications per run and resumes from committed cursors later. The
cbb715e11963atagen2026-10-24 23:24 UTC213
older-history cursor is separate from the cursor for scans from the newest
cbb715e11963atagen2026-10-24 23:24 UTC214
notifications. A newest scan records an anchor and runs at startup and every
cbb715e11963atagen2026-10-24 23:24 UTC215
60 seconds. It follows the committed cursor until it reaches that anchor,
cbb715e11963atagen2026-10-24 23:24 UTC216
resuming on later runs when needed to find newly added notifications.
cbb715e11963atagen2026-10-24 23:24 UTC217
cbb715e11963atagen2026-10-24 23:24 UTC218
SDK cache gaps retain pagination tokens and support room pagination, but
cbb715e11963atagen2026-10-24 23:24 UTC219
running that pagination mutates the live cache. Mention context capture uses
cbb715e11963atagen2026-10-24 23:24 UTC220
independent `/context` and `/messages` requests. Tokens may expire. Context can
cbb715e11963atagen2026-10-24 23:24 UTC221
be retried around a known event while the room remains joined, and known gaps
cbb715e11963atagen2026-10-24 23:24 UTC222
remain visible when continuity cannot be established.
cbb715e11963atagen2026-10-24 23:24 UTC223
cbb715e11963atagen2026-10-24 23:24 UTC224
Coverage exposes the earliest observed timestamp, history-endpoint support,
cbb715e11963atagen2026-10-24 23:24 UTC225
recovery activity, known room gaps, unresolved candidate count, and retention
cbb715e11963atagen2026-10-24 23:24 UTC226
truncation. The earliest timestamp is a summary, not proof that every room is
cbb715e11963atagen2026-10-24 23:24 UTC227
covered after that time. The inbox keeps its incomplete-history flag even
cbb715e11963atagen2026-10-24 23:24 UTC228
after notification pagination ends.
cbb715e11963atagen2026-10-24 23:24 UTC229
cbb715e11963atagen2026-10-24 23:24 UTC230
Complete offline mention recovery across arbitrary muted and encrypted rooms
cbb715e11963atagen2026-10-24 23:24 UTC231
is not supplied by this endpoint or by the current sync configuration. It
cbb715e11963atagen2026-10-24 23:24 UTC232
would require a retained, replayable event source or bounded per-room history
cbb715e11963atagen2026-10-24 23:24 UTC233
recovery with proof of continuity. The notification endpoint does not provide
cbb715e11963atagen2026-10-24 23:24 UTC234
that guarantee.
cbb715e11963atagen2026-10-24 23:24 UTC235
cbb715e11963atagen2026-10-24 23:24 UTC236
## Storage, context, and API
cbb715e11963atagen2026-10-24 23:24 UTC237
cbb715e11963atagen2026-10-24 23:24 UTC238
`src/mentions/store` implements encrypted SQLite and IndexedDB record stores.
cbb715e11963atagen2026-10-24 23:24 UTC239
Persistent session construction opens the account store, and local logout
cbb715e11963atagen2026-10-24 23:24 UTC240
deletes it alongside the SDK stores. The mentions actor owns the store for
cbb715e11963atagen2026-10-24 23:24 UTC241
inbox commits and discovery checkpoints.
cbb715e11963atagen2026-10-24 23:24 UTC242
cbb715e11963atagen2026-10-24 23:24 UTC243
The passphrase wraps a random SDK `StoreCipher`. Logical keys are hashed, and
cbb715e11963atagen2026-10-24 23:24 UTC244
authenticated encrypted records include schema, account identity, and logical
cbb715e11963atagen2026-10-24 23:24 UTC245
key. Restore rejects mismatched accounts, unsupported schemas, corrupt records,
cbb715e11963atagen2026-10-24 23:24 UTC246
and inconsistent record counts or byte totals. Atomic transactions compare the
cbb715e11963atagen2026-10-24 23:24 UTC247
previous encrypted header before publishing a new revision, so stale writers
cbb715e11963atagen2026-10-24 23:24 UTC248
cannot overwrite a newer checkpoint.
cbb715e11963atagen2026-10-24 23:24 UTC249
cbb715e11963atagen2026-10-24 23:24 UTC250
The default budget is 64 MiB of encrypted records, hashed keys, header, and
cbb715e11963atagen2026-10-24 23:24 UTC251
wrapped cipher, excluding database page overhead. The store record cap is
cbb715e11963atagen2026-10-24 23:24 UTC252
35,128. The inbox defaults to 5,000 entries, 25,000 shared context events, and
cbb715e11963atagen2026-10-24 23:24 UTC253
5,000 unresolved candidates within that byte budget. When a size or record
cbb715e11963atagen2026-10-24 23:24 UTC254
limit requires eviction, the store drops bounded data and marks coverage
cbb715e11963atagen2026-10-24 23:24 UTC255
truncated.
cbb715e11963atagen2026-10-24 23:24 UTC256
cbb715e11963atagen2026-10-24 23:24 UTC257
The storage tests cover atomic restore of records and checkpoints, stale
cbb715e11963atagen2026-10-24 23:24 UTC258
writers, capacity failure, encrypted physical records, wrong credentials or
cbb715e11963atagen2026-10-24 23:24 UTC259
account, corruption, unsupported schemas, and injected SQLite write failure.
cbb715e11963atagen2026-10-24 23:24 UTC260
The inbox tests cover batch failure, notification recovery, live context
cbb715e11963atagen2026-10-24 23:24 UTC261
updates, and capture persistence across session resume.
cbb715e11963atagen2026-10-24 23:24 UTC262
cbb715e11963atagen2026-10-24 23:24 UTC263
cbb715e11963atagen2026-10-24 23:24 UTC264
The inbox is serialized into one `inbox` value and encrypted as a single
cbb715e11963atagen2026-10-24 23:24 UTC265
record on every revision. Each update reserializes and re-encrypts the retained
cbb715e11963atagen2026-10-24 23:24 UTC266
inbox, so write cost scales with retained data. Decrypted previews, saved
cbb715e11963atagen2026-10-24 23:24 UTC267
context, raw candidates, pending capture state, media source registrations,
cbb715e11963atagen2026-10-24 23:24 UTC268
read state, coverage, and sync checkpoints stay inside the authenticated
cbb715e11963atagen2026-10-24 23:24 UTC269
encrypted payload. SDK SQLite custom values remain plaintext and are not used
cbb715e11963atagen2026-10-24 23:24 UTC270
for decrypted mentions.
cbb715e11963atagen2026-10-24 23:24 UTC271
cbb715e11963atagen2026-10-24 23:24 UTC272
Context capture in `src/engine/context.rs` sizes the before and after requests
cbb715e11963atagen2026-10-24 23:24 UTC273
independently and retains the media registrations produced by normalization.
cbb715e11963atagen2026-10-24 23:24 UTC274
Each capture requests up to 10 events on each side. At most two capture jobs
cbb715e11963atagen2026-10-24 23:24 UTC275
run concurrently, with up to 128 pending or failed entries considered for
cbb715e11963atagen2026-10-24 23:24 UTC276
scheduling at a time. Capture uses independent `/context` and `/messages`
cbb715e11963atagen2026-10-24 23:24 UTC277
requests, so it does not paginate the live cache. Media source registrations
cbb715e11963atagen2026-10-24 23:24 UTC278
are encrypted with saved events and registered again when context is read.
cbb715e11963atagen2026-10-24 23:24 UTC279
Media bodies and key material do not enter the mentions public model.
cbb715e11963atagen2026-10-24 23:24 UTC280
cbb715e11963atagen2026-10-24 23:24 UTC281
One account-scoped mentions actor serializes store commits for entries,
cbb715e11963atagen2026-10-24 23:24 UTC282
unresolved candidates, shared context events, read state, revisions, discovery
cbb715e11963atagen2026-10-24 23:24 UTC283
checkpoints, and retention decisions.
cbb715e11963atagen2026-10-24 23:24 UTC284
cbb715e11963atagen2026-10-24 23:24 UTC285
Pending capture state is durable with the inbox, so interrupted capture can
cbb715e11963atagen2026-10-24 23:24 UTC286
resume after startup. When a room leaves the joined state, saved context
cbb715e11963atagen2026-10-24 23:24 UTC287
remains readable and its capture state becomes unavailable. Refetch is
cbb715e11963atagen2026-10-24 23:24 UTC288
disabled after departure.
cbb715e11963atagen2026-10-24 23:24 UTC289
cbb715e11963atagen2026-10-24 23:24 UTC290
Store publication follows commit. `ClientHandle` exposes page, context, retry,
cbb715e11963atagen2026-10-24 23:24 UTC291
read, read-through, and recovery methods. The public `Command`, mention models,
cbb715e11963atagen2026-10-24 23:24 UTC292
mirror state, and updates support serde when the `serde` feature is enabled,
cbb715e11963atagen2026-10-24 23:24 UTC293
so native and browser consumers can transport them. Browser worker integration
cbb715e11963atagen2026-10-24 23:24 UTC294
is a consumer phase after SDK and macltok revision publication. Malice's worker
cbb715e11963atagen2026-10-24 23:24 UTC295
still needs mention variants in its explicit `Request` dispatch and TypeScript
cbb715e11963atagen2026-10-24 23:24 UTC296
methods.
cbb715e11963atagen2026-10-24 23:24 UTC297
cbb715e11963atagen2026-10-24 23:24 UTC298
Mirror snapshots contain at most 50 entry previews, coverage, unread count,
cbb715e11963atagen2026-10-24 23:24 UTC299
and a committed revision boundary, with no saved context bodies. The watcher
cbb715e11963atagen2026-10-24 23:24 UTC300
coalesces updates and computes upserts and removals relative to the previous
cbb715e11963atagen2026-10-24 23:24 UTC301
bounded snapshot it observed. A missed update therefore leaves the latest
cbb715e11963atagen2026-10-24 23:24 UTC302
snapshot authoritative. A removal can mean an entry fell out of the top 50,
cbb715e11963atagen2026-10-24 23:24 UTC303
and does not imply that it was deleted from the inbox. Pagination uses
cbb715e11963atagen2026-10-24 23:24 UTC304
timestamp and stable event key within a discovery revision boundary.
cbb715e11963atagen2026-10-24 23:24 UTC305
Read-through boundaries exclude mentions first discovered or reactivated after
cbb715e11963atagen2026-10-24 23:24 UTC306
that boundary, including older messages resolved by late decryption.
cbb715e11963atagen2026-10-24 23:24 UTC307
cbb715e11963atagen2026-10-24 23:24 UTC308
## Executable probes
cbb715e11963atagen2026-10-24 23:24 UTC309
cbb715e11963atagen2026-10-24 23:24 UTC310
`tests/mentions_discovery.rs` exercises unopened-room update and cache access,
cbb715e11963atagen2026-10-24 23:24 UTC311
push-rule behavior for ignored senders and muted rooms, and typed
cbb715e11963atagen2026-10-24 23:24 UTC312
notification-history pagination.
cbb715e11963atagen2026-10-24 23:24 UTC313
cbb715e11963atagen2026-10-24 23:24 UTC314
`tests/mentions_decryption.rs` exchanges a real encrypted event and a delayed
cbb715e11963atagen2026-10-24 23:24 UTC315
room key between two mock clients, then retrieves the decrypted mention
cbb715e11963atagen2026-10-24 23:24 UTC316
without opening a UI timeline.
cbb715e11963atagen2026-10-24 23:24 UTC317
cbb715e11963atagen2026-10-24 23:24 UTC318
`tests/mentions_checkpoint.rs` holds an event handler pending and verifies
cbb715e11963atagen2026-10-24 23:24 UTC319
that the SDK has not persisted its position. It cancels the calling task,
cbb715e11963atagen2026-10-24 23:24 UTC320
then acknowledges the handler and verifies that detached response processing
cbb715e11963atagen2026-10-24 23:24 UTC321
still saves the position. A new Sliding Sync instance restores that position.
cbb715e11963atagen2026-10-24 23:24 UTC322
This is the historical event-handler ordering probe. It does not exercise the
cbb715e11963atagen2026-10-24 23:24 UTC323
production response-commit hook or the durable inbox transaction. It also
cbb715e11963atagen2026-10-24 23:24 UTC324
demonstrates that returning a handler error still advances the position.
cbb715e11963atagen2026-10-24 23:24 UTC325
cbb715e11963atagen2026-10-24 23:24 UTC326
`tests/mentions_inbox.rs` covers live mention classification, read-through
cbb715e11963atagen2026-10-24 23:24 UTC327
with revision boundaries, independent context limits, and persistence across
cbb715e11963atagen2026-10-24 23:24 UTC328
session resume. `tests/mentions_late_decryption.rs` verifies that a delayed room key
cbb715e11963atagen2026-10-24 23:24 UTC329
updates the production inbox once. `src/mentions/service_tests.rs` covers
cbb715e11963atagen2026-10-24 23:24 UTC330
checkpoint rollback, notification-history recovery, and context updates.
cbb715e11963atagen2026-10-24 23:24 UTC331
`src/mentions/store/tests.rs` exercises encrypted record restore, capacity,
cbb715e11963atagen2026-10-24 23:24 UTC332
stale writers, and corruption handling.
cbb715e11963atagen2026-10-24 23:24 UTC333
cbb715e11963atagen2026-10-24 23:24 UTC334
These probes establish SDK behavior against mocked responses. They do not
cbb715e11963atagen2026-10-24 23:24 UTC335
establish homeserver notification retention, room-list completeness, or
cbb715e11963atagen2026-10-24 23:24 UTC336
complete history on a deployed server.
cbb715e11963atagen2026-10-24 23:24 UTC337
cbb715e11963atagen2026-10-24 23:24 UTC338
Focused validation passed 63 native tests and 12 shared inbox and IndexedDB
cbb715e11963atagen2026-10-24 23:24 UTC339
tests in headless Firefox. Compilation checks also pass without storage
cbb715e11963atagen2026-10-24 23:24 UTC340
features and for wasm with IndexedDB and mirror support. The full flake gate
cbb715e11963atagen2026-10-24 23:24 UTC341
was skipped by request.