marmot-ts (the TypeScript Marmot client — browsers, Node, Bun, Deno)
wraps a completely different MLS implementation: ts-mls. That backend
shares no code with OpenMLS, which is what MDK/whitenoise use, so
Amethyst passing on both is a strong signal that the on-wire MLS layer
is spec-correct rather than coincidentally self-consistent.
Add a Node-based generator under quartz/tools/tsmls-vector-gen/ that
uses ts-mls 2.0.0-rc.10 directly to emit:
- Alice's KeyPackage + Bob's joiner KeyPackage
- Alice's Welcome after add_member + commit
- Bob's init/encryption/signature private keys (PKCS#8 Ed25519 unwrapped
to the raw 32-byte seed for parity with the MDK vector shape)
- Post-join MLS-Exporter("marmot","group-event",32) KAT
- Three application PrivateMessages from Alice → Bob with the
expected plaintexts
TsMlsWelcomeInteropTest mirrors MdkWelcomeInteropTest but consumes the
new vector. It proves:
- KeyPackage self-signature verifies under our Ed25519 + SignContent.
- Amethyst.processWelcome unwraps the group secrets, derives the
welcome_key/nonce, AEAD-decrypts GroupInfo, verifies GroupInfoTBS
with signer_pub, decodes the ratchet tree, and binds the joiner's
leaf — end-to-end.
- Post-join exporter secret matches ts-mls byte-for-byte.
- Amethyst decrypt() parses the RFC 9420 §6.3.1 PrivateMessageContent
framing (application_data<V> + FramedContentAuthData.signature<V> +
zero padding) and verifies the sender's FramedContentTBS signature
against Alice's leaf, for all three ciphertexts.
https://claude.ai/code/session_01HfHdd5S5rvxUW2ihEpLGJr
tsmls-vector-gen
Node helper that emits MLS interop test vectors from ts-mls, the
TypeScript MLS implementation that powers
marmot-ts (the Marmot
protocol client that runs in the browser / Node / Bun / Deno).
Produces quartz/src/commonTest/resources/mls/tsmls-welcome.json, which
TsMlsWelcomeInteropTest consumes to prove Amethyst can parse and
decrypt a Welcome + application messages authored by the TypeScript
side. Together with mdk-vector-gen (OpenMLS/Rust) the Marmot suite now
has cross-implementation KATs from two independent MLS backends.
Regenerating
cd quartz/tools/tsmls-vector-gen
npm install --legacy-peer-deps
node generate.mjs > ../../src/commonTest/resources/mls/tsmls-welcome.json
The generator uses fresh randomness each run; commit the regenerated
JSON if you change the generator. ts-mls returns Ed25519 signature
private keys as PKCS#8-DER envelopes (16-byte header + 32-byte seed);
we strip the header before emitting so the vector's signature_priv
field is directly comparable to the one mdk-vector-gen produces.