Commit e521a7177da3
tarball1 file changed+73-43
@@ -470,9 +470,11 @@ joined room, and answers newest first. it opens a search the way a jump opens an
470470
event context, a session owning the media of every hit it hands out under its471471
own bounded scope, paged with `search_more` and its cursor and freed with472472
`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, unencrypted473
+so a host that never closes anything still holds a bounded amount, and the474
+closed one goes out as `SearchClosed`. a hit's media ids carry their search's475
+owner prefix like a context's, and stop resolving once it closes. encrypted476
+rooms can't use the server's `/search`, so everything goes through one local477
+index, unencrypted476478
rooms too, so results and coverage mean the same thing everywhere. a hit comes477479
back the way event context shows its target, latest edit applied, with byte478480
ranges of the matched words in its text@@ -486,60 +488,88 @@ immutable segment with `SingleSegmentIndexWriter` into a `RamDirectory`, edits
486488
and redactions are tombstones in a bitset beside it, and segments of a size487489
class merge through `merge_filtered_segments` once six pile up, dropping the488490
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
+dedicated compressor off. message text is indexed and not stored, a preview492
+reads the event back. the one exception is an edit waiting for its original,493
+whose text is kept so the original can take it over. a 2026-10-25 spike ran the491494
whole cycle in a headless firefox worker492495
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`496
+it lives with the store, so it's per login and never synced, and it's only497
+opened for the user and homeserver that made it. natively it's498
+`macltok-search.sqlite3` beside the sdk's databases, created 0600, and in a499
+browser the `::macltok-search` IndexedDB database, and both go when the store is500
+deleted. every segment, every tombstone generation, the manifest and the waiting501
+undecryptable events are blobs sealed by a `StoreCipher` under the store502
+passphrase, stored under a keyed hash of their name like the mentions rows, the503
+name sealed inside so blobs can't be swapped. the manifest a commit saves is the504
+index as it stands once that commit lands, and blobs it doesn't point at are505
+dropped on the next load. one commit is one transaction, sqlite in wal with506
+normal sync and IndexedDB relaxed, so a crash can lose the last few seconds of507
+commits but never leaves half of one. the working copy lives in memory, all of508
+it, which is the price of tantivy's directory being synchronous and IndexedDB509
+not. a store that won't open or read, or five commits failing in a row, means510
+an index in memory for the rest of the engine, and coverage says511
+`persistent: false` with the reason in `in_memory`503512
504513
it's fed from what the client sees. each joined room's event cache is followed505514
from its first update, so sync, pagination and the redecryptor's replacements506515
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
-arrives516
+shrink. the generic update channel is followed from before the index loads, and517
+if it lags every joined room is followed, since a lost update could be a room's518
+only one. an edit lands on its original under the original's key, the latest by519
+the original's sender winning. one that arrives first waits under its target and520
+its own sender, out of results, until the original turns up and takes the521
+author's edit, so a stranger's early edit can't crowd it out. an edit of an edit522
+is ignored, and an edit to content we can't read clears the old text. a523
+redaction drops the key and is remembered for a while, so a stale copy can't524
+bring it back. redacting the latest edit reads the original again with whatever525
+edits of it are still around. a message still encrypted waits, raw, up to two526
+thousand of them in sixteen blobs, and is decrypted eight at a time when a room527
+key for its session arrives514528
515529
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 per530
+so it never grows a timeline someone has open, filtered to messages, encrypted531
+events, redactions, stickers and polls. one request at a time across every532
+room, two seconds apart, a hundred events a page and ten pages a room per518533
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 off534
+and resumes from its saved token next time. a room that's been indexed before535
+also gets a head pass the first time it shows up in a run, from the newest event536
+back until it meets something that was already indexed when the engine started,537
+which picks up what happened while we were offline. a failing room backs off on538
+its own from a minute to fifteen. a saved token the server turns down with a 400539
+is dropped and the room starts again from the newest. leaving a room cancels its540
+page. all of that is `EngineConfig`, and zero pages a room turns backfill off524541
525542
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 later543
+reached the start, and where backfill is at, plus the undecryptable messages in544
+scope. it comes with every page545
+546
+the analyser makes a token of every run of letters and digits up to 64 chars,547
+so hashes and long words fit, and lowercases and folds accents, so `cafe` finds548
+`Café`. han, kana, hangul and thai have no spaces to split on, so every char is549
+a token and so is every pair of neighbours, and a query for a run needs all its550
+pairs. that finds a word inside a sentence at the cost of the odd match across a551
+word boundary. emoji are tokens too. queries have no field syntax, so `re:` or a552
+url is just its words. words all have to match, quotes make a phrase and `-`553
+excludes, and a query with nothing left to find is `Invalid` rather than554
+everything in scope. there's no stemming536555
537556
a hit is read back when it's shown, `load_or_fetch_event` for the original and538557
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 it558
+server's `/event` after, eight at a time. the original's bundled edit is ignored,559
+since the index already knows the newest one. one that's turned out redacted is560
+dropped from the page and from the index. one that can't be read at all, gone561
+from the cache with the server unreachable or refusing, is left out of that page562
+but stays indexed, so it shows up again once it can be read. the cursor still563
+moves past it564
+565
+tantivy's merger times itself with `Instant::now()` whenever a `log` logger is on566
+at debug, and that panics in wasm. on 2026-10-25 we capped `log` at info at567
+compile time in wasm, `max_level_info` and `release_max_level_info` both, since568
+in release the release cap wins. it's all or nothing for the whole wasm build, so569
+a host gives up `log` debug and trace from every crate, not just tantivy, and570
+asking for any other cap is log's own "multiple max_level_* features set" error.571
+tantivy timing with something wasm can call, or reading `Instant` only behind572
+its own feature, reopens it543573
544574
## the sdk fork