# 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 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

Run `Engine::run` and drain its single `EventStream` independently of command
submission. `ClientHandle::send` returns an enqueue ID; match that ID against
`CommandSucceeded`, `CommandSucceededWithRoom`, or `CommandFailed` before
reporting completion. A successful message command only enters the SDK send
queue; the timeline item's send state reports subsequent network delivery.

Start a timeline from `TimelineReset`, then apply `TimelineChanged` diffs with
`TimelineDiff::try_apply`. If a diff does not match the current snapshot, stop
applying diffs for that timeline and send `TimelineCommand::Resync`. Resume only
after its fresh `TimelineReset`; the reset precedes the command completion.

With `sqlite`, persist the initial `Session::record()` separately from its
encrypted SQLite 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 path and passphrase.

`LoggedOut` and `SessionAccessRejected` both stop the engine. after a logout or a
hard rejection the record and the store are dead, bin them. a soft logout wants
the store kept and a fresh login with `PasswordLogin::device_id` set to the old
device, or the e2ee keys go with it. a bare `Stopped` means keep everything. 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.

## 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. 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
```
