Expand description
mail4agent-messenger — sans-I/O Matrix Client-Server-subset sync + E2EE
(Olm/Megolm) engine. Shells execute wire::OutgoingRequest and persist
the sealed records this crate exports. No network and no disk inside the
library.
§Contract
This crate performs no I/O of any kind: no network sockets, no
filesystem access, no browser storage calls, and no async/.await
anywhere in its source (plan §0). Every side effect crosses the
boundary as plain data:
- Network: the core hands out
wire::OutgoingRequestvalues; a shell (native or web) performs the actual HTTP call and feeds the result back in as anwire::HttpResponseDescriptor. The core never opens a socket itself. - Disk/persistence: the core owns an in-memory working set and
marks records dirty as it mutates state;
persist::FlushGatebatches dirty/deleted records intopersist::FlushBatchvalues a shell writes to durable storage (the filesystem on native, IndexedDB on web) and acks back in. The core never calls a storage API itself.
§Flush-before-send barrier
persist::FlushEpoch is the barrier the plan’s manager decision #3
requires: a state mutation (an Olm/Megolm ratchet advance, most
critically) must be durably flushed and acknowledged before the
ciphertext that used it is allowed to leave the process — scoped
per-request (M12’s persist::RequiredSeq epoch) rather than as one
global barrier, so an unrelated /sync or typing indicator is never
held behind it. See persist’s module doc for the exact ordering
guarantee this makes, with a worked example.
§Synchronous by construction
No function in this crate’s public API returns a Future or is
declared async fn. This is what lets the same source compile and
unit-test identically on native and wasm32-unknown-unknown: the only
platform-divergent code is which transport/executor a shell wires in,
never this crate.
§Pieces landed so far (M1, M2)
M1 — the wire-boundary primitives: Matrix identifier newtypes
(ids), this crate’s error type (error), the
OutgoingRequest/HttpResponseDescriptor shapes (wire), and the
flush-before-send barrier’s data structures (persist).
M2 — the working-set store (store): store::Store, the
in-memory CryptoStore/StateStore implementation later pieces
(crypto, room, sync engine) code against, sealed for export through
persist::FlushGate by a store::RecordCodec (this piece ships only
store::InsecurePlainCodecForTests, gated behind the test-support
Cargo feature; the real AES-256-GCM codec is a later piece, M14).
M3 — the /sync and event wire types (wire::events,
wire::sync): wire::sync::SyncResponse and
wire::sync::parse_sync_response, the Matrix event envelope
(wire::events::RawEvent, wire::events::StrippedStateEvent,
wire::events::ToDeviceEvent), relation aggregation
(wire::events::RelatesTo), and typed content for the state/timeline/
to-device/account-data/ephemeral event types this crate reads.
M5 — Matrix canonical-JSON signing (canonical_json:
canonical_json::sign_json, canonical_json::verify_json_signature)
and the device’s own Olm account (crypto::account::OlmAccountState):
generate-once-and-persist identity, one-time/fallback key maintenance off
/sync’s own counters, and the signed device_keys//keys/upload
request bodies built from it.
M4 — room state, timeline assembly, and relation aggregation
(room): room::RoomState folded from m.room.* state events plus
room::RoomKind derivation, room::Timeline assembled from /sync
windows and /messages back-pages with local-echo reconciliation and
live v11-allow-list redaction, and room::RelationsBundle aggregation
(reactions, edits, replies, threads).
M6 — device-list tracking (crypto::device_tracker) and per-device
Olm 1:1 sessions (crypto::olm_sessions): /keys/query//keys/claim
request building and response validation, to-device encryption/
decryption with full payload binding, and the security checks that keep
a key change or an unverified device from being silently trusted.
M7 — Megolm group sessions (crypto::group_sessions): lazy
outbound-session creation and rotation, room-key sharing batched over
Olm to-device, and inbound decrypt with bound-sender authentication and
message-index replay protection.
M12 — the outgoing request queue (outgoing_queue):
outgoing_queue::OutgoingQueue tracks pending requests per
outgoing_queue::Lane (Sync/ToDevice/Room/Other), replaying
them idempotently with capped exponential backoff
(outgoing_queue::ResponseOutcome) and surviving a crash via
store::StateStore::pending_requests. persist::FlushGate gained a
per-request flush epoch (persist::FlushEpoch, persist::RequiredSeq)
replacing its earlier global barrier — see persist’s module doc.
M13a — the single-writer kernel and the RECEIVE path
(core::MessengerCore): open/restart restore (including the
core::Counters sequence that backs every future
ids::RequestId/ids::TxnId), the single-flight /sync loop, full
ingestion order (to-device decrypt and m.room_key routing,
device-list/OTK/keys/query bookkeeping, room state/timeline/Megolm
decrypt, ephemeral/account-data folding), and the receive-side
core::MessengerCommand variants (MarkRead, SetTyping,
RetryDecryption, LoadOlder).
M13b — the SEND path, in the same module (core::MessengerCore):
a per-room ordered send pipeline for core::MessengerCommand::SendMessage/
React/Redact/RetrySend (key-query → key-claim → key-share → the
room event itself, for an encrypted room; straight to the wire
otherwise — key sharing is always awaited before the room event that
needs it), plus every room-membership/tag/typing command
(CreateRoom, JoinRoom, LeaveRoom, Invite, Kick, SetTag,
RemoveTag, SetTyping’s own debounce). Plan §3’s separate
sync_engine.rs never materialized as its own module — everything it
described lives directly in core instead, alongside the kernel it
would otherwise have had to be threaded through anyway.
M14 — at-rest sealing (store::sealed::SealedRecordCodec): the
production store::RecordCodec, AES-256-GCM under a caller-supplied
32-byte core::CoreSecrets::store_seal_key, binding each record’s own
storage key as AAD. core::MessengerCore::open_sealed is the
constructor a shell calls; core::MessengerCore::open stays generic so
this crate’s own tests keep using [store::InsecurePlainCodecForTests].
The session private key is the Olm account
crypto::account::OlmAccountState already generates. This crate does
not derive keys, read a passphrase, or own an identity scheme.
Re-exports§
pub use canonical_json::sign_json;pub use canonical_json::verify_json_signature;pub use canonical_json::CanonicalJsonError;pub use core::CoreConfig;pub use core::CoreSecrets;pub use core::Counters;pub use core::CreateRoomKind;pub use core::MessageKind;pub use core::MessengerCommand;pub use core::MessengerCore;pub use core::MessengerEvent;pub use core::OutgoingMessage;pub use core::PublicRoomsResultEntry;pub use core::UserDirectoryResultEntry;pub use crypto::account::OlmAccountState;pub use crypto::device_tracker::DeviceKeyChanged;pub use crypto::device_tracker::DeviceTracker;pub use crypto::device_tracker::DroppedDevice;pub use crypto::device_tracker::KeysQueryOutcome;pub use crypto::device_tracker::StoredDevice;pub use crypto::group_sessions::GroupDecryptError;pub use crypto::group_sessions::GroupSessionManager;pub use crypto::group_sessions::OutboundSessionUpdate;pub use crypto::group_sessions::RoomEventPlaintext;pub use crypto::group_sessions::UtdReason;pub use crypto::group_sessions::MAX_DEVICES_PER_SEND_TO_DEVICE_REQUEST;pub use crypto::olm_sessions::DecryptedToDevice;pub use crypto::olm_sessions::KeysClaimOutcome;pub use crypto::olm_sessions::KeysClaimRefusal;pub use crypto::olm_sessions::OlmDecryptError;pub use crypto::olm_sessions::OlmSessionManager;pub use error::MessengerError;pub use ids::DeviceId;pub use ids::EventId;pub use ids::RequestId;pub use ids::RoomId;pub use ids::TxnId;pub use ids::UserId;pub use outgoing_queue::Jitter;pub use outgoing_queue::Lane;pub use outgoing_queue::OutgoingQueue;pub use outgoing_queue::PendingRequest;pub use outgoing_queue::ResponseOutcome;pub use persist::FlushBatch;pub use persist::FlushEpoch;pub use persist::FlushGate;pub use persist::RecordKey;pub use persist::RequiredSeq;pub use persist::SealedRecord;pub use room::aggregate_relation;pub use room::highlight;pub use room::last_preview;pub use room::recompute_bundle;pub use room::EditRecord;pub use room::Forwarded;pub use room::GapMarker;pub use room::ItemContent;pub use room::MemberRole;pub use room::MemberState;pub use room::RelatingEvent;pub use room::RelationsBundle;pub use room::RoomKind;pub use room::RoomState;pub use room::SendState;pub use room::StateChangeSummary;pub use room::Timeline;pub use room::TimelineItem;pub use store::sealed::SealedRecordCodec;pub use store::CryptoStore;pub use store::RecordCodec;pub use store::StateStore;pub use store::Store;pub use store::StoreError;pub use wire::HttpMethod;pub use wire::HttpResponseDescriptor;pub use wire::OutgoingRequest;pub use wire::OutgoingRequestKind;
Modules§
- canonical_
json - Matrix canonical JSON (spec “Canonical JSON”:
https://spec.matrix.org/latest/appendices/#canonical-json) plus
sign_json/verify_json_signature, the two operations every signed Matrix object (device_keys, one-time/fallback keys, room events, …) is built from. - core
MessengerCore— the single-writer kernel tying every earlier piece together (plan §3’slib.rs/sync_engine.rs, work pieces M13a+M13b): the/syncloop, to-device/device-list/room ingestion, Megolm decrypt, the receive-sideMessengerCommandvariants (M13a), and the per-room ordered send pipeline plus every room-membership/tag/typing command (M13b, this module’s own “M13b send pipeline” section below).- crypto
- The Olm/Megolm/cross-signing state machine — plan §3’s
crypto/. - error
MessengerError, this crate’s single error type.- ids
- Matrix identifier newtypes.
- outgoing_
queue OutgoingQueue— the outgoing HTTP request queue: per-lane ordering, idempotent retries with capped exponential backoff, andcrate::store::StateStore-backed crash safety (plan §5, M12).- persist
- The flush-before-send barrier’s data structures.
- persist_
log - The native shell’s on-disk record log codec — pure encode/decode, no
filesystem access of its own (this crate’s own “no I/O” contract, crate
doc). A shell’s writer thread is the caller: it appends
encode_batch’s output torecords.logandsync_datas the file; boot callsreplayto rebuild the current record set and learn exactly how many bytes of the file were valid frames, so a torn tail left by a crash mid-write/ mid-sync_datacan be truncated away rather than re-attempted forever.encode_snapshotis the same frame format collapsed to a single frame, for the shell’s own periodic compaction intosnapshot.bin. The web shell never touches this module — an IndexedDB transaction already gives it atomicity for free. - room
- Room state, timeline assembly, and relation aggregation.
- store
- The working-set store:
Store, the in-memoryCryptoStore/StateStoreimplementation every later piece (crypto, room, sync engine) codes against, plus the sealed-record export/import protocol that ties it tocrate::persist::FlushGate. - wire
- Wire-boundary data types: the shapes that cross this crate’s async boundary as plain data (see the crate doc’s “Contract” section). This module never performs an HTTP call itself — it only builds the request description and parses the response description a shell hands back.