The packed 1216-byte hybrid public had no consumer that wanted it
whole -- everything unpacked immediately. Message layouts and the
credential record now carry peer_m | peer_x as explicit fields; _PUB
and the unpack-at-use idiom are gone. credential_id values change with
the record layout. Step comments in the exchange methods explain what
each solve/challenge proves.
Each exchange party ships a single x25519 ephemeral public (e_alice in
msg1, e_bob in msg2) and every x25519 leg is a plain DH against those
shipped keys; ML-KEM legs ship as bare ciphertexts. Encapsulation is
split into explicit ML-KEM + DH steps at the call sites, key objects
are kept as members (statics derived once from the persisted idroot,
ephemerals generated directly), and all fixed-width wire layouts are
full-message structs. Also: role-suffixed key naming throughout, dead
struct/layout cleanup.