Commit cff1b0a4eb30
tarball1 file changed+78-0
@@ -463,6 +463,84 @@ most. a diff for a timeline with no reset is a divergence too, so a wrapper
463463
that swallowed the reset still heals. anything a late subscriber needs, failures464464
included, lives in the state rather than only in an update465465
466
+## search467
+468
+`search_messages` takes words and a scope of one room, a list of them or every469
+joined room, and answers newest first. it opens a search the way a jump opens an470
+event context, a session owning the media of every hit it hands out under its471
+own bounded scope, paged with `search_more` and its cursor and freed with472
+`close_search`. at most eight stay open and opening another closes the oldest,473
+so a host that never closes anything still holds a bounded amount. a hit's474
+media ids stop resolving once its search closes. encrypted rooms can't use475
+the server's `/search`, so everything goes through one local index, unencrypted476
+rooms too, so results and coverage mean the same thing everywhere. a hit comes477
+back the way event context shows its target, latest edit applied, with byte478
+ranges of the matched words in its text479
+480
+the index is tantivy, but not the fork's matrix-sdk-search. that crate keeps its481
+schema private, keeps an index per room, opens a fresh `IndexWriter` for every482
+event, ranks by score, hands back bare event ids and turns mmap on, which wasm483
+can't build. `IndexWriter` can't run in wasm either, since it spawns indexing484
+threads and rayon pools. so macltok keeps its own segments. each flush writes an485
+immutable segment with `SingleSegmentIndexWriter` into a `RamDirectory`, edits486
+and redactions are tombstones in a bitset beside it, and segments of a size487
+class merge through `merge_filtered_segments` once six pile up, dropping the488
+tombstoned docs as they go. all of that is single threaded with the docstore's489
+dedicated compressor off. body text is indexed, not stored, a preview reads the490
+event back from the event cache or the server. a 2026-10-25 spike ran the491
+whole cycle in a headless firefox worker492
+493
+it lives with the store, per login and never synced. natively it's494
+`macltok-search.sqlite3` beside the sdk's databases, in a browser the495
+`::macltok-search` IndexedDB database, and both go when the store is deleted.496
+every segment, every tombstone generation and the manifest is a blob sealed by497
+a `StoreCipher` under the store passphrase, its own key sealed inside so blobs498
+can't be swapped. one commit is one transaction, so a crash keeps the last499
+commit whole. the working copy lives in memory, all of it, which is the price500
+of tantivy's directory being synchronous and IndexedDB not. a store that won't501
+open or read means an index in memory for this engine, and coverage says502
+`persistent: false`503
+504
+it's fed from what the client sees. each joined room's event cache is followed505
+from its first update, so sync, pagination and the redecryptor's replacements506
+all arrive as diffs, and a room idle for five minutes is let go so the cache can507
+shrink. an edit lands on its original under the original's key, the latest by508
+the original's sender winning, and one that arrives first holds the key509
+provisionally until the original turns up. a redaction drops the key and is510
+remembered for a while, so a stale copy can't bring it back. redacting the511
+latest edit reads the original again. a message still encrypted waits, raw, up512
+to two thousand of them, and is decrypted when a room key for its session513
+arrives514
+515
+backfill is `/messages` straight from the room, not through the event cache,516
+so it never grows a timeline someone has open. one request at a time across517
+every room, two seconds apart, a hundred events a page and ten pages a room per518
+engine run, round robin. a room walks back from the newest event to the start,519
+and resumes from its saved token next time. the first time a room shows up in520
+a run it also gets a head pass from the newest event back until it meets521
+something indexed, which picks up what happened while we were offline. failures522
+back off from a minute to fifteen. all of that is `EngineConfig`, and zero pages523
+a room turns backfill off524
+525
+coverage is per room, the oldest and newest indexed message, whether backfill526
+reached the start, and where backfill is at. it comes with every page527
+528
+the analyser splits on anything that isn't alphanumeric, lowercases and folds529
+accents, so `cafe` finds `Café`. it doesn't stem or segment cjk, a run of cjk is530
+one token. words all have to match, quotes make a phrase and `-` excludes.531
+tantivy's merger times itself with `Instant::now()` when a `log` logger is on at532
+debug, which panics in wasm. so in wasm macltok caps `log` at info at compile533
+time, `max_level_info` and `release_max_level_info` both, since in release the534
+release cap wins. a host asking for any other cap gets log's own "multiple535
+max_level_* features set" error rather than a quiet panic later536
+537
+a hit is read back when it's shown, `load_or_fetch_event` for the original and538
+for its latest edit, which is the event cache and its store first and the539
+server's `/event` after. one that's turned out redacted is dropped from the page540
+and from the index. one that can't be read at all, gone from the cache with the541
+server unreachable or refusing, is left out of that page but stays indexed, so542
+it shows up again once it can be read. the cursor still moves past it543
+466544
## the sdk fork467545
468546
we build against a fork of matrix-rust-sdk, branch `macltok`, cut from the