Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d3c839a
feat(event cache): Add a method to access the linked chunk mutably
poljar Sep 26, 2025
8103b9c
feat(event cache): Create the redecryptor
poljar Sep 24, 2025
f2cc6c6
test(redecryptor): Add a test to show that the redecryptor works
poljar Sep 26, 2025
e934235
feat(redecryptor): Rejigger things so we can relisten to the room key…
poljar Oct 2, 2025
5c3bca8
doc(event cache): Document the redecryptor
poljar Oct 2, 2025
4ed2393
feat(event cache): Enable the redecryptor in the event cache
poljar Sep 25, 2025
a2f89e8
feat: Redecryptor start to send out redecryptor reports
poljar Oct 7, 2025
621d936
feat(redecryptor): Let the redecryptor listen to room key withheld up…
poljar Oct 10, 2025
3a0a5b9
feat(redecryptor): Use the room to redecrypt events
poljar Oct 10, 2025
7e98858
feat(redecryptor): More precise logs for the redecryption attempts
poljar Oct 16, 2025
4109fdd
feat(redecryptor): Post-process the events once they are replaced
poljar Oct 16, 2025
4a519bd
test(redecryptor): More tests for the redecryptor
poljar Oct 16, 2025
4c4cd41
test(timeline): Workarounds to get the timeline tests passing
poljar Oct 15, 2025
d7d4730
docs(redecryptor): Document the redecryptor a bit more
poljar Oct 15, 2025
d2eab60
fix(event-cache): Limit the visibility of room_linked_chunk_mut a bit…
poljar Nov 12, 2025
84a21a4
fix(event-cache): Don't hold on to the event cache locks as long when…
poljar Nov 12, 2025
3ad7062
chore(redecryptor): Use relative imports more often
poljar Nov 12, 2025
717f016
docs(redecryptor): Add some docs to the Redecryptor struct itself
poljar Nov 12, 2025
952c5af
chore(redecryptor): Time how long it takes to replace UTDs
poljar Nov 12, 2025
f9c23b3
refactor(redecryptor): Use an abort handle to manage the redecryption…
poljar Nov 12, 2025
38df621
chore(event-cache): Limit the visibility of post_process_new_events
poljar Nov 12, 2025
2e9e9ae
chore(redecryptor): Ensure the upgrade_event_cache method is inlined
poljar Nov 12, 2025
913ebe9
docs(redecryptor): Clarify that we're talking about the UI timeline i…
poljar Nov 12, 2025
0d08ed0
refactor(redecryptor): Add some type aliases for the event ID/event t…
poljar Nov 12, 2025
9508675
fix(redecryptor): Early return if we don't have any events to process
poljar Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 135 additions & 25 deletions crates/matrix-sdk/src/event_cache/redecryptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! The Redecryptor (Rd) is a layer and long-running background task which
//! handles redecryption of events in case we couldn't decrypt them imediatelly.
//! The Redecryptor (affectionately known as R2D2) is a layer and long-running
//! background task which handles redecryption of events in case we couldn't
//! decrypt them imediatelly.
//!
//! Rd listens to the OlmMachine for received room keys and new
//! There are various reasons why a room key might not be available imediatelly
//! when the event becomes available:
//! - The to-device message containing the room key just arrives late, i.e.
//! after the room event.
//! - The event is a historic event and we need to first download the room
//! key from the backup.
//! - The event is a historic event in a previously unjoined room, we need
//! to receive historic room keys as defined in [MSC3061](https://github.com/matrix-org/matrix-spec/pull/1655#issuecomment-2213152255).
//!
//! R2D2 listens to the OlmMachine for received room keys and new
//! m.room_key.withheld events.
//!
//! If a new room key has been received it attempts to find any UTDs in the
//! [`EventCache`]. If Rd decrypts any UTDs from the event cache it will replace
//! the events in the cache and send out new [`RoomEventCacheUpdates`] to any of
//! its listeners.
//! [`EventCache`]. If R2D2 decrypts any UTDs from the event cache it will
//! replace the events in the cache and send out new [`RoomEventCacheUpdates`]
//! to any of its listeners.
//!
//! If a new withheld info has been received it attempts to find any relevant
//! events and updates the [`EncryptionInfo`] of an event.
Expand All @@ -31,28 +41,49 @@
//! a `None` on the room keys stream and we need to re-listen to it.
//!
//! Another gotcha is that room keys might be received on another process if the
//! Client is operating on a iOS device. A separate process is used in this case
//! to receive push notifications. In this case the room key will be received
//! and Rd won't get notified about it. To work around this decryption requests
//! can be explicitly sent to Rd.
//! Client is operating on a Apple device. A separate process is used in this
//! case to receive push notifications. In this case the room key will be
//! received and R2D2 won't get notified about it. To work around this
//! decryption requests can be explicitly sent to R2D2.
//!
//! ```markdown
//!
//! .----------------------.
//! | |
//! | Beeb, boop! |
//! | .
//! ----------------------._ \
//! -; _____
//! .`/L|__`.
//! / =[_]O|` \
//! |"+_____":|
//! __:='|____`-:__
//! ||[] ||====|| []||
//! ||[] ||====|| []||
//! |:== ||====|| ==:|
//! ||[] ||====|| []||
//! ||[] ||====|| []||
//! _||_ ||====|| _||_
//! (====) |:====:| (====)
//! }--{ | | | | }--{
//! (____) |_| |_| (____)
//!
//! ┌─────────────┐
//! │ │
//! ┌───────────┤ Timeline │◄────────────┐
//! ┌───────────┤ Timeline │◄────────────┐
//! │ │ │ │
//! │ └─────▲───────┘ │
//! │
//! │
//! │
//! Decryption Redecryptor
//! request report
//! │ RoomEventCacheUpdates
//! │
//! │
//! │ ┌──────────┴────────────┐ │
//! │ └──────▲──────┘ │
//! │
//! │
//! │
//! Decryption Redecryptor
//! request report
//! │ RoomEventCacheUpdates
//! │
//! │
//! │ ┌───────────┴───────────┐ │
//! │ │ │ │
//! └──────► Redecryptor │────────┘
//! └──────► R2D2 │────────┘
//! │ │
//! └───────────▲───────────┘
//! │
Expand All @@ -67,6 +98,7 @@
//! │ OlmMachine │
//! │ │
//! └──────────────┘
//! ```

use std::{collections::BTreeSet, pin::Pin, sync::Weak};

Expand Down Expand Up @@ -101,6 +133,8 @@ use tokio_stream::wrappers::{
};
use tracing::{info, instrument, trace, warn};

#[cfg(doc)]
use super::RoomEventCache;
use crate::{
Room,
event_cache::{
Expand Down Expand Up @@ -434,7 +468,44 @@ impl EventCache {

/// Explicitly request the redecryption of a set of events.
///
/// TODO: Explain when and why this might be useful.
/// The redecryption logic in the event cache might sometimes miss that a
/// room key has become available and that a certain set of events has
/// become decryptable.
///
/// This might happen because some room keys might arrive in a separate
/// process handling push notifications or if a room key arrives but the
/// process shuts down before we could have decrypted the events.
///
/// For this reason it is useful to tell the event cache explicitly that
/// some events should be retried to be redecrypted.
///
/// This method allows you to do so. The events that get decrypted, if any,
/// will be advertised over the usual event cache subscription mechanism
/// which can be accessed using the [`RoomEventCache::subscribe()`]
/// method.
///
/// # Examples
///
/// ```no_run
/// # use matrix_sdk::{Client, event_cache::DecryptionRetryRequest};
/// # use url::Url;
/// # use ruma::owned_room_id;
/// # use std::collections::BTreeSet;
/// # async {
/// # let homeserver = Url::parse("http://localhost:8080")?;
/// # let client = Client::new(homeserver).await?;
/// let event_cache = client.event_cache();
/// let room_id = owned_room_id!("!my_room:localhost");
///
/// let request = DecryptionRetryRequest {
/// room_id,
/// utd_session_ids: BTreeSet::from(["session_id".into()]),
/// refresh_info_session_ids: BTreeSet::new(),
/// };
///
/// event_cache.request_decryption(request);
/// # anyhow::Ok(()) };
/// ```
pub fn request_decryption(&self, request: DecryptionRetryRequest) {
let _ =
self.inner.redecryption_channels.decryption_request_sender.send(request).inspect_err(
Expand All @@ -444,8 +515,47 @@ impl EventCache {

/// Subscribe to reports that the redecryptor generates.
///
/// TODO: Explain when the redecryptor might send such reports.
pub fn subscrube_to_decryption_reports(
/// The redecryption logic in the event cache might sometimes miss that a
/// room key has become available and that a certain set of events has
/// become decryptable.
///
/// This might happen because some room keys might arrive in a separate
/// process handling push notifications or if room keys arrive faster than
/// we can handle them.
///
/// This stream can be used to get notified about such situations as well as
/// a general channel where the event cache reports which events got
/// successfully redecrypted.
///
/// # Examples
///
/// ```no_run
/// # use matrix_sdk::{Client, event_cache::RedecryptorReport};
/// # use url::Url;
/// # use tokio_stream::StreamExt;
/// # async {
/// # let homeserver = Url::parse("http://localhost:8080")?;
/// # let client = Client::new(homeserver).await?;
/// let event_cache = client.event_cache();
///
/// let mut stream = event_cache.subscribe_to_decryption_reports();
///
/// while let Some(Ok(report)) = stream.next().await {
/// match report {
/// RedecryptorReport::Lagging => {
/// // The event cache might have missed to redecrypt some events. We should tell
/// // it which events we care about, i.e. which events we're displaying to the
/// // user, and let it redecrypt things with an explicit request.
/// }
/// RedecryptorReport::ResolvedUtds { .. } => {
/// // This may be interesting for statistical reasons or in case we'd like to
/// // fetch and inspect these events in some manner.
/// }
/// }
/// }
/// # anyhow::Ok(()) };
/// ```
pub fn subscribe_to_decryption_reports(
&self,
) -> impl Stream<Item = Result<RedecryptorReport, BroadcastStreamRecvError>> {
BroadcastStream::new(self.inner.redecryption_channels.utd_reporter.subscribe())
Expand Down
Loading