MLS Web API

Unofficial Proposal Draft,

This version:
https://frosne.github.io/MLS-WebAPI-Standard/
Issue Tracking:
GitHub
Inline In Spec
Editor:
Anna Weine (Mozilla)

Abstract

This specification defines a Web API, exposed only in secure contexts, that enables applications to use Messaging Layer Security (MLS) [RFC9420] for end-to-end encrypted and authenticated group communication. The user agent performs the underlying MLS cryptographic operations and manages MLS client and group state on behalf of the application.

The API provides browser-managed operations for creating MLS identities, credentials, and key packages; creating and joining MLS groups; processing group membership changes; protecting and unprotecting application messages; and exporting MLS-derived secrets.

This specification does not define the MLS protocol wire format, the delivery service used to transport MLS messages, application account identity, contact discovery, user verification, or application-level authorization policy.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is not normative.

Many web applications provide group communication, collaboration, or sharing features in which a set of clients need access to shared confidential state. Examples include team messaging, collaborative workspaces, shared documents, and file sharing systems.

Providing end-to-end protected group communication is difficult. Applications need to handle changing group membership, clients that are not online at the same time, authentication of group members, updates to cryptographic state, and protection of application messages. Implementing these mechanisms directly in application code is complex and can lead to inconsistent security behavior across applications.

The Messaging Layer Security protocol defines an asynchronous group key establishment protocol for groups of clients. MLS is designed to provide efficient group key establishment with forward secrecy and post-compromise security. However, implementing MLS correctly requires careful handling of credentials, key packages, group state, epochs, proposals, commits, Welcome messages, application messages, and exporters.

This specification defines a Web API, exposed only in secure contexts, through which applications delegate MLS state management and cryptographic operations to the user agent. The API handles MLS state and cryptographic operations while leaving message transport, application identity, and application-level group policy to the application.

Applications remain responsible for transporting MLS protocol messages between clients. For example, an application may publish key packages to an application server, send Welcome messages to newly added clients, and deliver commits or application messages through its own messaging infrastructure.

1.1. Goals

1.2. Non-goals

2. Use Cases

This section is not normative.

The following use cases illustrate applications that could use the MLS Web API. They are examples only and do not limit the scope of this specification.

2.1. Encrypted group messaging {#encrypted-group-messaging}

A messaging application allows a set of clients to exchange end-to-end encrypted and authenticated messages.

The application can use the MLS Web API to create MLS identities and credentials, publish key packages through an application-defined service, create or join MLS groups, and protect application messages before delivering them to other clients.

The application remains responsible for message delivery, user accounts, contact discovery, abuse prevention, notifications, and application-level policy. The user agent is responsible for performing the MLS cryptographic operations and maintaining the MLS state exposed through this API.

When the group membership changes, the application can use the API to process the corresponding MLS proposals, commits, and Welcome messages. Future messages can then be protected using the updated group state.

2.2. Collaborative workspaces {#collaborative-workspaces}

A collaborative workspace application allows a group of clients to share confidential workspace state, such as document updates, comments, tasks, or project events.

The application can use an MLS group to represent the set of clients that currently have access to the workspace. Updates to the workspace can be protected as MLS application messages, or the application can use MLS-derived secrets to protect other application-specific data.

The application decides which users or clients are allowed to participate in the workspace. This specification does not define that authorization policy. Once the application has made a membership decision, it can use the MLS Web API to apply the corresponding MLS group operation.

2.3. Encrypted file sharing {#encrypted-sharing}

A file sharing application allows a group of clients to access encrypted files or folders.

The application can use the MLS Web API to establish an MLS group for the clients that should have access to a shared resource. The application can then use MLS application messages or MLS-derived exported secrets to protect file encryption keys, file metadata, or other application-specific secrets.

When a client is removed from the group, the application can use MLS group operations to update the group state so that future secrets are available only to the remaining members. This does not by itself prevent a removed client from accessing plaintext or keys that it already received before removal.

2.4. Shared application secrets {#shared-app-secrets}

Some applications need a group of clients to derive a shared secret for application-specific use.

For example, an application may need to encrypt shared configuration data, protect a room key, or derive a content encryption key for a particular resource. The application can use the MLS Web API to export MLS-derived secrets for these purposes.

Applications using exported secrets are responsible for using them safely and for separating different application purposes with appropriate labels and context values.

3. Security Considerations

This section identifies security considerations for the MLS Web API.

This section is incomplete. Requirements identified here are expected to be moved into normative algorithms and interface definitions in later drafts.

3.1. User Agent Requirements

User agents MUST NOT expose MLS private key material to applications. This API is designed so that applications can perform all required MLS operations without access to underlying private key material.

The API is exposed only in secure contexts. Exposing MLS operations to non-secure contexts would allow network attackers to interfere with application code and could compromise the security properties expected from MLS.

MLS identities, credentials, key packages, and group state are security-sensitive. User agents MUST scope this state to the origin of the context in which it was created, and MUST NOT allow one origin to access MLS state created by another origin.

The user agent is responsible for ensuring that each key package it generates is used for at most one group addition. Once a key package has been consumed, the user agent MUST retire it and MUST NOT allow it to be used again. See [RFC9420] Section 16.8.

This specification does not define user interface requirements for group membership, credential verification, user consent, or trust decisions. If a user agent provides such user interface, it MUST NOT mislead users about what has or has not been verified by the application.

Define the exact origin-scoping model for MLS identities, credentials, key packages, and group state.

Define how invalid MLS messages are reported to applications and whether invalid messages leave group state unchanged.

Define how user agents track, retire, and prevent unsafe reuse of key packages.

Define the credential validation model — what the user agent validates automatically versus what is left to the application, and how applications provide trust anchors for X.509 credentials.

Decide whether this API requires any user interface for group membership, credential verification, consent, or trust decisions.

Define how this API interacts with permissions policy.

3.2. Application Responsibilities

Applications are responsible for authenticating application users and deciding which clients are authorized to participate in a group. An MLS credential does not, by itself, prove that the credential holder represents a particular real-world user or application account.

Applications are also responsible for transporting MLS protocol messages. User agents MUST process MLS messages according to [RFC9420] and reject invalid state transitions. This API does not guarantee message availability or reliable delivery.

Applications should treat MLS protocol messages received from the network as untrusted input. User agents MUST validate protocol messages before applying them to stored MLS state. User agents MUST NOT allow invalid messages to corrupt existing MLS state.

This API does not protect an application from its own script. If an attacker can run script in an application’s origin, for example through cross-site scripting, the attacker may be able to invoke this API with that origin’s authority. Applications should use standard web security mechanisms such as Content Security Policy to reduce the risk of script injection.

3.3. Protocol-Level Considerations

Exported secrets derived via the MLS exporter do not provide forward secrecy. The underlying exporter_secret is not deleted after use, meaning that a compromise of the group state could expose previously derived secrets. Applications that use exported secrets for sensitive purposes should be aware of this limitation. See [RFC9420] Section 8.5.

Applications should be aware of the group fragmentation risks described in [RFC9420] Section 16.12.

Define the exporter API and what constraints apply to labels and context values.

4. Privacy Considerations

This section identifies privacy considerations for the MLS Web API.

This section is incomplete. Requirements identified here are expected to be moved into normative storage, lifetime, and exposure rules in later drafts.

4.1. Persistent State and Lifetime

The MLS Web API creates persistent state associated with an origin, including identities, credentials, key packages, and group state. This state can reveal that a user has used a particular application or participated in particular groups. User agents MUST treat this state as privacy-sensitive and MUST apply the same storage lifetime, site data clearing, and private browsing controls as other origin-scoped sensitive state.

User agents MUST clear MLS state associated with an origin when site data for that origin is cleared. User agents MUST NOT persist MLS state beyond the lifetime of a private browsing or ephemeral browsing session.

Define the privacy model for MLS state associated with an origin.

Define how MLS state lifetime relates to application uninstall, origin changes, site data deletion, browser profile deletion, and account logout.

4.2. Metadata and Observability

MLS protects message contents at the protocol layer, but it does not hide all metadata. Delivery services, application servers, network observers, or other entities may still observe information such as message timing, message sizes, group activity, delivery patterns, network addresses, and server-visible routing information, depending on the application’s transport design. Group membership changes in particular — such as who joined or left a group — can be sensitive and may be visible to the delivery service regardless of MLS protections.

This specification does not guarantee metadata privacy against the application server or delivery service. Applications that require stronger metadata privacy should provide additional mechanisms outside this API.

A client may be added to a group without explicit user awareness if the application does not inform the user. Applications should notify users when they are added to new groups, particularly in contexts where group membership has privacy implications.

The getSupportedCipherSuites() method can reveal differences between user agents or configurations. User agents should avoid exposing unnecessary variation in the supported cipher suite set.

4.3. Fingerprinting

The set of cipher suites and credential types supported by a user agent may vary across implementations and versions. Applications that query or depend on this information could use it as a fingerprinting vector to identify or track users across origins.

Define whether and how supported cipher suites and credential types are exposed to applications, and whether this creates an unacceptable fingerprinting surface.

Enumeration APIs that list identities, credentials, key packages, or groups expose information about prior application activity to script. This creates a potential fingerprinting surface and should be considered carefully when defining which enumeration operations are exposed.

Define what enumeration APIs are exposed to script and whether they should be minimized or restricted to reduce fingerprinting and activity leakage.

Publishing multiple key packages for the same credential allows other parties to correlate a user’s participation across different groups. Applications should limit the number of simultaneously valid key packages they publish where possible.

Define whether the API should limit or discourage publication of large numbers of simultaneously valid key packages.

Exported secrets can allow applications to link MLS group state to other application data. Applications should avoid using exported secrets in ways that create unnecessary cross-context identifiers or long-lived tracking values.

4.4. Cross-Context Isolation

MLS state MUST NOT be accessible to third-party contexts such as cross-origin iframes. User agents should apply storage partitioning rules consistently to MLS state.

Define how MLS state interacts with browser storage partitioning and other privacy mechanisms such as Firefox’s Total Cookie Protection and Safari’s Intelligent Tracking Prevention (ITP).

Define precisely how MLS state interacts with third-party context restrictions and storage partitioning.

Browser extensions operating in an application’s origin may be able to observe MLS API calls or access MLS state. User agents should consider whether MLS state requires additional protection from extension access.

Define whether MLS state requires protection beyond standard origin isolation against browser extension access.

4.5. Synchronization

If a user agent synchronizes MLS state across devices, profiles, or browser installations, it MUST preserve the confidentiality and integrity of that state and MUST provide behavior consistent with user expectations regarding data availability and deletion.

Define whether this specification should explicitly permit or restrict synchronization of MLS state across devices, profiles, or browser installations. If synchronization is permitted, define additional requirements for user visibility and deletion of synchronized state.

Define whether user agents need to provide user-visible controls for inspecting, deleting, or resetting MLS state.

5. API Summary

This section is not normative.

The MLS Web API exposes two main interfaces: MLS and MLSGroupView.

The MLS interface is the client-level entry point. It provides operations for managing client-level MLS state associated with the current origin. Through this interface, an application can generate an MLS client identity, generate an MLS credential, generate a key package, create a new group, retrieve an existing group view, join a group from a Welcome message, and delete stored MLS state.

The MLS interface also provides utility operations for extracting the group identifier and group epoch from MLS protocol messages. These utilities allow an application to route received MLS messages to the appropriate local group state before processing them.

The MLSGroupView interface represents the user agent’s local view of one MLS group for one MLS client. A group view exposes the group identifier and client identifier associated with that local view.

An MLSGroupView provides operations for changing group membership. An application can propose adding or directly add a member from a key package, propose removing or directly remove a member, or close its local participation in the group. These operations produce MLS protocol objects, such as proposals, commits, and Welcome messages, as well as additional protocol objects that new members may need to initialize their group state. The application is responsible for transporting those objects to other clients.

Note: This version of the API does not expose separate operations for checking, clearing, or applying pending proposals or pending commits.

An MLSGroupView also provides operations for inspecting and processing local group state. An application can request group details, receive and process MLS messages, including both handshake messages and application messages.

For application data, an MLSGroupView provides a send operation that accepts application plaintext and returns an MLS-protected application message. The send operation does not transmit the message over the network; applications remain responsible for message delivery.

The MLSGroupView also provides an exporter operation. An application can request an MLS-derived exported secret by providing an exporter label, exporter context, and output length.

MLS protocol objects are represented by typed byte containers. The MLSBytes dictionary contains an object type and byte content. The object type identifies the intended role of the bytes, such as a client identifier, group identifier, credential, key package, proposal, commit output, Welcome message, exporter output, application-message ciphertext, or application-message plaintext.

Additional dictionaries describe structured results returned by the API. MLSGroupDetails describes a group identifier, group epoch, and current members. MLSCommitOutput describes the output of a commit operation, including the commit message, optional Welcome message, and optional group information. MLSExporterOutput describes an exported secret and the label and context used to derive it. MLSReceived describes the result of processing a received MLS message, including application plaintext or commit-related output.

Objects returned by this API are handles to user-agent-managed MLS state or typed representations of MLS protocol data. Unless explicitly specified by this specification, these objects do not expose raw long-term MLS private key material to applications.

Applications remain responsible for transporting MLS protocol messages through application-defined delivery mechanisms. Applications also remain responsible for application-level semantics, including account identity, authorization policy, user verification, contact discovery, abuse prevention, notifications, and user interface behavior.

The exact validation behavior, error behavior, storage lifetime, message processing rules, and state transition rules are defined by later sections of this specification.

5.1. Commit Model

Group membership changes in this API are applied by generating a direct commit. When addMember() or removeMember() is called, the user agent generates a commit that applies the change immediately and advances the group to a new epoch.

The MLS proposal mechanism defined in [RFC9420] Section 12.1 is outside the scope of this specification. All group membership changes are applied via direct commits.

When a commit adds members, the resulting MLSCommitOutput includes Welcome information that must be delivered to the newly added clients. Existing members must receive and process the commit to advance to the new epoch.

Define whether MLSCommitOutput should expose a single Welcome message, a sequence of Welcome messages, or another representation of Welcome information.

5.2. Typical Application Flow

The application calls createIdentity() to create an MLS identity and initial key package. It publishes the keyPackage through an application-defined service so other clients can add it to a group.

To create a new group, the application calls createGroup() with the MLSIdentity. To join an existing group, it calls joinGroup() with the MLSIdentity and a received Welcome message. After joining, the user agent automatically retires the consumed key package state and refreshes the current key package.

The application stores clientId and groupId for later recovery. These values are application-visible lookup values; they are not MLS private key material.

A normal application logout does not by itself delete MLS state, create a new MLS identity, or remove the client from any MLS group. When the user logs in again later, the application can use the stored client identifier and group identifier to recover user-agent-managed MLS state for that origin.

To resume use of an existing group, the application calls getGroup() with the stored group identifier and client identifier. The application is responsible for delivering any MLS messages that arrived while the page, worker, or application session was not active to the recovered MLSGroupView before sending new messages.

If the application needs the identity handle again, for example to publish the current keyPackage, it can call getIdentity() with the stored client identifier.

The application uses MLSGroupView to change membership, process received messages, send protected application messages, and export secrets. Message transport, account identity, and application policy remain the application’s responsibility.

5.2.1. Resuming a Session

If the page, worker, or application session is restarted, the application can recover its local MLS state. A typical flow proceeds as follows:

  1. Retrieve the stored client identifier and group identifier from application storage.

  2. Call getGroup() with the group identifier and client identifier.

  3. If getGroup() returns null, use an application-specific recovery path, such as asking another member to add this client again.

  4. Deliver any missed MLS protocol messages to the returned MLSGroupView.

  5. Use the returned MLSGroupView to send or process application messages.

5.2.2. Account Switching on the Same Origin

A single origin can be used by more than one application account over time. For example, Alice might log in to an application, log out, and then Bob might log in later using the same browser profile and the same origin.

User-agent-managed MLS state is scoped to the relevant origin. Origin scoping does not, by itself, distinguish Alice’s application account from Bob’s application account.

Applications that support account switching on the same origin are responsible for keeping the mapping between application accounts and MLS lookup values separate. The user agent prevents one origin from accessing another origin’s MLS state, but it does not define the application’s account model.

5.3. Private browsing and ephemeral contexts

A user agent MAY choose not to expose the MLS Web API in private browsing or other ephemeral browsing modes.

If a user agent does not expose the MLS Web API in such a mode, any attempt to access the API MUST fail with a "SecurityError" DOMException.

If a user agent exposes the MLS Web API in private browsing or another ephemeral browsing mode, the user agent MUST store MLS state created in that mode in storage that is separate from non-private MLS state.

A user agent MUST NOT allow an MLS operation in a private browsing or ephemeral context to access MLS identity records, key package state, or group state created outside that context.

A user agent MUST NOT allow an MLS operation outside a private browsing or ephemeral context to access MLS identity records, key package state, or group state created inside that context.

MLS state created in a private browsing or ephemeral context MUST be deleted when the corresponding private browsing or ephemeral storage lifetime ends.

Within the lifetime of a private browsing or ephemeral context, a user agent MAY allow getGroup() to recover MLS state created earlier in the same context.

After the private browsing or ephemeral storage lifetime ends, getGroup() MUST NOT recover MLS state created in that context.

5.4. Operations Not Covered

The following MLS protocol operations defined in [RFC9420] are not currently covered by this API:

Define whether and how Update proposals are exposed. Update proposals are defined in [RFC9420] Section 12.1.2 and are important for post-compromise security.

Define whether external joins via GroupInfo and External Commits are supported. External joins are described in [RFC9420] Section 3.3 and specified in [RFC9420] Section 12.4.3.2.

Define whether group reinitialization is supported. Reinitialization is defined in [RFC9420] Section 11.2, and the ReInit proposal is defined in [RFC9420] Section 12.1.5.

This specification does not expose MLS proposals. Applications that require group membership changes use direct commits via addMember() and removeMember(). The proposal mechanism defined in [RFC9420] Section 12.1 is outside the scope of this specification.

6. Concepts and Terminology

This section defines terms used by this specification.

Terms defined by the Messaging Layer Security protocol are used with the meaning given in [RFC9420], unless this specification explicitly defines a Web API-specific meaning.

6.1. MLS client

An MLS client is a protocol participant that holds MLS cryptographic state.

In this specification, an MLS client is represented to applications through user-agent-managed state. This state can include an MLS identity, credentials, key packages, and one or more group views.

6.1.1. MLS identity

An MLS identity is user-agent-managed state representing a logical MLS client for an origin.

An MLS identity is represented to applications by an MLSClientId.

An MLS identity is not itself an MLS credential and does not directly expose long-term private key material to applications.

6.1.2. MLS credential

An MLS credential is credential state associated with an MLS identity.

A credential is used by MLS to authenticate a client within the protocol.

An MLS credential does not, by itself, establish application account identity, user verification, or application-level authorization. Applications are responsible for binding MLS credentials to their own account and authorization systems.

6.1.3. MLS key package

An MLS key package is an MLS protocol object that allows another client to add the corresponding MLS client to a group.

Applications can publish key packages through an application-defined service so that other clients can use them when adding members to groups.

Key packages are security-sensitive protocol objects. This specification needs to define key package generation, exposure, lifetime, retirement, and reuse behavior.

6.2. MLS group

An MLS group is a logical collection of MLS clients that share cryptographic group state.

A group has a group identifier and advances monotonically through a sequence of epochs as valid commits are processed.

6.2.1. MLS epoch

An MLS epoch identifies a particular state of an MLS group.

Processing a valid commit advances a group from one epoch to another.

6.2.2. MLS cipher suite

An MLS cipher suite identifies the cryptographic algorithms used by an MLS group.

An MLS group uses a single cipher suite. The cipher suite determines the cryptographic primitives used for MLS operations, including HPKE, hashing, message authentication, and signatures.

When creating a group, the selected cipher suite needs to be compatible with the capabilities of the clients that are expected to participate in the group.

Define whether applications can select an MLS cipher suite when creating a group, or whether the user agent selects the cipher suite automatically.

6.2.3. MLS group view

An MLS group view is the user agent’s local representation of one MLS client’s view of an MLS group.

In this specification, an MLS group view is represented by an MLSGroupView object.

A group view exposes operations for changing group membership, processing MLS protocol messages, producing MLS-protected application messages, processing received application messages, and exporting MLS-derived secrets.

6.3. MLS commit

An MLS commit is an MLS protocol message that applies one or more group changes and advances the group to a new epoch.

A commit can include a change directly or can apply one or more previously generated proposals.

When a commit adds a new member, additional information, such as a Welcome message, may need to be delivered to the newly added client.

6.4. Welcome message

A Welcome message is an MLS protocol message that allows a newly added client to initialize local group state.

Applications are responsible for delivering Welcome messages to newly added clients.

6.5. MLS application message

An MLS application message is an MLS-protected message carrying application-defined data.

The MLS Web API can protect application plaintext into an MLS application message and can process received MLS application messages to recover application plaintext.

The API does not define the semantics of the application data carried in an MLS application message.

6.6. MLS exporter

An MLS exporter derives application-specific secret material from MLS group state.

Applications provide an exporter label, exporter context, and output length.

Applications are responsible for using exported secrets safely and for using distinct labels and context values for distinct purposes.

6.7. API representation and state

This section defines Web API-specific concepts used to represent MLS data and MLS-related state.

6.7.1. MLS byte object

An MLS byte object is a typed byte container used by this API to carry serialized MLS values or MLS-related byte sequences across the Web API boundary.

In the current API design, MLS byte objects are represented by the MLSBytes dictionary. The type member identifies the intended role of the bytes, and the content member contains the byte sequence.

Examples of MLS byte objects include client identifiers, group identifiers, credentials, key packages, proposals, commits, Welcome messages, application-message ciphertexts, application-message plaintexts, exporter labels, exporter contexts, and exporter outputs.

An MLS byte object does not expose parsed MLS protocol fields to the application. A user agent validates and interprets the byte sequence according to the operation that consumes it.

6.7.2. User-agent-managed MLS state

User-agent-managed MLS state is MLS-related state stored and operated on by the user agent on behalf of an application.

This state can include identities, credentials, key packages, group state, pending proposals, pending commits, and other state needed to perform MLS operations.

User-agent-managed MLS state is expected to be scoped to the relevant origin.

7. Web IDL Definitions

This section defines the Web IDL types and interfaces used by the MLS Web API.

7.1. MLSBytes type

This section defines byte-oriented types used to represent MLS values at the Web API boundary.

The MLSObjectType enum identifies the intended role of a byte sequence.

The MLSBytes dictionary combines an object type with byte content.

enum MLSObjectType {
  "group-epoch",
  "group-identifier",
  "client-identifier",
  "proposal",
  "commit-output",
  "commit-processed",
  "welcome",
  "exporter-output",
  "exporter-label",
  "exporter-context",
  "application-message-ciphertext",
  "application-message-plaintext",
};

enum MLSCredentialType {
  "basic",
  "x509"
};

dictionary MLSCredential {
  required MLSCredentialType type;
  required (USVString or Uint8Array) content;
};

dictionary MLSBytes {
  required MLSObjectType type;
  required Uint8Array content;
};

7.2. MLS result dictionaries

This section defines dictionaries used as structured return values from MLS Web API operations. Each dictionary captures the output of a specific category of operation: group state inspection, commit output, exporter output, and received message processing.

dictionary MLSGroupMember {
  required MLSClientId clientId;
  required Uint8Array credential;
};

dictionary MLSGroupDetails {
  required Uint8Array groupId;
  required Uint8Array groupEpoch;
  required sequence<MLSGroupMember> members;
};

dictionary MLSCommitOutput {
  required MLSObjectType type;
  required Uint8Array groupId;
  required Uint8Array commit;
  Uint8Array welcome;      // present when members were added
  Uint8Array groupInfo;    // present when external joins are supported
  Uint8Array ratchetTree;  // present when full ratchet tree is included
  Uint8Array committerClientId; // client identifier of the committer  
};

dictionary MLSExporterOutput {
  required MLSGroupId groupId;
  required MLSGroupEpoch groupEpoch;
  required Uint8Array label;
  required Uint8Array context;
  required Uint8Array secret;
};

dictionary MLSReceived {
  required MLSObjectType type;
  required Uint8Array groupId;
  Uint8Array groupEpoch;
  Uint8Array content;
  MLSCommitOutput commitOutput;
};

typedef Uint8Array MLSLeaveRequest;

Define whether MLSReceived should be a single overloaded dictionary or split into separate result types per message category.

7.3. MLS Protocol Object Types

This section defines type aliases for MLS protocol objects represented as typed byte containers. These aliases make method signatures more readable and signal the intended role of each byte container at the Web API boundary.

typedef Uint8Array MLSClientId;

typedef Uint8Array MLSKeyPackage;

typedef MLSBytes MLSGroupId;

typedef MLSBytes MLSGroupEpoch;

7.4. MLS Input Union Types

This section defines union types accepted as inputs by MLS Web API operations. These types allow methods to accept both typed MLSBytes containers and raw Uint8Array values, and in some cases plain strings, reducing friction for callers that work directly with byte arrays or string data.

typedef (MLSBytes or Uint8Array) MLSBytesOrUint8Array;

typedef (Uint8Array or USVString) Uint8ArrayOrUSVString;

typedef (MLSBytes or Uint8Array or USVString) MLSBytesOrUint8ArrayOrUSVString;

The current union input types accept multiple representations of the same data for flexibility, but this comes at the cost of type safety and readability at the API boundary. Define whether the final design should simplify input types, for example by accepting only Uint8Array, or by adopting th

7.5. MLSIdentity

An MLSIdentity object represents user-agent-managed MLS identity state for an origin.

[SecureContext, Exposed=(Window, Worker)]
interface MLSIdentity {
  readonly attribute MLSClientId clientId;
  readonly attribute MLSKeyPackage keyPackage;
  readonly attribute MLSCipherSuite cipherSuite;
};

The clientId attribute reflects the opaque client identifier associated with this MLSIdentity.

The keyPackage attribute reflects the current MLS key package associated with this identity.

The cipherSuite attribute reflects the MLS cipher suite used to generate this identity and its associated key package.

7.5.1. MLS Identity Key Package Lifecycle

Each MLSIdentity is associated with a single current MLS key package, reflected by the keyPackage attribute.

The user agent generates the initial key package when createIdentity() is called. A key package MUST NOT be reused for multiple group additions, as defined in [RFC9420] Section 16.8.

When a key package is consumed — for example, when the identity is used to join a group via joinGroup() — the user agent MUST generate a fresh key package and update the keyPackage attribute accordingly.

7.6. MLS Cipher Suite Types

The MLSCipherSuite type represents an MLS cipher suite identifier as defined in [RFC9420] Section 5.1. Cipher suite identifiers are 16-bit unsigned integers assigned by IANA in the MLS Cipher Suites registry defined in [RFC9420] Section 17.1.
typedef unsigned short MLSCipherSuite;

7.7. The MLS interface

The MLS interface is the client-level entry point for the MLS Web API.

It exposes operations for generating identities, credentials, and key packages; creating, retrieving, and joining groups; deleting stored MLS state; and extracting routing information from MLS protocol messages.

[SecureContext, Exposed=(Window, Worker)]
interface MLS {

  Promise<MLSIdentity> createIdentity(
    MLSCredential credentialContent,
    optional MLSCipherSuite cipherSuite
  );

  Promise<MLSGroupView> createGroup(
    MLSIdentity identity);

  Promise<MLSGroupView> joinGroup(
    MLSIdentity identity,
    MLSBytesOrUint8Array welcome);

  Promise<MLSIdentity?> getIdentity(MLSClientId clientId);

  Promise<MLSGroupView?> getGroup (MLSGroupId groupId,
    MLSClientId clientId);

  Promise<MLSGroupId> getGroupIdFromMessage(
    MLSBytesOrUint8Array message);

};

The mls attribute must return the MLS object associated with the current browsing context.

partial interface Navigator {
  [SameObject, SecureContext] readonly attribute MLS mls;
};

7.8. The MLSGroupView interface

The MLSGroupView interface represents the user agent’s local view of one MLS group for one MLS client.

It exposes operations for group membership changes, local group-state inspection, application message protection and processing, pending proposal and commit handling, and exporter secret derivation.

[SecureContext, Exposed=(Window, Worker)]
interface MLSGroupView {
  readonly attribute Uint8Array groupId;

  readonly attribute Uint8Array clientId;

  Promise<MLSGroupDetails> details();

  Promise<MLSCommitOutput> addMember(MLSKeyPackage keyPackage);

  Promise<MLSCommitOutput> removeMember(Uint8Array clientId);

  Promise<MLSCommitOutput> requestLeave();


  Promise<Uint8Array> send(Uint8Array message);

  Promise<MLSReceived> receive(Uint8Array message);

  Promise<MLSExporterOutput> exportSecret(
    Uint8Array label,
    Uint8Array context,
    unsigned long long length);
};

8. Client-Level Interface MLS Functionality

This section defines the behaviour of the client-level MLS interface.

The MLS interface provides operations for creating local MLS client state, generating credentials and key packages, creating and joining groups, retrieving exising group views, and deleting local MLS state.

8.1. Client-level model

The client-level methods of the MLS WebAPI operate on user-agent-managed MLS state associated with an origin. An MLSIdentity object represents a local MLS client identity created by the user agent for an origin. An MLSIdentity object is a handle to user-managed MLS state.

Createing an MLSIdentity creates the NMLS state needed for initial use, including:

  1. opaque client identifier;

  2. signing key material;

  3. an MLS credential created from application-provided credential content;

  4. an initial MLS key package; and

  5. any key package state needed to later process a Welcome message derived from key package.

8.1.1. MLS Client identifiers

An MLS client identifier is an opaque byte sequence generated by the user agent to identify an MLS identity record within user-agent-managed MLS storage for an origin.

Client identifiers generated by the API MUST be opaque.

Client identifiers generated by this API MUST be generated by the user agent.

Client identifiers generated by this API MUST be difficult to guess.

Client identifiers generated by this API MUST be unique among MLS identity records stored for the relevant origin.

Client identifiers generated by this API MUST NOT encode application account identifiers, user names, email addresses, origins, timestamps, or other meaningful application data.

8.1.1.1. Lookup privacy

The getIdentity() and getGroup() methods provide targeted lookup of user-agent-managed MLS state. They do not enumerate all identities or groups stored for an origin.

If no matching identity or group state exists for the relevant origin, these methods resolve with null.

A user agent SHOULD avoid exposing different observable behaviour for "no such identity" and "no such group" through getGroup(), except where this specification explicitly requires an error.

8.1.2. MLS cipher suite support

Each MLS group uses a single MLSCipherSuite, as defined in [RFC9420] Section 5.1.

A conforming user agent MUST support the MLS Web API mandatory cipher suite. The MLS Web API mandatory cipher suite is MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519, whose MLSCipherSuite identifier is 0x0001.

A conforming user agent MAY support additional cipher suites from the IANA MLS Cipher Suites registry defined in [RFC9420] Section 17.1.

The user agent’s supported MLS cipher suite set is the set of MLSCipherSuite values that the user agent is willing to use for MLS operations exposed by this API. The supported MLS cipher suite set MUST include the MLS Web API mandatory cipher suite.

A user agent MUST NOT create or process MLS state using a MLSCipherSuite that is not in the user agent’s supported MLS cipher suite set.

8.1.2.1. Querying Supported Cipher Suites
The getSupportedCipherSuites() static method allows applications to query the user agent’s supported MLS cipher suite set.
partial interface MLS {
  static sequence<MLSCipherSuite> getSupportedCipherSuites();
};

When getSupportedCipherSuites() is called, the user agent MUST run these steps:

  1. Let suites be a new empty sequence.

  2. Append the MLSCipherSuite identifier of the MLS Web API mandatory cipher suite (0x0001) to suites.

  3. For each additional MLSCipherSuite in the user agent’s supported MLS cipher suite set, append its identifier to suites.

  4. Return suites.

8.1.3. Supporting Algorithms

8.1.3.1. Extracting Welcome bytes

To extract Welcome bytes, given welcome, the user agent MUST run these steps:

  1. If welcome is an MLSBytes object, then:

    1. If welcome’s type is not "welcome", return failure.

    2. Return a copy of welcome’s content.

8.1.3.2. Extracting Credential Content Bytes

To extract credential content bytes from an MLSCredential credential, run the following steps:

  1. If credential’s type is not "basic", then:

    1. Reject p with a "NotSupportedError" DOMException

    2. Return p.

  2. Let credentialContent be credential’s content.

  3. If credentialContent is empty, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  4. If credentialContent is a Uint8Array, let credentialBytes be a copy of credentialContent.

  5. If credentialContent is a USVString, let credentialBytes be the UTF-8 encoding of credentialContent.

  6. Return credentialBytes.

Note: This algorithm currently only supports MLS Basic credentials as defined in [RFC9420] Section 5.3.2. Support for additional credential types may be defined in future versions of this specification.

8.1.3.3. Extracting Client Identifier bytes
To extract client identifier bytes, given clientId, the user agent MUST run these steps:
  1. Let bytes be a copy of a bytes help by clientId.

  2. If bytes is empty, return failure.

  3. Return bytes.

8.1.3.4. Extracting Group identifier bytes
To extract group identifier bytes , given groupId, the user agent MUST run these steps:
  1. Let bytes be a coup of a bytes held by groupId.

  2. If bytes is empty, return failure.

  3. Return bytes.

8.1.3.5. Extracting MLS Message Bytes

To extract MLS message bytes from a value message, run the following steps:

  1. If message is a Uint8Array, return the bytes of message.

  2. If message is an MLSBytes, return message’s content.

  3. Return failure.

8.2. MLS identity records

An MLS identity record is user-agent-managed MLS state representing a local MLS client for an origin.

Each MLS identity record has an opaque client identifier.

Each MLS identity record is scoped to an origin.

An MLS identity record contains, or can obtain, signing key material compatible with MLS operations performed by the user agent.

The signing private key material associated with an MLS identity record MUST NOT be exposed to application code.

8.2.1. Resolving an MLS identity record

To resolve an MLS identity record , given an MLSIdentity identity and an enviroment settings object settings, the user agent MUST run these steps:

  1. Let origin be settings’s origin.

  2. If identity’s origin is not origin, return failure.

  3. Let record be identity’s associated MLS identity record.

  4. If record is not associated with any MLS identity record, return failure.

  5. If record’s origin is not origin, return failure.

  6. Return record.

8.3. The createIdentity() method

When the createIdentity method is invoked with credentialContent and optional cipherSuite, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLS object’s relevant settings object.

    1. If settings is not a secure context, then:

      1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  3. Let origin be settings’s origin.

  4. If cipherSuite was not provided, then set cipherSuite to the MLS Web API default cipher suite.

  5. Otherwise:

    1. Let supported be the result of running getSupportedCipherSuites().

    2. If supported does not contain cipherSuite, then:

      1. Reject p with a "NotSupportedError" DOMException.

      2. Return p.

  6. Let credentialContent be the result of running extract credential content bytes on credential.

  7. If result is a DOMException, reject p with result and return p.

  8. Let credentialContent be result.

  9. In parallel, run the following steps:

    1. Let signatureScheme be the signature scheme associated with cipherSuite.

    2. Generate a new opaque client identifier, unique among MLS identity records stored for origin.

    3. Generate a signature key pair compatible with signatureScheme.

    4. Create an MLS Basic credential as defined in [RFC9420] Section 5.3.2, with identity set to credentialBytes and public key set to the generated signature public key.

    5. Generate an MLS key package using the generated client identifier, the generated credential, the generated signing key material, and cipherSuite.

    6. If generating the MLS credential or the MLS key package fails, then:

      1. Delete any partially created identity state.

      2. Reject p with an "OperationError"DOMException.

      3. Abort these steps.

    7. Create a new MLS identity record containing at least:

      1. the generated client identifier;

      2. the generated signing key pair;

      3. the generated credential;

      4. cipherSuite;

      5. signatureScheme;

      6. the generated key package;

      7. any key package state needed to process a Welcome message derived from the generated key package; and

      8. origin.

    8. Store the MLS identity record in user-agent-managed MLS storage scoped to origin.

    9. The user agent MUST NOT make the MLS identity record accessible to any origin other than origin.

    10. If storing the MLS identity record fails, then:

      1. Delete any partially created identity state.

      2. Reject p with an "OperationError" DOMException.

      3. Abort these steps.

    11. Let identity be a new MLSIdentity object associated with the stored MLS identity record.

    12. Resolve p with identity.

  10. Return p.

Creating a new MLS identity with a Basic credential:

const identity = await navigator.mls.createIdentity({
  type: "basic",
  content: "alice@example.com"
});

8.4. The createGroup() method

The createGroup() method creates a local group with the supplied identity as the initial member.

When the createGroup() method is invoked with identity, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLS object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let identityRecord be the result of running resolve an MLS identity record with identity and settings.

  6. If identityRecord is a failure, then:

    1. Reject p with an "InvalidStateError" DOMException.

    2. Return p.

  7. In parallel, run the following steps:

    1. Let cipherSuite be identityRecord cipher suite.

    2. Let credential be identityRecord credential.

    3. Let signingKeyMaterial be identityRecord signing key material.

    4. Let clientId be identityRecord client identifier.

    5. Let supported be the result of running getSupportedCipherSuites().

    6. If supported does not contain cipherSuite, then:

      1. Reject p with a "NotSupportedError" DOMException.

      2. Aborth these steps.

    7. Generate a new opaque MLS group identifier.

    8. The generated group identifier MUST be unique among MLS group states stored for origin.

    9. Create a new MLS group according to [RFC9420] Section 11, using cipherSuite, credential, signingKeyMaterial, clientId, and the generated group identifier.

    10. If creating the MLS group fails, then:

      1. Reject p with an "OperationError" {DOMException}.

      2. Abort these steps.

    11. Let groupState be the MLS group state produced by creating the group.

    12. Let groupEpoch be the epoch associated with groupState.

    13. Create a new MLS group state record containing at least:

      1. the generated group identifier;

      2. groupEpoch;

      3. cipherSuites;

      4. groupState;

      5. clientId;

      6. origin; and

      7. a reference to identityRecord;

    14. Store the new MLS group state record in user-agent-managed MLS storage scoped to origin.

    15. If storing the MLS group state record fails, then:

      1. Delete any partially created group state.

      2. Reject p with an "OperationError" DOMException.

      3. Abort these steps.

    16. The user agent MUST NOT allow any origin other than origin to access the MLS group state record.

    17. Let groupView be a new MLSGroupView object associated with the stored MLS group state record.

    18. Resolve p with groupView.

  8. Return p.

Note: Creating a group does not consume the MLSIdentity object’s current key package.

8.5. The joinGroup() method

When the joinGroup() method is invoked with identity and welcome, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLS object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let identityRecord be the result of running resolve an MLS identity record with identity and settings.

  6. If identityRecord is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. Let welcomeBytes be the result of running extract Welcome bytes with welcome.

  8. If welcomeBytes is failure, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  9. In parallel, run the following steps:

    1. Let cipherSuite be identityRecord’s cipher suite.

    2. Let currentKeyPackage be identityRecord’s current MLS key package.

    3. Let currentKeyPackageState be identityRecord’s current key package private state.

    4. If currentKeyPackageState is null or has been retired, then:

      1. Reject p with an "InvalidStateError" DOMException.

      2. Abort these steps.

    5. Process welcomeBytes as a Welcome message according to [RFC9420] Section 12.4.3, using currentKeyPackage and currentKeyPackageState.

    6. If processing fails, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    7. Let groupState be the MLS group state produced by processing welcomeBytes.

    8. If groupState’s cipher suite is not cipherSuite, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    9. Let groupId be the group identifier associated with groupState.

    10. Let groupEpoch be the epoch associate with groupState.

    11. If an MLS group state record already exists for origin, groupId, and identityRecord’s client identifier, then:

      1. Reject p with an "InvalidStateError" DOMException.

      2. Abort these steps.

    12. Create a new MLS group state record containing at least:

      1. groupId;

      2. groupEpoch;

      3. cipherSuite;

      4. groupState;

      5. identityRecord’s client identifier;

      6. origin; and

      7. a reference to identityRecord.

    13. Generate a fresh MLS key package using identityRecord’s client identifier, credential, signing key material, and cipherSuite.

    14. If generating the fresh MLS key package fails, then:

      1. Delete any partially created group state.

      2. Reject p with an "OperationError" DOMException.

      3. Abort these steps.

    15. Update identityRecord by:

      1. retiring identityRecord’s current MLS key package and current key package private state;

      2. setting identityRecord’s current MLS key package to the fresh MLS key package; and

      3. setting identityRecord’s current key package private state to the private state associated with the fresh MLS key package.

    16. Store the new MLS group state record and the updated identityRecord in user-agent-managed MLS storage scoped to origin as a single atomic update.

    17. The user agent MUST NOT make the MLS group state record or the updated identityRecord accessible to any origin other than origin.

    18. If storing the group state record or updated identityRecord fails, then:

      1. Delete any partially created group state.

      2. Restore identityRecord’s previous current MLS key package and current key package private state, if they were changed.

      3. Reject p with an "OperationError" DOMException.

      4. Abort these steps.

    19. The user agent MUST NOT allow any origin other than origin to access the MLS group state record or the updated identityRecord.

    20. Let groupView be a new MLSGroupView object associated with the stored MLS group state record.

    21. Resolve p with groupView.

  10. Return p.

8.6. The getIdentity method

When the getIdentity() method is invoked with clientId, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLS object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let clientIdBytes be the result of running extract client identifier bytes with clientId.

  6. If clientIdBytes is a failure, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  7. In parallel, run the following steps:

    1. Let identityRecord be the MLS identity record stored in user-agent-managed MLS storage for origin whose client identifier is equal to clientIdBytes.

    2. If no such identityRecdord exists, then:

      1. Resolve p with null.

      2. Abort these steps.

    3. Let identity be a new MLSIdentity object associated with identityRecord.

    4. Resolve p with identity.

  8. Return p.

8.7. The getGroup() method

When the getGroup() method is invoked with groupId and clientId, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLS object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be setting’s origin.

  5. Let groupIdBytes be the result of running extract group identifier bytes with groupId.

  6. If groupBytes is failure, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  7. Let clientIdBytes be the result of running extract client identifier bytes with clientId.

  8. If clientIdBytes is failure, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  9. In parallel, run the following steps:

    1. Let identityRecord be the MLS identity record stored in user-agent-managed MLS storage for origin whose client identifier is equal to clientIdBytes.

    2. If no such identityRecord exists, then:

      1. Resolve p with null.

      2. Abord this steps.

    3. Let groupStateRecord be the MLS group state record stored in user-agent-managed MLS storage for origin whose group identifier is equal to groupIdBytes and whose client identifier is equal to clientIdBytes.

    4. If no suech groupStateRecord exists, then:

      1. Resolve p with null.

      2. Abord this steps.

    5. Let groupView be a new MLSGroupView object associated with groupStateRecord.

    6. Resolve p with groupView.

  10. Return p.

Note: The getGroup() method restores a local group view from user-agent-managed MLS store. It does not fetch or process MLS Messages that were delivered while the application was not running. Applications remain responsible for delivering any missing MLS messages to the returned MLSGroupView.

8.8. The getGroupIdFromMessage() method

When the getGroupIdFromMessage() method is invoked with message, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLS object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let messageBytes be the result of running extract MLS message bytes with message.

  5. If messageBytes is failure, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  6. In parallel, run the following steps:

    1. Parse messageBytes as an MLSMessage as defined in [RFC9420] Section 6 for the purpose of extracting the group identifier.

    2. If messageBytes does not encode a valid MLSMessage from which a group identifier can be extracted, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    3. Let groupIdBytes be the group identifier extracted from messageBytes.

    4. Let result be a new MLSGroupId containing groupIdBytes.

    5. Resolve p with result.

  7. Return p.

Note: The getGroupIdFromMessage() method is a routing helper. It does not process, authenticate, decrypt, or apply the message to MLS group state. A

9. Group-Level Interface MLS Functionality {#group-level-interface}

9.1. Group-level model

An MLSGroupView object represents user-agent-managed MLS group state for one MLS client in one MLS group.

Each MLSGroupView object is associated with an MLS group state record.

An MLS group state record contains at least:

  1. a group identifier;

  2. a group epoch;

  3. an MLS cipher suite;

  4. MLS group state;

  5. the client identifier for the local MLS client;

  6. the origin for which the group state record was created; and

  7. a reference to the MLS identity record associated with the local MLS client.

9.2. The groupId attribute

The groupId attribute returns the group identifier associated with this MLSGroupView.

When the groupId attribute is accessed, the user agent MUST run these steps:

  1. Let record be this MLSGroupView object’s associated MLS group state record.

  2. Return a new Uint8Array containing record’s group identifier.

9.3. The clientId attribute

The clientId attribute returns the group identifier associated with this MLSGroupView.

When the clientId attribute is accessed, the user agent MUST run these steps:

  1. Let record be this MLSGroupView object’s associated MLS group state record.

  2. Return a new Uint8Array containing record’s client identifier.

9.4. Supporting Algorithms

9.4.1. Resolving an MLS group state record

To resolve an MLS group state record, given an MLSGroupView groupView and an environment settings object settings, the user agent MUST run these steps:

  1. Let origin be settings’s origin.

  2. Let record be groupView’s associated MLS group state record.

  3. If record is null, then return failure.

  4. If record’s origin is not origin, then return failure.

  5. Return record.

9.5. The details() method

The details() method returns an application-visible snapshot of the local MLS group state associated with this MLSGroupView. The returned MLSGroupDetails includes the group identifier, the current group epoch, and the members currently represented in the local group state.

When the details() method is invoked, the user agent run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  5. If record is failure, then:

    1. Reject p with an "InvalidStateError" DOMException.

    2. Return p.

  6. In parallel, run the following steps:

    1. Let groupState be record’s MLS group state.

    2. Let members be a new empty sequence.

    3. For each member in the current group membership represented by groupState:

  7. Let details be a new MLSGroupDetails dictionary.

  8. Set details’s groupId to a new Uint8Array containing record’s group identifier.

  9. Set details’s groupEpoch to a new Uint8Array containing the encoded epoch associated with groupState.

  10. Set details’s members to members.

  11. Resolve p with details.

  12. Return p.

9.6. The addMember() method

The addMember() method creates MLS commit output that adds a new member to the group using the supplied key package.

Whhen the addMember() method is invoked with keyPackage, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  6. If record is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. Let keyPackageBytes be a copy of the bytes held by keyPackage.

  8. If keyPackageBytes is empty, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  9. If keyPackageBytes is failure, then

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  10. In parallel, run the following steps:

    1. Let groupState be record’s MLS group state.

    2. Let cipherSuite be record’s cipher suite.

    3. Parse keyPackageBytes as defined in [RFC9420] Section 10.

    4. If parsing fails, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    5. Let parsedKeyPackage be the parsed MLS KeyPackage.

    6. If parsedKeyPackage’s cipher suite is not cipherSuite, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    7. Perform the member addition and Commit operation as defined in [RFC9420] Section 12.4, adding parsedKeyPackage to groupState. If this fails, then:

      1. Reject p with an "OperationError" DOMException.

      2. Abort these steps.

    8. Let commitMessage, welcomeMessage, and updatedGroupState be the Commit message, Welcome message, and updated local group state produced by that operation.

    9. Let updatedGroupEpoch be the epoch associated with updatedGroupState.

    10. Update record by:

      1. Setting record’s MLS group state to updatedGroupState

      2. Setting record’s group epoch to updatedGroupEpoch.

    11. Store the updated record in user-agent-managed MLS storage scoped to origin.

    12. If storing the updated record fails, then:

      1. Restore record’s previous MLS group state and group epoch

      2. Reject p with an "OperationError" DOMException.

      3. Abort these steps.

    13. Let result be a new MLSCommitOutput dictionary.

    14. Set result’s groupId to a new Uint8Array containing record’s group identifier.

    15. Set result’s commit to a new Uint8Array containing commitMessage.

    16. Set result’s welcome to a new Uint8Array containing welcomeMessage.

    17. Set result’s groupInfo to a new Uint8Array containing the group information produced by the operation.

    18. Set result’s ratchetTree to a new Uint8Array containing the ratchet tree produced by the operation.

    19. Resolve p with result.

  11. Return p.

Adding a new member using his key package

const bobIdentity = await bobMls.createIdentity(...);

await publish(bobIdentity.keyPackage);

// Later Alice receives Bob's key package:
await aliceGroup.addMember(bobKeyPackage);

9.7. The removeMember() method

The removeMember() method creates MLS commit that removes a member from the group.

The method is not used for local self-removal. If clientId indentifies the local MLS client for this MLSGroupView, the method rejects.

When the removeMember() method is invoked with clientId, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  6. If record is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. Let targetClientId be the result of running extract client identifier bytes with clientId.

  8. If targetClientId is failure, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  9. If targetCLientId is equal to record’s client identifier, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  10. In parallel, run the folllowing steps:

    1. Let groupState be record’s MLS group state.

    2. If targetClientId does not identify a member of groupState, then:

      1. Reject p with a "NotFoundError" DOMException.

      2. Abort these steps.

    3. Perform the Remove and Commit operation as defined in [RFC9420] Section 12.4, removing the member identified by targetClientId from groupState. If this fails, then:

      1. Reject p with an "OperationError" DOMException.

      2. Abort these steps.

    4. Let commitMessage and updatedGroupState be the Commit message and updated local group state produced by that operation.

    5. Let updatedGroupEpoch be the epoch of updatedGroupState.

    6. Update record by:

      1. Setting record’s MLS group state to updatedGroupState;

      2. Setting record’s group epoch to updatedGroupEpoch.

    7. Store the updated record in user-agent-managed MLS storage scoped to origin.

    8. If storing the updated record fails, then:

      1. Restore record’s previous MLS group state and group epoch

      2. Reject p with an "OperationError" DOMException.

      3. Abort these steps.

    9. Let result be a new MLSCommitOutput dictionary.

    10. Set result’s groupId to a new Uint8Array containing record’s group identifier.

    11. Set result’s commit to a new Uint8Array containing commitMessage.

    12. Set result’s welcome to a new Uint8Array containing welcomeMessage.

    13. Set result’s groupInfo to a new Uint8Array containing the group information produced by the operation.

    14. Set result’s ratchetTree to a new Uint8Array containing the ratchet tree produced by the operation.

    15. Resolve p with result.

  11. Return p.

9.8. The requestLeave() method

The requestLeave() method creates protocol output requesting that the local MLS client be remvoed from the group.

The method does not remove the local client from the MLS group by itself. It does not advnace the local group epoch, delete local MLS state, or notify other group memebers.

The local client is removed from the group only after the group processes a valid commit that removes that client.

When the requestLeave() jethod is invoked, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  6. If record is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. In parallel, run the following steps:

    1. Let groupState be record’s MLS group state.

    2. Let clientId be record’s client identifier.

    3. Create a Remove proposal for clientId as defined in [RFC9420] Section 12.1.3, proposing removal of clientId from groupState.

    4. If this fails, then:

    5. Reject p with an "OperationError" DOMException.

    6. Abort these steps.

    7. Let leaveRequest be the serialised Remove proposal.

    8. Let result be a new Uint8Array containing leaveRequest.

    9. Resolve p with result.

  8. Return p.

Removing a member from the group/p>

const bobClientId = bob.clientId;
const commitOutput = await group.removeMember(removedClientId);

9.9. The send() method

The send() method produces an MLS-protected application message from application plaintext bytes.

When the send() method is invoked with message, the user agent MUST run the following stes:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  6. If record is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. Let plaintext be a copy of the bytes held by message.

  8. In parallel, run the following steps:

    1. Let groupState be record’s MLS group state.

    2. Protect plaintext as an MLS application message using groupState| as defined in [RFC9420] Section 15.1.

    3. If this fails:

      1. Reject p with a "OperationError" DOMException.

      2. Abort these steps.

    4. Let ciphertext and updatedGroupState be the serialised application message and updated group state produced by that operation.

    5. Update record by setting record’s MLS group state to updatedGroupState.

    6. Store the updated record in user-agent-managed MLS storage scoped to origin.

    7. If storing the updated record fails, then:

      1. Restore record’s previous MLS group state, if it was changed.

      2. Reject p with a "OperationError" DOMException.

      3. Abort these steps.

    8. Let result be a new Uint8Array containing ciphertext.

    9. Resolve p with result.

  9. Return p.

Sending a message

async function sendMessageToGroup(group, text) {
  const encoder = new TextEncoder();
  const plaintext = encoder.encode(text);
  const protectedMessage = await group.send(plaintext);

  // The returned value is an MLS-protected application message.
  await deliverMessageToGroup({
    groupId: group.groupId,
    message: protectedMessage,
  });
}

await sendMessageToGroup(group, "hello group");

9.10. The receive() method ##

The receive() method processes an MLS protocol message delivered by the application.

The method can process MLS application messages and MLS handshake messages for the group associated with this MLSGroupView.

If the message is an MLS application message, the method returns the application plaintext.

If the message is a valid MLS handshake message, such as a commit, the user agent updates the local MLS group state and returns a result indicating that the message was processed.

This method does not process Welcome messages. Welcome messages are processed with joinGroup().

When the receive() method is invoked with message, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  6. If record is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. Let messageBytes by a copy of the bytes held my message.

  8. If messageBytes is empty, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  9. In parallel, run the following steps:

    1. Let groupState be record’s MLS group state.

    2. Parse messageBytes as an MLS protocol message as defined in [RFC9420] Section 6.

    3. If parsing fails, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    4. If the parsed message is a Welcome message, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    5. If the parsed message’s group identifier is not byte-for-byte equal to record’s group identifier, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    6. Process the parsed message using groupState as defined in [RFC9420] Section 15. If processing fails, then:

      1. Reject p with a "DataError" DOMException.

      2. Abort these steps.

    7. Let result be a new MLSReceived dictionary.

    8. Set result’s groupId to a new Uint8Array containing record’s group identifier.

    9. If the parsed message is an application message, then:

      1. Let plaintext be the application plaintext produced by decrypting the message.

      2. Set result’s type to "application-message-plaintext".

      3. Set result’s content to a new Uint8Array containing plaintext.

      4. Set result’s groupEpoch to a new Uint8Array containing record’s current group epoch.

    10. Otherwise, if the parsed message is a commit, then:

      1. Let updatedGroupState be the MLS group state produced by processing the commit.

      2. Let updatedGroupEpoch be the epoch of updatedGroupState.

      3. Update record by:

        1. setting record’s MLS group state to updatedGroupState; and

        2. setting record’s group epoch to updatedGroupEpoch.

      4. If processing the commit causes the local client to no longer be a member of the group, mark record as no longer active.

      5. Store the updated record in user-agent-managed MLS storage scoped to origin.

      6. If storing the updated record fails, then:

        1. Restore record’s previous MLS group state and group epoch, if they were changed.

        2. Reject p with an "OperationError" DOMException.

        3. Abort these steps.

      7. Set result’s type to "commit-processed".

      8. Set result’s groupEpoch to a new Uint8Array containing the encoded updatedGroupEpoch.

    11. Resolve p with result.

  10. Return p.

9.11. The exportSecret() method

The exportSecret() method derives application-specific secret material from the MLS group state associated with this MLSGroupView.

The application provides an exporter label, exporter context, and output length. The user agent uses the MLS exporter for the current group state to derive the requested secret.

The method does not expose MLS group secrets, epoch secrets, exporter secrets, or key schedule state. It exposes only the derived exporter output requested by the application.

When the exportSecret() method is invoked with label, context, and length, the user agent MUST run the following steps:

  1. Let p be a new Promise.

  2. Let settings be this MLSGroupView object’s relevant settings object.

  3. If settings is not a secure context, then:

    1. Reject p with a "SecurityError" DOMException.

    2. Return p.

  4. Let origin be settings’s origin.

  5. Let record be the result of running resolve an MLS group state record with this MLSGroupView object and settings.

  6. If record is failure, then:

    1. Reject p with a "InvalidStateError" DOMException.

    2. Return p.

  7. Let labelBytes be a copy of the bytes held by label.

  8. Let contextBytes be a copy of the bytes held by context.

  9. If length is 0, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  10. Let maximumLength be the maximum exporter output length for record’s cipher suite.

  11. If length is greater than maximumLength, then:

    1. Reject p with a "DataError" DOMException.

    2. Return p.

  12. In parallel, run the following steps:

    1. Let groupState be record’s MLS group state.

    2. Let groupEpoch be record’s group epoch.

    3. Derive length bytes of secret material using labelBytes and contextBytes with the MLS exporter for groupState as defined in [RFC9420] Section 8.5.

    4. If exporting secret material fails:

      1. Reject p with a "OperationError" DOMException.

      2. Return p.

    5. Let secret be the derived exporter output.

    6. Let result be a new MLSExporterOutput dictionary.

    7. Set result’s groupId to a new Uint8Array containing record’s group identifier.

    8. Set result’s groupEpoch to a new Uint8Array containing the encoded groupEpoch.

    9. Set result’s label to a new Uint8Array containing labelBytes.

    10. Set result’s context to a new Uint8Array containing contextBytes.

    11. Set result’s secret to a new Uint8Array containing secret.

    12. Resolve p with result.

  13. Return p.

Exporting a key

const label = encoder.encode("com.example.files.content-key.v1");
const context = encoder.encode("folder:7f2a9c");
const exported = await group.exportSecret(label, context, 32);
const contentKeyBytes = exported.secret;

10. Dependencies # {#dependencies} {#dom-groupview-receive}

This specification depends on the following specifications.

[RFC9420]

The Messaging Layer Security protocol defines the MLS protocol and protocol objects used by this API, including clients, credentials, key packages, groups, epochs, proposals, commits, Welcome messages, application messages, and exporters.

[WebIDL]

Web IDL defines the interface definition language used by this specification. It also defines Web IDL interfaces, dictionaries, enumerations, typedefs, promises, typed arrays, exceptions, and JavaScript binding behavior used by this API.

[HTML]

HTML defines the web application execution environment used by this specification, including Window and Worker exposure, origins, realms, relevant global objects, environment settings objects, event loops, and tasks.

[SECURE-CONTEXTS]

Secure Contexts defines secure contexts and the model for restricting powerful APIs to contexts that meet minimum authentication and confidentiality requirements.

[INFRA]

Infra defines common specification data structures and algorithm conventions used by this specification, including lists, ordered maps, sets, byte sequences, and common algorithm notation.

[ENCODING]

Encoding defines UTF-8 encoding and decoding. This dependency is required only if this specification allows string inputs that are converted to byte sequences.

Decide whether the final API accepts string inputs directly. If the final API accepts only byte-oriented inputs such as Uint8Array or MLSBytes, this dependency may not be needed.

[RFC5280]

RFC 5280 defines the X.509 certificate format used by MLS X.509 credentials. This dependency applies only to implementations that support the X.509 credential type.

The following specifications are referenced informatively:

[WebCryptoAPI]

The Web Cryptography API defines the CryptoKey model referenced by this specification when discussing alternative designs for opaque handles to cryptographic state. This specification does not currently depend on Web Cryptography operations.

11. Acknowledgements

The editors would like to thank Benjamin Beurdouche, Martin Thomson, and Christoph Kerschbaumer for their valuable feedback and contributions to this specification, and the Mozilla NSS and PSM teams for their work on the underlying cryptographic infrastructure that informed this design.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC5280]
D. Cooper; et al. Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile. May 2008. Proposed Standard. URL: https://www.rfc-editor.org/info/rfc5280/
[RFC9420]
R. Barnes; et al. The Messaging Layer Security (MLS) Protocol. July 2023. Proposed Standard. URL: https://www.rfc-editor.org/info/rfc9420/
[SECURE-CONTEXTS]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[WEBCRYPTO-2]
Daniel Huigens. Web Cryptography Level 2. URL: https://w3c.github.io/webcrypto/
[WebIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Non-Normative References

[WebCryptoAPI]
Mark Watson. Web Cryptography API. URL: https://w3c.github.io/webcrypto/

IDL Index

enum MLSObjectType {
  "group-epoch",
  "group-identifier",
  "client-identifier",
  "proposal",
  "commit-output",
  "commit-processed",
  "welcome",
  "exporter-output",
  "exporter-label",
  "exporter-context",
  "application-message-ciphertext",
  "application-message-plaintext",
};

enum MLSCredentialType {
  "basic",
  "x509"
};

dictionary MLSCredential {
  required MLSCredentialType type;
  required (USVString or Uint8Array) content;
};

dictionary MLSBytes {
  required MLSObjectType type;
  required Uint8Array content;
};


dictionary MLSGroupMember {
  required MLSClientId clientId;
  required Uint8Array credential;
};

dictionary MLSGroupDetails {
  required Uint8Array groupId;
  required Uint8Array groupEpoch;
  required sequence<MLSGroupMember> members;
};

dictionary MLSCommitOutput {
  required MLSObjectType type;
  required Uint8Array groupId;
  required Uint8Array commit;
  Uint8Array welcome;      // present when members were added
  Uint8Array groupInfo;    // present when external joins are supported
  Uint8Array ratchetTree;  // present when full ratchet tree is included
  Uint8Array committerClientId; // client identifier of the committer  
};

dictionary MLSExporterOutput {
  required MLSGroupId groupId;
  required MLSGroupEpoch groupEpoch;
  required Uint8Array label;
  required Uint8Array context;
  required Uint8Array secret;
};

dictionary MLSReceived {
  required MLSObjectType type;
  required Uint8Array groupId;
  Uint8Array groupEpoch;
  Uint8Array content;
  MLSCommitOutput commitOutput;
};

typedef Uint8Array MLSLeaveRequest;


typedef Uint8Array MLSClientId;

typedef Uint8Array MLSKeyPackage;

typedef MLSBytes MLSGroupId;

typedef MLSBytes MLSGroupEpoch;


typedef (MLSBytes or Uint8Array) MLSBytesOrUint8Array;

typedef (Uint8Array or USVString) Uint8ArrayOrUSVString;

typedef (MLSBytes or Uint8Array or USVString) MLSBytesOrUint8ArrayOrUSVString;

[SecureContext, Exposed=(Window, Worker)]
interface MLSIdentity {
  readonly attribute MLSClientId clientId;
  readonly attribute MLSKeyPackage keyPackage;
  readonly attribute MLSCipherSuite cipherSuite;
};

typedef unsigned short MLSCipherSuite;

[SecureContext, Exposed=(Window, Worker)]
interface MLS {

  Promise<MLSIdentity> createIdentity(
    MLSCredential credentialContent,
    optional MLSCipherSuite cipherSuite
  );

  Promise<MLSGroupView> createGroup(
    MLSIdentity identity);

  Promise<MLSGroupView> joinGroup(
    MLSIdentity identity,
    MLSBytesOrUint8Array welcome);

  Promise<MLSIdentity?> getIdentity(MLSClientId clientId);

  Promise<MLSGroupView?> getGroup (MLSGroupId groupId,
    MLSClientId clientId);

  Promise<MLSGroupId> getGroupIdFromMessage(
    MLSBytesOrUint8Array message);

};

partial interface Navigator {
  [SameObject, SecureContext] readonly attribute MLS mls;
};

[SecureContext, Exposed=(Window, Worker)]
interface MLSGroupView {
  readonly attribute Uint8Array groupId;

  readonly attribute Uint8Array clientId;

  Promise<MLSGroupDetails> details();

  Promise<MLSCommitOutput> addMember(MLSKeyPackage keyPackage);

  Promise<MLSCommitOutput> removeMember(Uint8Array clientId);

  Promise<MLSCommitOutput> requestLeave();


  Promise<Uint8Array> send(Uint8Array message);

  Promise<MLSReceived> receive(Uint8Array message);

  Promise<MLSExporterOutput> exportSecret(
    Uint8Array label,
    Uint8Array context,
    unsigned long long length);
};

partial interface MLS {
  static sequence<MLSCipherSuite> getSupportedCipherSuites();
};

Issues Index

This section is incomplete. Requirements identified here are expected to be moved into normative algorithms and interface definitions in later drafts.
Define the exact origin-scoping model for MLS identities, credentials, key packages, and group state.
Define how invalid MLS messages are reported to applications and whether invalid messages leave group state unchanged.
Define how user agents track, retire, and prevent unsafe reuse of key packages.
Define the credential validation model — what the user agent validates automatically versus what is left to the application, and how applications provide trust anchors for X.509 credentials.
Decide whether this API requires any user interface for group membership, credential verification, consent, or trust decisions.
Define how this API interacts with permissions policy.
Define the exporter API and what constraints apply to labels and context values.
This section is incomplete. Requirements identified here are expected to be moved into normative storage, lifetime, and exposure rules in later drafts.
Define the privacy model for MLS state associated with an origin.
Define how MLS state lifetime relates to application uninstall, origin changes, site data deletion, browser profile deletion, and account logout.
Define whether and how supported cipher suites and credential types are exposed to applications, and whether this creates an unacceptable fingerprinting surface.
Define what enumeration APIs are exposed to script and whether they should be minimized or restricted to reduce fingerprinting and activity leakage.
Define whether the API should limit or discourage publication of large numbers of simultaneously valid key packages.
Define how MLS state interacts with browser storage partitioning and other privacy mechanisms such as Firefox’s Total Cookie Protection and Safari’s Intelligent Tracking Prevention (ITP).
Define precisely how MLS state interacts with third-party context restrictions and storage partitioning.
Define whether MLS state requires protection beyond standard origin isolation against browser extension access.
Define whether this specification should explicitly permit or restrict synchronization of MLS state across devices, profiles, or browser installations. If synchronization is permitted, define additional requirements for user visibility and deletion of synchronized state.
Define whether user agents need to provide user-visible controls for inspecting, deleting, or resetting MLS state.
Define whether MLSCommitOutput should expose a single Welcome message, a sequence of Welcome messages, or another representation of Welcome information.
Define whether and how Update proposals are exposed. Update proposals are defined in [RFC9420] Section 12.1.2 and are important for post-compromise security.
Define whether external joins via GroupInfo and External Commits are supported. External joins are described in [RFC9420] Section 3.3 and specified in [RFC9420] Section 12.4.3.2.
Define whether group reinitialization is supported. Reinitialization is defined in [RFC9420] Section 11.2, and the ReInit proposal is defined in [RFC9420] Section 12.1.5.
Define whether applications can select an MLS cipher suite when creating a group, or whether the user agent selects the cipher suite automatically.
Define whether MLSReceived should be a single overloaded dictionary or split into separate result types per message category.
The current union input types accept multiple representations of the same data for flexibility, but this comes at the cost of type safety and readability at the API boundary. Define whether the final design should simplify input types, for example by accepting only Uint8Array, or by adopting th
Decide whether the final API accepts string inputs directly. If the final API accepts only byte-oriented inputs such as Uint8Array or MLSBytes, this dependency may not be needed.