Change:
11.2.1. <ins> External Commits are a mechanism for new members (external parties that want to become members of the group) to add themselves to a group, without requiring that an existing member has to come online to issue a Commit that references an Add Proposal. Whether existing members of the group will accept or reject an External Commit follows the same rules that are applied to other handshake messages. New members can create and issue an External Commit if they have access to the following information: group ID current epoch ID ciphersuite public tree hash confirmed transcript hash group extensions Public group key This information is aggregated in "ExternalCommitInfo" as follows: "struct { CipherSuite cipher_suite; CipherSuite cipher_suite; opaque group_id<0..255>; uint64 epoch; opaque tree_hash<0..255>; opaque confirmed_transcript_hash<0..255>; Extension extensions<0..2^32-1>; HPKEPublicKey group_public_key; } ExternalCommitInfo; " Note that the "tree_hash" field is used the same way as in the ratchet-tree-extension. The information above are not deemed public data in general, but applications can choose to make them available to new members in order to allow External Commits. External Commits work like regular Commits, with a few differences: External Commits MUST reference the Add Proposal that adds the issuing new member to the group External Commits MUST be signed by the new member External Commits MUST contain a "path" field (and is therefore a "full" Commit) When processing a Commit, both existing and new members MUST use the external init secret as described in The sender type for the MLSPlaintext encpsulating the External Commit MUST be "new_member" If the Add Proposal is also issued by the new member, its member SenderType MUST be "new_member" 11.2.1.1. The external init secret is calculated using the following steps: In each epoch, existing members use the "external_secret" from the key schedule to derive an HPKE keypair: "group_private_key, group_public_key = KEM.DeriveKeyPair(external_secret)" The group public key is published as part of the "ExternalCommitInfo". The new joiner generates a random "external_seed_secret" of length "KDF.Nh". The new joiner sends the seed to the group by computing: "kem_output, context = SetupBaseS(group_public_key, public_group_context) " where "public_group_context" is the ExternalCommitInfo serialized in TLS syntax. The new joiner sends "kem_output" to the group by encapsulating it in an ExternalInitSecret proposal: "struct { opaque kem_output<0..2^16-1>; } ExternalInitSecret; " Existing members of the group process the ExternalInit proposal and can in turn compute: "context = SetupBaseR(kem_output, group_private_key, public_group_context) " Both sides can now compute: "external_init_secret = ctx.export("external init secret", KDF.Nh) " - Both sides use "external_init_secret" as the iit secret when processing the commit. 11.2.2. External Commits are a mechanism for new members (external parties that want to become members of the group) to add themselves to a group, without requiring that an existing member has to come online to issue a Commit that references an Add Proposal. Whether existing members of the group will accept or reject an External Commit follows the same rules that are applied to other handshake messages. New members can create and issue an External Commit if they have access to the following information: group ID current epoch ID ciphersuite public tree confirmed transcript hash group extensions This information is aggregated in "ExternalCommitInfo" as follows: "struct { CipherSuite cipher_suite; GroupContext group_context; optional<Node> ratchet_tree<1..2^32-1>; } ExternalCommitInfo; " Note that the "ratchet_tree" field is used the same way as in the ratchet-tree-extension. The information above are not deemed public data in general, but applications can choose to make them available to new members in order to allow External Commits. External Commits work like regular Commits, with a few differences: External Commits MUST reference the Add Proposal that adds the issuing new member to the group External Commits MUST be signed by the new member External Commits MUST contain a "path" field (and is therefore a "full" Commit) When processing a Commit, both existing and new members MUST use an empty init secret (all-zero vector of size Hash.length) If the Add Proposal is also issued by the new member, its member SenderType MUST be "new_member" 11.2.3. </ins> The sender of a Commit message is responsible for sending a Welcome message to any new members added via Add proposals.  The Welcome message provides the new members with the current state of the group,

Discussion:
Sofía noted "It is also unclear at some points the notation used for the Diffie-Hellman or Elliptic Curve parameters. Related to this, is unsure if this protocol takes into account the cofactor issues."
What precisely is unclear? X25519 specifies the scalar-clamping that's used to remove the 8-torsion part of the given curve point. And P-256 is a prime curve, so it has no cofactor.
Well, it only says: It does not refer that you are referring to section 'The X25519 and X448 Functions' of the RFC (if so). And actually, I think it is referred to the subsection in that section where is says "To implement the X25519(k, u) and X448(k, u)". It also says: which does not recommend taking into account that "For X25519, in order to decode 32 random bytes as an integer scalar, set the three least significant bits of the first byte and the most significant bit of the last to zero, set the second most significant bit of the last byte to 1 and, finally, decode as little-endian. " (the clamping mechanism). It is a little bit unclear from the text how the generation of the shared secret happens, as it does not point to an specific part of the RFC, nor specify if the encoding/decoding functions are needed. But this is just my read ;) Precisely. But this section is a little bit more defined. Compare: :)
And around unclear: The definition of used to derive the private key, is defined as "The ciphersuite must also specify an algorithm Derive-Key-Pair that maps octet strings with the same length as the output of the hash function to key pairs for the asymmetric encryption scheme.". It is unclear how this algorithm looks in practice. It seems to imply that this function takes any amount of same length octet strings as input (generated as an output of a hash function) and map to create a private key and a public key somehow.It says "Given an octet string X, the private key produced by the Derive-Key-Pair operation is SHA-256(X). (Recall that any 32-octet string is a valid Curve25519 private key.) The corresponding public key is X25519(SHA-256(X), 9)."

If you don't read the RFC, it is unclear that the "9 is the u-coordinate of the base point and is encoded as a byte with value 9, followed by 31 zero bytes". It also does not specify if the private key should have been generated from randomness.When saying "Encryption keys are derived from shared secrets by taking the first 16 bytes of H(Z), where Z is the shared secret and H is SHA-256.", it seems like 'H(input)' could be defined somewhere else previously. Maybe it should be in place to have a definition section for the different functions, like on the RFC7748 in "3. Notation"?When it says "Generate an ephemeral DH key pair (x, x G) in the DH group specified by the ciphersuite in use", does this refer only to the usage of elliptic curves (as it generates the public key as 'xG'? Maybe if it is DH, it should be noted as 'g^x'? This is just some things that were unclear for me when reading :) Perhaps they are discussed somewhere else. It also depends on how self-contained that document wants to be. Hope this helps :)
Ah, ok. These seem like fair points. I had thought that X25519 specifies that clamping be done on every call to , but I see now that it's done once in the decoding step and then never again. Also that P-256 excerpt was very informative. Thanks!