v0.3.0
Minor Changes
-
#62
b319e26Thanks @germ-mark! - AppBinding — an optional app-state binding welded into a session at creation and immutable for its lifetime. A TwoMLS session is definitionally bound to its two agents, but agents are mutable (the rotation/candidate lifecycle); the newAppBindingGroupContext extension (0xF0A2, the APQInfo mechanism) binds the session to the app's immutable relationship identity:initiategainsapp_binding: Option<Vec<u8>>(Swift:appBinding: Data?),receive/acceptgainexpected_app_binding— verified on the joined welcome with an exact, symmetric match (a stripped or unequal binding is a wrong-relationship welcome or downgrade attempt; a binding the caller did not state is never silently accepted) before any invitation state is claimed, so a rejected welcome leaves the invitation fully reusable. The acceptor's return group mirrors the verified binding; the initiator requires the return welcome to carry its own binding back unchanged. The binding lives on the classical halves only — a PQ half smuggling one is rejected at every PQ join — and an empty binding is reserved as invalid (rejected at creation and as an expectation;Noneis the unbound state). Newapp_binding()getter reads it back (it rides the persisted group state, so a restored session's owner re-verifies); new errorAppBindingMismatch. GroupContextExtensions proposals remain outside the TwoMLS operation whitelist — now a deliberately tested guarantee — so the binding can never be rewritten.⚠️ Binding contract 14 → 15 — binding and binary MUST pair. Taketwo_mls_pq.swiftfrom this release (TwoMlsPqErrorgained a variant; a stale pairing mis-reads FFI buffers). Key packages and invitations regenerate: leaves now advertise the AppBinding extension type, soCOMBINER_KEY_PACKAGE_VERSION2 → 3 andINVITATION_VERSION3 → 4 (prerelease hard cut — v2 published key-package blobs and v3 invitation archives are rejected outright; re-pair sessions). Session archives are unaffected: the binding is optional, and existing (unbound) sessions restore and keep working.Adopter guidance: pass a digest, not raw identifiers — the first adopter (germDM) binds
H(domain-tag ‖ role-ordered did:did), sharing its canonicalization with the companion CommProtocol relationship-scoped introduction context so the delegation binding and the session binding cannot drift. The crate never interprets the bytes.