# macltok

`macltok` is a compact Rust application backend built above `matrix-sdk` and
`matrix-sdk-ui`. It aims to provide the stateful behavior needed by a useful
Matrix client without depending on a GUI toolkit or inventing another protocol
SDK.

The crate currently provides

- typed commands, owned UI-safe events and bounded per-client transport
- Sliding Sync with normalized connection and room-list state
- independently retained live room timelines and backwards pagination
- text, emote, reply, edit, redaction, reaction, receipt and typing operations
- raw encrypted or unencrypted media acquisition with configurable byte limits
- encryption state, recovery-key import and SAS verification
- password login and session restoration with caller-owned encrypted SQLite or
  IndexedDB stores

Matrix room, event, user, device and transaction identifiers remain strongly
typed. A toolkit timeline identity covers local echoes without pretending they
already have remote event IDs.

## Consuming the engine

spawn `Engine::run`, drain the `EventStream` from its own task, and talk to the
engine through `ClientHandle`. every command answers once, awaited
(`handle.send_message(..).await?`) or as a `Pending` you can await later or
drop. a sent message answers when the sdk send queue has it, delivery shows up
as the timeline item's send state. reads like `rooms`, `members` and `media`
go straight to the sdk. the stream only carries state

start a timeline from `TimelineReset`, then apply `TimelineChanged` diffs with
`TimelineDiff::try_apply`. when one doesn't fit, stop applying, call `resync`,
and pick up from the next `TimelineReset`

With `sqlite` or `indexeddb`, persist the initial `Session::record()`
separately from its encrypted store. On `SessionTokensChanged`, obtain a new record from
`ClientHandle::session_record()` and replace the saved record atomically in
protected storage. Restore with the same store location and passphrase.

`Engine::run` resolves to a `Termination` saying why it stopped, and
`Termination::retain` says what to do about it. after `LoggedOut` or
`AccessRevoked` the record and the store are dead, bin them. `SoftLoggedOut`
wants the store kept and a fresh login with `PasswordLogin::device_id` set to the
device it carries, or the e2ee keys go with it. `ShutDown` and `HandlesDropped`
keep everything. an `Err` is a real failure and kills nothing. one store
directory per login, the sdk won't share a crypto store between devices.
The headless example is
login-only; it does not persist sessions across runs.

## wasm

macltok builds for `wasm32-unknown-unknown`. the `indexeddb` feature gives you
`StoreConfig::indexeddb` in place of `StoreConfig::sqlite`, and asking for either
on the wrong target won't compile. `Engine::run` hands back a `LocalBoxFuture`
there, so spawn it with `wasm_bindgen_futures::spawn_local`

getrandom picks its browser backend from a cfg flag, and `.cargo/config.toml`
only reaches this crate's own builds. every cdylib that pulls macltok in needs
it too

```toml
[target.wasm32-unknown-unknown]
rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""]
```

what's different in a browser

- a panic aborts the whole instance. natively a panicking command costs its
  caller an `Interrupted`, here it takes the engine and everything else with it
- `MediaUploadProgress` only ever reports completion, the sdk's wasm http path
  has no upload progress
- the sdk doesn't retry requests or time them out individually. sync still
  restarts itself with backoff, that part is ours
- two tabs or workers on one store each need `StoreConfig::with_lock_holder`
  with a name of their own, or they both think they hold the crypto store

## Dependency policy

UI frameworks, image decoders, application-directory discovery and logging
subscribers do not belong in this crate. SQLite remains optional through the
`sqlite` and `bundled-sqlite` features, IndexedDB through `indexeddb`. Media
decoding belongs to the consumer.

## Verification

```sh
nix develop --command cargo fmt -- --check
nix develop --command cargo test --all-targets
nix develop --command cargo test --all-targets --features sqlite
nix develop --command cargo clippy --all-targets -- -D warnings
nix develop --command cargo clippy --all-targets --features sqlite -- -D warnings
nix flake check
```
