Skip to main content

Crate mail4agent_messenger

Crate mail4agent_messenger 

Source
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::OutgoingRequest values; a shell (native or web) performs the actual HTTP call and feeds the result back in as an wire::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::FlushGate batches dirty/deleted records into persist::FlushBatch values 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’s lib.rs/sync_engine.rs, work pieces M13a+M13b): the /sync loop, to-device/device-list/room ingestion, Megolm decrypt, the receive-side MessengerCommand variants (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, and crate::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 to records.log and sync_datas the file; boot calls replay to 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_data can be truncated away rather than re-attempted forever. encode_snapshot is the same frame format collapsed to a single frame, for the shell’s own periodic compaction into snapshot.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-memory CryptoStore/ StateStore implementation every later piece (crypto, room, sync engine) codes against, plus the sealed-record export/import protocol that ties it to crate::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.