xmtp / libxmtp

LibXMTP is a shared library encapsulating the core functionality of the XMTP messaging protocol, such as cryptography, networking, and language bindings.
MIT License
39 stars 16 forks source link

Self-removal/muting support MVP #441

Open neekolas opened 7 months ago

neekolas commented 7 months ago

Added by Rich: MLS allows you to publish proposals and commits. In some systems, the proposals are sent separately, and then a later commit is published by someone else that commits all of the previous proposals. In our current implementation, all proposals are immediately committed by the proposer, so we don't use separate proposal messages, just commits that include the proposals.

The problem with removing yourself is that MLS prohibits commiting a Remove proposal that removes the committer. I think the reason is that generating commits requires deriving secrets to use for the next epoch of the group, and therefore anyone being removed from the group should not have access to those secrets.

It seems there are two ways to solve this problem:

  1. Add support for separating out proposals and commits. So to remove yourself, you publish a proposal, and then somebody else commits it. This requires a bunch of changes to how we do proposals/commits today. We will need to chat about how to design this.

  2. You remove yourself from the group, but don't update the group encryption state (in essence, you've effectively 'muted' the group without changing the group membership). I don't like this so much, because you still need a way to notify your other devices you've done this, otherwise you're only muting for your current device.

richardhuaaa commented 4 months ago

The proposer needs to create an intent, and make sure that the proposal makes it onto the group successfully, before deleting the group on their end. Logic to add for the proposer:

  1. Add another membership state like LEFT here: https://github.com/xmtp/libxmtp/blob/main/xmtp_mls/src/storage/encrypted_store/group.rs#L192
  2. Create a IntentKind::LeaveGroup intent type here.
  3. Cmd+F for IntentKind::RemoveMembers, and follow the same lifecycle:
    1. Just like remove_members(), add a method called leave_group() that constructs the LeaveGroup intent and then syncs.
    2. In get_publish_intent_data(), add another match arm for LeaveGroup. There is no IntentData to fetch - you know that you are removing all installations with your own wallet address. Use the same logic to gather the list of leaf nodes to remove, then build a list of proposals (not a commit) to remove them. Return this from get_publish_intent_data and it will be sent on the group.
      1. When saving the payload hash on the intent, we can just hash the first proposal, so that it's easier to match against later.
    3. In process_own_message(), add another case for IntentKind::LeaveGroup. Extract the PrivateMessageIn from the message: ProtocolMessage param, then decrypt it via openmls_group.process_message(). If the decryption fails, call conn.set_group_intent_to_publish() to reset the intent. If the decryption is successful, our proposal has been successfully synced, and we can go ahead and update the group membership state to LEFT. (conn.set_group_intent_committed() will automatically be called at the end of the function)
      1. We can update the group here because we don't care if the proposal is committed or not. We've done our end by making sure the proposal is correctly published, it's up to the rest of the group to commit it.
  4. We need to somehow expose that the group is 'left' to the bindings. Perhaps we can add an optional filter to whatever method lists groups if it doesn't already exist, and make sure the membership state is accessible to the bindings.

Once the committer receives the proposal, they can 'lazy commit' it the next time they interact with the group. Logic to add for the committer:

richardhuaaa commented 4 months ago

I wrote all of this up, but with XIP-46 coming, I just realized it may actually be easier to wait for that, and have the proposer just propose an update to the members list on the group context, and have the committer handle resolving the list of installations to remove. That might simplify the above significantly. In fact, it may be worth considering rolling this into the identity work.

richardhuaaa commented 3 months ago

Simpler metadata-based approach without floating proposals:

  1. Commit a metadata change to remove your inbox ID - use a new field called something like 'pending_removals'
  2. When any other installation processes metadata changes:
    1. If they remove yourself, go ahead and update the group state to LEFT_GROUP
    2. If they remove someone else, publish a commit to remove all of their installations from the group

Reason for this is there are some complexities with doing it through floating proposals: