Build on the supported package contract
Use @absolutejs/e2ee-mls through its supported public entry points.
@absolutejs/e2ee-mlsv0.5.0betaMessagingExperimental RFC 9420 MLS messaging provider for @absolutejs/e2ee.
bun add @absolutejs/e2ee-mlsExperimental RFC 9420 messaging provider for @absolutejs/e2ee, backed by the pure TypeScript ts-mls@2.0.0-rc.16 engine.
This package exercises the complete AbsoluteJS messaging boundary with real MLS messages: per-device credentials, KeyPackages, Welcome messages, encrypted application data, membership commits, self-updates, and sealed group state. It uses the mandatory-to-implement MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 ciphersuite.
This provider is not production-approved. ts-mls has not received a formal security audit, and this adapter has not received an independent review. Read SECURITY.md.
The provider requires an AuthenticationService; it does not accept anonymous or self-asserted device credentials. The service issues credentials bound to the generated MLS signature public key and validates that same binding when a remote KeyPackage or membership change is processed.
KeyPackage directory, delivery, durable state, and recovery services remain the application's responsibility through the contracts in @absolutejs/e2ee. Strict E2EE clients must keep sealed state on participant-controlled devices. stateProtection is required and must encrypt and authenticate exported MLS state using a device-controlled key. A managed recovery authority may wrap that already-sealed result separately.
Remote add, remove, and other sensitive membership proposals fail closed unless authorizeMembershipChange explicitly approves them. Applications should bind that callback to conversation roles and the current verified device roster.
Create every additional device's KeyPackage, call addMembers(), deliver its returned Welcome exactly once, and deliver the returned handshake to existing members. Authenticated message context must use the local device ID as senderId and the session's current epoch.
Changing between strict-e2ee and managed-recovery requires a new conversation. Never relabel or wrap an existing strict conversation in place. For state-loss recovery, use a request-bound recovery grant and replaceMembers() to add the fresh device and remove the lost leaves in one epoch. Do not restore an old serialized MLS snapshot as a second live client.
Outcomes
Use @absolutejs/e2ee-mls through its supported public entry points.
Hardening checklist
Follow in order
Working example for Usage.
import { createMlsMessagingProvider } from "@absolutejs/e2ee-mls";
const provider = await createMlsMessagingProvider({
authenticationService,
stateProtection,
});
const alice = await provider.createDeviceCredential({
deviceId: "alice-phone",
identityId: "alice",
});
const conversation = await provider.createConversation({
conversationId: crypto.randomUUID(),
creatorCredential: alice,
securityMode: "strict-e2ee",
});Supported entry points declared by this package manifest.
Package entry point declared in package.json.
Scripts declared by this package manifest.
Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.