Readonlychannelhex(SHA-256(sharedSecret ‖ salt)) — identical on both sides.
Readonlyepoch0 after establish; grows on rotate.
ReadonlyroleDirection role: affects nothing cryptographically (X3DH is symmetric), but useful for application-side audit. Set on establish, preserved through rotation.
A long-lived E2EE channel derived from an X3DH shared secret.
Key representations coexist: the raw root material (32B) lives in module-private internals (WeakMap) for message-key derivation and rotation; a non-extractable CryptoKey is derived from it for AEAD operations. The raw material is wiped only at rotation or destroyChannel.
Sequence counters are managed by the APPLICATION (immutable design — React-friendly).
The channel's internal state (rootKeyMaterial, replay window) is stored in a module-private WeakMap keyed by the Channel object reference. The application never sees or touches internals.
Channel objects are FROZEN (D2): all fields are readonly and the object is sealed with Object.freeze at creation — mutation attempts throw in strict mode. Functions accepting a Channel validate the runtime brand (D1) and reject impostor objects with INVALID_ARGUMENT.
Example