0.0.10 — cipher-suite binding, last-resort key packages, total session archive (first release off main)
Integration build for downstream (AbstractTwoMLS, germDM feat/PQ) — built straight from main at 030197e (all PRs merged; #41, #42/#37, #43, #44 and the provider-agnostic landing #36). Unlike 0.0.8/0.0.9 (cut from throwaway integration branches), this is the first release off main now that every open PR has landed.
Built with Apple CryptoKit backing both halves (--features cryptokit), Xcode 27 beta 3 toolchain.
⚠️ FFI surface changed — binding and binary MUST pair
Take the attached two_mls_pq.swift from this release; a 0.0.9-era binding against this binary fails fast. AbstractTwoMLS's bindingContractVersion() assertion at first client/invitation construction catches a stale pairing immediately, and uniffi's own load-time checksum rejects a mismatched binding outright.
Binding contract 3 → 5 (two increments since 0.0.9):
- v4 (#41) —
TwoMlsPqErrorgainedCipherSuiteMismatch(a peer key package/welcome carries a suite pair that doesn't match the session's fixed suite);MlsCipherSuite.is_supportedrenamed tois_combiner_pq(the name always meant "is the PQ combiner half", not "is a supported suite");AgentStaterenamed toPrincipalState(part of #44). - v5 (#43) —
TwoMlsPqErrorgainedInvitationSpent(a single-use invitation whose key package was already consumed rejects every furtherreceive, from any remote — distinct from the per-remoteDuplicateWelcome);generate_invitationgained alast_resortflag (that signature change is also caught by uniffi's load-time checksum).
⚠️ Persisted state is not portable across pre-release builds
Archive layouts changed again (total/self-contained session archive, retained key packages folded in). Per the pre-release policy the archive version bytes are intentionally not bumped (still pinned to 1), so old archives are not auto-detected — regenerate all persisted sessions and invitations from this release rather than loading 0.0.9-era archives.
What's new since 0.0.9
- Cipher-suite binding, hardened (#41): the classical+PQ cipher-suite pair is now a fixed, validated property of a session — established once at construction and enforced on every peer key package and welcome. A wrong-suite peer is rejected with
CipherSuiteMismatch, distinct fromPqNotAvailable(peer offers no PQ half) andUnsupportedCipherSuite(a local provider gap). The suite classifier now covers all RFC 9420 suites and thePqNotAvailablediagnostic is corrected. - Last-resort key packages + invitation key-package lifecycle (#43): invitations are explicitly single-use or last-resort (
generate_invitation(last_resort:)). A single-use invitation's key package is consumed on the first accepted session and every laterreceivefails withInvitationSpent; a last-resort invitation never spends. Session key packages self-prune on use (mls-rs delete + store honors it); invitation key packages are erased explicitly. The session archive now carries the client's retained key packages so restore is self-contained. - Total, self-contained session archive/restore (#37, #42): archive/restore is parameterless and total — no external state threaded in at restore, id-based
stage_rotation, retained key packages included. - Principal terminology (#44): the Identity/Agent concept is renamed to Principal, CommProtocol-agnostic (
AgentState→PrincipalState; a terminology bridge is documented on the client). - Session rendezvous (#38) and a rotation listen-window regression test (#39, guarding the 0.0.9 pruning fix).
- Provider-agnostic core fully landed on
main(#36) — both halves run on Apple CryptoKit in this binary (awslcremains the portable/Linux-CI configuration); plus doc-staleness fixes across README/book/benches (#40).
Carried over from 0.0.9: crypto-provider-agnostic core (both halves on CryptoKit), session persistence groundwork, A.5 PQ rekey, invitation forward table (spawn-token routing), raw-digest FFI convention, honest epoch reporting, bindingContractVersion() canary.
Integration
.binaryTarget(
name: "TwoMLSPQrs",
url: "https://github.com/germ-network/TwoMLSPQ/releases/download/0.0.10/TwoMLSPQ.xcframework.zip",
checksum: "f83f43d1d35afadcfc9adbadae290b6c83209c9c53e6680fcaa4d243108a3acf"
)Copy the attached two_mls_pq.swift into your wrapper target from this same release, and bump the consumer's expectedBindingContract to 5.
🤖 Generated with Claude Code