Coordinates the DAVE (Discord Audio Video E2EE) protocol.
This is a pure struct (not a GenServer) to avoid bottlenecks on the audio hot path (50 frames/sec). The NIF resource is thread-safe (Mutex on the Rust side).
When protocol_version is 0, all operations are passthrough (zero-cost).
Summary
Functions
Returns true if DAVE E2EE is active (version > 0 and NIF session available).
The protocol version currently in effect, including transitions executed after this copy of the manager was taken.
Records a frame that failed to decrypt, and recovers once too many fail in a row.
Decrypts a DAVE-encrypted frame.
Encrypts an Opus frame through DAVE E2EE.
Handles an MLS-related voice gateway event.
Builds an OP26 key package payload from the current MLS session.
Creates a new DAVE manager. Version 0 means passthrough (no E2EE).
Returns true when the MLS session is ready to encrypt media.
Returns true when after executed a transition that before had not, leaving DAVE in effect.
Types
@type t() :: %EDA.Voice.Dave.Manager{ channel_id: non_neg_integer(), decrypt_failures: non_neg_integer(), downgraded: boolean(), last_transition_id: non_neg_integer() | nil, mls_session: reference() | nil, pending_transitions: %{required(non_neg_integer()) => non_neg_integer()}, protocol_version: non_neg_integer(), reinitializing: boolean(), transitions: non_neg_integer(), user_id: non_neg_integer(), version_ref: :atomics.atomics_ref() | nil }
The DAVE state of one voice connection.
protocol_version is the version in effect. The playback process works on a copy of this struct
taken when the connection became ready, so the version is also held in version_ref, an
:atomics shared by every copy: a transition executed by the session reaches playback already
under way. transitions counts executed transitions, decrypt_failures the frames that failed
to decrypt in a row.
Functions
Returns true if DAVE E2EE is active (version > 0 and NIF session available).
@spec current_version(t()) :: non_neg_integer()
The protocol version currently in effect, including transitions executed after this copy of the manager was taken.
Records a frame that failed to decrypt, and recovers once too many fail in a row.
A session that stops decrypting — the group state out of step with the other members — would otherwise stay broken until the bot leaves the channel. After 36 failures in a row, the last transition is reported as invalid (OP 31) and the session is re-initialised with a fresh key package, so Discord removes the bot from the group and adds it again. Failures while that is under way, or while a transition is pending, are expected and not counted.
Returns the updated manager and the payloads to send to the voice gateway.
Decrypts a DAVE-encrypted frame.
In passthrough mode, returns the frame unchanged.
Returns {:ok, decrypted_frame, updated_manager} or {:error, reason}. On an error, pass the
manager to decrypt_failed/1.
Encrypts an Opus frame through DAVE E2EE.
In passthrough mode, returns the frame unchanged.
Returns {:ok, encrypted_frame, updated_manager} on success.
Returns {:error, reason, updated_manager} when an active DAVE session
cannot encrypt media, so callers can stop playback instead of sending
undecryptable raw Opus.
@spec handle_mls_event(t(), non_neg_integer(), map()) :: {t(), list()}
Handles an MLS-related voice gateway event.
Returns {updated_manager, reply_payloads} where reply_payloads is
a list of payloads to send back to the voice gateway.
Builds an OP26 key package payload from the current MLS session.
Returns {:ok, payload} when available, otherwise :error.
@spec new(non_neg_integer(), non_neg_integer(), non_neg_integer()) :: t()
Creates a new DAVE manager. Version 0 means passthrough (no E2EE).
Returns true when the MLS session is ready to encrypt media.
Returns true when after executed a transition that before had not, leaving DAVE in effect.
This is when VOICE_DAVE_READY is dispatched: a transition to protocol 0 is not one.