Skip to content

Conversation

@kaylendog
Copy link
Contributor

@kaylendog kaylendog commented Sep 25, 2025

When the labs flag ("Encrypted state events") is enabled, a new option ("Encrypt state events") appears when creating a room.

Whether or not the labs flag is enabled, in a room created with this option, state events are encrypted and decrypted as specified in MSC4362.

image image

People invited to the room later (without MSC4268 enabled) will not be able to decrypt state (e.g. room name) that was sent before they joined.

Checklist

@kaylendog
Copy link
Contributor Author

Two potential considerations:

  • What do clients without the flag enabled see when using a room with state event encryption enabled?
  • Can clients without the flag enabled still send encrypted state events if they are sent in a room with state encryption enabled?

@Half-Shot
Copy link
Member

Can clients without the flag enabled still send encrypted state events if they are sent in a room with state encryption enabled?

IMO it would make sense that clients with this flag enabled can create / enable rooms to have encrypted state, but all supporting clients regardless of features should be able to interact in these rooms. So, they should probably attempt to send encrypted state if the room requires it.

@kaylendog
Copy link
Contributor Author

Can clients without the flag enabled still send encrypted state events if they are sent in a room with state encryption enabled?

IMO it would make sense that clients with this flag enabled can create / enable rooms to have encrypted state, but all supporting clients regardless of features should be able to interact in these rooms. So, they should probably attempt to send encrypted state if the room requires it.

I agree! The last commit I made locks MatrixClient.enableEncryptedStateEvents to true, but the toggle switch on the create room dialogue is still locked behind the lab flag.

@kaylendog
Copy link
Contributor Author

kaylendog commented Sep 25, 2025

It appears that the state events sent immediately after room creation are not encrypted - investigating... Although this may be preferable behaviour - clients could fall back to this if room key sharing ever fails?

@kaylendog kaylendog changed the title Implement lab for encrypted state events (MSC3414) Implement lab for encrypted state events (MSC3414/MSC4362) Sep 26, 2025
@kaylendog
Copy link
Contributor Author

Latest changes enabling room name encryption might belong in the JS SDK? Thoughts appreciated!

@kaylendog
Copy link
Contributor Author

It will be quite challenging to improve coverage on src/createRoom.ts, since it relies on an event listener attached to RoomState. There doesn't seem to be any existing infrastructure for mocking this behaviour.

Copy link
Member

@florianduros florianduros left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First quick review :)

Comment on lines 351 to 355
const timeout = setTimeout(() => {
logger.warn("Timed out while waiting for room to enable encryption");
roomState.off(RoomStateEvent.Update, onRoomStateUpdate);
resolve();
}, 3000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have to wait for this long, we need a spinner or some feedback to the user

Copy link
Contributor Author

@kaylendog kaylendog Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an existing spinner, I could force it to be shown If state event encryption is enabled?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@florianduros this code (as linked by Skye above) has existing logic for whether or not we want to show a spinner, so I am guessing that this already covers what we need - if it's a "background" operation then we should not show a spinner, and if it's a "foreground" operation then we should. All this PR does is introduce a potential additional delay, but because there is already a possibility of a spinner I think we can assume that this is already potentially slow, so nothing has materially changed here.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay make sense. We can add later a spinner if we think the ux lacks feedback


const roomState = resolvedRoom.getLiveTimeline().getState(Direction.Forward)!;

// Soft fail, since the room will still be functional if the initial state is not encrypted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if it fails, how do we inform the user?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to write that we don't care, but I've changed my mind: I think we should fail if this doesn't work: otherwise we will send potentially-confidential information in the clear.

Copy link
Member

@richvdh richvdh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few drive-by comments; sorry, haven't done a full review

Comment on lines 373 to 383
// Set room avatar
if (opts.avatar) {
let url: string;
if (opts.avatar instanceof File) {
({ content_uri: url } = await client.uploadContent(opts.avatar));
} else {
url = opts.avatar;
}
await client.sendStateEvent(roomId, EventType.RoomAvatar, { url }, "");
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you could do with a test that hits this codepath

Comment on lines 343 to 366
await new Promise<void>((resolve, reject) => {
if (resolvedRoom.hasEncryptionStateEvent()) {
return resolve();
}

const roomState = resolvedRoom.getLiveTimeline().getState(Direction.Forward)!;

// Soft fail, since the room will still be functional if the initial state is not encrypted.
const timeout = setTimeout(() => {
logger.warn("Timed out while waiting for room to enable encryption");
roomState.off(RoomStateEvent.Update, onRoomStateUpdate);
resolve();
}, 3000);

const onRoomStateUpdate = (state: RoomState): void => {
if (state.getStateEvents(EventType.RoomEncryption, "")) {
roomState.off(RoomStateEvent.Update, onRoomStateUpdate);
clearTimeout(timeout);
resolve();
}
};

roomState.on(RoomStateEvent.Update, onRoomStateUpdate);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the createRoom function is way too long and complicated. Factor this bit (at least) out to a separate function?

Copy link
Member

@andybalaam andybalaam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a review, primarily to remind myself what needs doing when I get back to this.


let e2eeStateSection: JSX.Element | undefined;
if (
SettingsStore.getValue("feature_msc3414_encrypted_state_events", null, false) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we replace 3414 with 4362 everywhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, no because MSC4362 says to use prefix io.element.msc3414.encrypt_state_events

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we could rename the feature flag, though that might get confusing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do kinda think that we should rename the feature flag, and change the comments in the rust-sdk. We're implementing the proposals of MSC4362, not MSC3414.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, no because MSC4362 says to use prefix io.element.msc3414.encrypt_state_events

Perhaps MSC4362 should be adjusted to use just encrypt_state_events, as in MSC3414, which we can move to if it ever gets merged into the spec? I agree with Rich that this should probably be io.element.msc4362.encrypt_state_events. I wrote up MSC4362 primarily to document what I had implemented. Still, now that the proposal exists, I think we should start working more in the other direction, where the proposal informs the implementation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated MSC4362 to use io.element.msc4362.encrypt_state_events and I will update this PR to reflect that.

Copy link
Member

@andybalaam andybalaam Nov 17, 2025

@andybalaam andybalaam changed the title Implement lab for encrypted state events (MSC3414/MSC4362) Implement lab for encrypted state events MSC4362 Nov 14, 2025
@andybalaam andybalaam changed the title Implement lab for encrypted state events MSC4362 Add labs flag for encrypted state events MSC4362 Nov 14, 2025
Since what we have implemented is described by that MSC.
@andybalaam andybalaam changed the title Add labs flag for encrypted state events MSC4362 Support encrypted state events MSC4362 Nov 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants