Change:
The "parent_hash" field contains a hash of this node's parent node, as described in parent-hash.  The "unmerged_leaves" field lists the leaves under this parent node that are unmerged, according to their <del> indices among all the leaves in the tree. </del> <ins> indices among all the leaves in the tree.  The entries in the "unmerged_leaves" vector MUST be sorted in increasing order. </ins> 8.2.

Discussion:
While reviewing , I realized that we have a potential interop problem around . This struct feeds into the tree hash, so everyone in the group needs to have the entries in the same order or else they'll disagree on the tree hash. But the order isn't specified anywhere: All the document says right now is "add L's leaf index to the unmergedleaves list", which could be implemented as an insert anywhere in the list. This PR resolves the ambiguity by requiring the list be sorted.
There's a few implications of simply allowing forks: FS issues - clients would need to use initsecret twice, which means keeping around initsecret for a potential fork, which breaks FS. One resolution here is to use something from the ASTree for initsecret (maybe the nth app secret, or a new ratchet derived from astreenode[n]secret). Does detection yield any sort of meaningful resolution technique? Are we better off than we are now in terms of user experience? (What we have now, if the DS fails to order, is two (or N) groups of clients operating on different versions of the tree, which effectively partitions the group) I wrote about this here: URL suggesting merges as a resolution technique. The main downside is clients would need to keep around the list of proposals for a while. This isn't bad imo, it reduces complexity to some extent. I'm somewhat in favour of a non-pseudorandom tiebreaker if there's no other arguments against it. How many bits of sequence number? Have we considered using varints? Similar to the CTLS proposal - URL Other forking considerations, I think there's more involved than just changing the epoch identifier: We'll need merges as well, since forks can lead to inconsistent group membership: Suppose in epoch 1 a group has A, B, and C A removes C in epoch 2A B adds D in epoch 2B We now have two group states 2A with AB and 2B with ABCD - and it's unclear which group to use to send with. We could mandate that any client MUST send a "merge" commit on it's view of history before sending any message. This commit could just be the list of epochs that are being merged (+a usual DirectPath update?), and processing it would mean replaying all proposals up to the common ancestor for those epochs on the common ancestor.

Forward secrecy issues. By allowing forks, devices must now keep the previous epoch around to derive possible future forks - but this means we'll be re-using initsecret. A possible solution could be to use the nth app secret from the ASTree instead of initsecret as the start for the new epoch secret. This ties nicely with using a non-pseudorandom tiebreaker. Ordering implications It's unclear what a sensible ordering strategy is for the DS if we allow forking. (simply ensuring that each commit is based on the "current" epoch isn't well defined since there can be multiple "current" epochs). By allowing forks the DS need not enforce ordering at all imo. This means that apps need to keep around old epochs, but this is already required per (2). A different question is how many we need to keep around - perhaps a TTL or the past N epochs after a topological sort would make sense. Generally I think getting rid of server ordering is good idea: Helps Matrix and general federation use cases A common consistent mutated state for group operations can cause server side DB contention in my experience Forks is one way to remove ordering, and if we need to support it anyways it's a nice benefit. Richard Barnes wrote: >