A stateful account-data mock backs a real SecretStore, so the builder
future, load_key(), and schedule_dehydration() now run in CI instead of
only in the live test. Replaces the ignored SSSS placeholder.
Signed-off-by: Jason Volk <jason@zemos.net>
- Bump the event broadcast capacity to the crate-standard 100.
- Keep a scheduled rotation alive across a standalone rehydrate(): the
post-drain cleanup now uses a private delete_device() helper while
stop() stays in the public delete().
- Source the module example pickle key from the caller; a freshly
generated key can never decrypt an existing device.
- Add the missing #[instrument] on is_key_stored().
- Spell out the sdk_dd import alias as dehydrated_devices.
- Give StartDehydratedDevicesSettings uniffi field defaults matching the
builder initial state.
- Scope the FFI zeroize doc to the Rust-side copy of the recovery key.
- Complete the truncated license header and spell out abbreviated event
flags in the live test.
Signed-off-by: Jason Volk <jason@zemos.net>
Room keys drained from a dehydrated device were imported with a hardcoded
TrustRequirement::Untrusted, sidestepping the sender-trust policy the
client applies to the same events when they arrive through sync.
Signed-off-by: Jason Volk <jason@zemos.net>
A drain stopped by the batch cap or a repeated cursor now returns
DehydratedDeviceError::DrainTruncated instead of emitting
RehydrationCompleted and deleting the device, since the server only keeps
the undelivered queue until deletion. A retried rehydrate() resumes from
the full queue and already-imported room keys re-import idempotently.
Signed-off-by: Jason Volk <jason@zemos.net>
The live MSC3814 round trip now subscribes via `state_stream()` and drives
`start()` through its builder, following the renamed method and the removed
`StartDehydrationOpts` struct.
Signed-off-by: Jason Volk <jason@zemos.net>
The drain loop now stops at a hard `MAX_TO_DEVICE_EVENTS` cap and counts with
saturating arithmetic, so a server that keeps returning fresh events and
cursors cannot loop without end or overflow the counters.
Signed-off-by: Jason Volk <jason@zemos.net>
The replay check in `rehydrate` compares the served device id against the last
one this client uploaded; that id now lives in the crypto store instead of
process memory, so the check survives a client restart.
Signed-off-by: Jason Volk <jason@zemos.net>
The `DEHYDRATION_INTERVAL` constant is an internal detail of the rotation task
rather than part of the public API, and its doc no longer refers to another
library.
Signed-off-by: Jason Volk <jason@zemos.net>
`start()` now returns a `StartDehydration` named future with
`create_new_key()`, `skip_rehydration()`, and `only_if_key_cached()` builder
methods, matching the recovery and backups subsystems, and the
`StartDehydrationOpts` struct is gone.
Signed-off-by: Jason Volk <jason@zemos.net>
The subscription method is now `state_stream()`, matching the backups and
recovery submodules; `events` was too overloaded a name in a Matrix client.
Signed-off-by: Jason Volk <jason@zemos.net>
The rotation task now uses `TaskMonitor::spawn_infinite_task` with
`abort_on_drop`, which aborts on every platform including Wasm and reports
accidental panics, so the hand-rolled `Drop` guard and its cfg gate are gone.
Signed-off-by: Jason Volk <jason@zemos.net>
The public dehydrated-device docs no longer name matrix-js-sdk or point at
an internal code review; the remaining notes describe the event types on
their own.
Signed-off-by: Jason Volk <jason@zemos.net>
Mirrors the SDK shape on the FFI `Encryption` struct with
`is_dehydrated_device_supported`, `create_dehydrated_device`,
`rehydrate_dehydrated_device`, `delete_dehydrated_device`,
`start_dehydrated_devices`, `stop_dehydrated_devices`, and a
`dehydrated_device_event_listener` callback interface so
Element X (Kotlin and Swift) can drive MSC3814 without bypassing
the SDK's lifecycle events.
Signed-off-by: Jason Volk <jason@zemos.net>
Adds two integration tests under `matrix-sdk-integration-testing`
exercising the wire endpoints end to end: a direct
`create` → `rehydrate` round trip with a locally generated pickle key,
and a `start` lifecycle that resolves the pickle key out of Secret
Storage and asserts that two consecutive `start` calls upload
distinct device IDs.
Signed-off-by: Jason Volk <jason@zemos.net>
Drives the full lifecycle through the new `MatrixMockServer` helpers
across 13 cases covering support probing, create with default and
explicit display names, delete on the three server responses, the
rehydrate round trip with single-page and paginated to-device feeds,
wrong-pickle-key handling, and the empty-server short-circuit.
Signed-off-by: Jason Volk <jason@zemos.net>
Adds prebuilt mocks for `GET`, `PUT`, `DELETE /dehydrated_device` and
`POST /dehydrated_device/{device_id}/events`, with helpers for the
common success, `M_NOT_FOUND`, and `M_UNRECOGNIZED` responses and
body matchers (`match_next_batch`, `match_missing_next_batch`) so
integration tests can drive the full lifecycle without hand-rolling
wiremock.
Signed-off-by: Jason Volk <jason@zemos.net>
Each public method on `DehydratedDevices` now carries a runnable `no_run`
example that shows the typical call site, so the docs.rs surface is
discoverable without cross-referencing the integration tests.
Signed-off-by: Jason Volk <jason@zemos.net>
Adds `Encryption::dehydrated_devices()` returning a manager with
`is_supported`, `create`, `rehydrate`, `delete`, `start`, and `stop`,
plus a Secret Storage round trip for the pickle key, a broadcast
event channel for lifecycle observability, and a weekly rotation
task. The module is gated behind `e2e-encryption` and follows the
matrix-js-sdk `DehydratedDeviceManager` shape so applications can
adopt MSC3814 without writing the wire-glue themselves.
Signed-off-by: Jason Volk <jason@zemos.net>
This required a bump of the chacha20 dep in the
matrix-sdk-store-encryption crate as well due to conflicting versions of
a common dependency between them.
Use `ReloadPolicy::Manual` for the Tantivy `IndexReader` of a `RoomIndex`.
Tantivy's default policy spawns a meta file watcher thread per index, i.e. one
per room, and panics if the thread cannot be spawned. Commits already reload the
reader explicitly, so the watcher is pure overhead.
Fixes `RingBuffer` capacity being lost across serialization and
deserialization. The buffer previously relied on the backing
`VecDeque`’s allocation capacity as its logical capacity. Since serde
only serialized the items, deserialization recreated the `VecDeque` with
capacity equal to the number of stored items. A partially filled buffer
could therefore come back effectively full, causing every subsequent
push to evict an existing item.
`RoomReadReceipts::pending` uses a `RingBuffer` to retain read receipts
whose target events are not currently known by the timeline. This can
happen when a receipt arrives before the corresponding event, for
example with limited sync responses or federation delays. Those pending
receipts are persisted as part of `RoomInfo`. After restoring them, the
old implementation could shrink the ring buffer to the number of
currently stored receipts. Once that happened, newly received pending
receipts would evict older ones immediately. In particular, the receipt
needed to advance `latest_active` could be lost, leaving unread counts
incorrect even though the client had received the relevant receipt.
By preserving the logical capacity, pending read receipts survive
persistence and can continue to be matched when their corresponding
events become available.
This patch adds `TracingTimer`s in the `StateLock::read`, `write` and
`clear_and_reload` methods. The idea is to get a sense of how long it
takes to compute these locks.
This patch also adds regular logs to indicate when a lock is acquiring,
or acquired.