pub struct EncryptionConfig {
pub enabled: bool,
pub auto_key_exchange: bool,
pub store_pending: bool,
pub require_encryption: bool,
pub pending_queue: PendingQueueConfig,
pub compact_envelope_enabled: bool,
pub rich_payload_enabled: bool,
pub crypto_recovery_enabled: bool,
pub encrypt_service_messages: bool,
}Expand description
Encryption configuration for automatic MLS handling.
Fields§
§enabled: boolWhether automatic encryption is enabled. When enabled, messages are automatically encrypted/decrypted using MLS.
auto_key_exchange: boolAuto-exchange key packages on peer discovery. When enabled, key packages are automatically sent when neighbors are discovered.
store_pending: boolStore pending messages when no session exists. Messages will be sent automatically after the session is established.
require_encryption: boolRequire encryption for messages (default: true).
When enabled, sends fail closed if encryption cannot be applied:
with MLS uninitialized every send returns crate::Error::EncryptFailed,
and with no confirmed session messages are queued (when
store_pending is set) or fail with
crate::Error::SessionNotReady instead of ever leaving as
plaintext. Inbound plaintext content — text messages and legacy
media alike — is rejected without being surfaced to the app
(plaintext carries no sender authentication, so anyone could
inject it under a contact’s name); each rejection emits a
crate::events::SecurityWarningCode::PlaintextReceiveRejected
warning, once per peer. Even under the opt-out, inbound plaintext
from a peer known to run MLS is rejected as a downgrade/forgery
attempt — see OfflineProtocol::encryption_capable_peers. A
confirmed session is not required, and deliberately so: an honest
peer queues rather than downgrading while a session is pending, so
cleartext from a peer we know speaks MLS is never legitimate.
Peers that have shown no MLS signal stay readable, which is what
makes this opt-out usable for legacy interop.
Disable only for deployments that deliberately send cleartext
(e.g. open broadcast/mesh bootstrap without provisioned MLS
storage) — set this to false explicitly, or use
EncryptionConfig::disabled to turn encryption off entirely.
Every plaintext send then emits a
crate::events::SecurityWarningCode::PlaintextSend warning
(once per peer). Internal control messages (key exchange, ACKs,
service discovery) are exempt and unaffected.
pending_queue: PendingQueueConfigBounds and eviction policy for pending inbound encrypted messages.
compact_envelope_enabled: boolWhether to negotiate and emit the compact MLS envelope for encrypted
messages (base64 of the binary EncryptedMessage form instead of JSON
with an integer-array ciphertext — roughly 2.7x smaller).
Defaults to true. It only takes effect toward recipients that
advertise support in their key package (env_versions), so a mixed
fleet automatically stays on the JSON envelope. Parsing of inbound
compact envelopes is always on, independent of this flag; this gates
only advertising and emitting. The end-to-end sibling of
TransportConfig::binary_wire_enabled, with an independent kill
switch because the two degrade separately.
rich_payload_enabled: boolWhether to negotiate and seal the rich __RICH_V1__ payload for
encrypted messages: quoted-reply context, rich media metadata, and
forward attribution carried inside the MLS ciphertext instead of on
the relay-visible outer message.
Defaults to true. It only takes effect toward recipients that
advertise support in their key package (rich_versions); toward
everyone else the rich extras are silently dropped — never sent
cleartext. Parsing of inbound sealed payloads is always on,
independent of this flag; this gates only advertising and sealing.
Independent kill switch from
EncryptionConfig::compact_envelope_enabled — the two degrade
separately.
crypto_recovery_enabled: boolWhether to recover an inbound encrypted message that failed to decrypt, rather than silently dropping it while telling the sender it was delivered.
Defaults to true. When enabled, an inbound encrypted DM (or media
chunk) that fails to decrypt is not delivery-ACKed and its id is not
left dedup-marked, so the sender keeps retrying instead of marking it
delivered — and each resend of a DM is re-sealed against the peer’s
current session, so a resend can actually land. A message that stays
undeliverable settles as an honest MessageFailed once the sender’s
retry budget lapses. When disabled, every decrypt failure falls back to
the legacy drop-and-ACK behavior.
Only an epoch mismatch additionally triggers a re-key. A decrypt
failure specifically because the two sides disagree on the MLS epoch
(e.g. after a fork) also starts a rate-limited session_reset
key-package exchange to rebuild the channel. Failures that are not an
epoch mismatch — an AEAD / authentication failure, a discarded past
ratchet generation, a malformed frame — withhold the ACK but never
re-key: widening the re-key trigger to cover them would turn every
malformed frame into a teardown, which is an unbounded churn vector.
That separation is why the two classifications stay distinct.
One consequence worth planning for: MessageDecryptionFailed is
advisory, not terminal, and fires once per failed attempt rather
than once per message. Treat it as “this attempt did not decrypt”, and
read MessageFailed (or FileReceiveFailed for media) as the terminal
signal.
The re-key trigger is unauthenticated, by construction — do not read
this switch as “only genuine peers can cause a re-key”. An MLS epoch is
checked during framing validation, which happens before any AEAD,
sender-data, or signature check, and a 1:1 session slot id is derivable
from two public user ids. A hand-built frame carrying a wrong epoch
therefore reaches the recoverable classification with its sender still
entirely unverified: any party who can inject a frame can drive a re-key,
with no key material, no captured ciphertext and no session
(test_forged_frame_reaches_session_desync_without_any_key_material
builds one from scratch). This is inherent to MLS framing, not a defect,
and cannot be authenticated away — the credential a sender check would
compare against only exists once decrypt succeeds.
What makes that safe is that acting on the trigger is harmless, not
that it is trusted: the re-key is confined to the claimed sender’s own
session slot (so one derivable id cannot be aimed at other peers), is
rate-limited to one per peer per 30 s, discards no queued message, and
emits a SESSION_REKEY_TRIGGERED security warning so a sustained rate —
injection rather than a real fork — is visible to the app. The residual
is bounded re-key churn on a single pair: delivery delayed, never lost.
OfflineProtocol::schedule_session_rekey carries the full analysis.
encrypt_service_messages: boolWhether service request and response bodies (__SVC_REQ__,
__SVC_RESP__) are sealed inside the recipient’s 1:1 MLS session
instead of crossing every hop as signed plaintext.
Defaults to true. It takes effect only toward a peer that advertised
svc_versions entry 1 in a signed key package (or proved it by sending
a sealed service frame) and only once the session with that peer is
confirmed. Toward everyone else, and before the session confirms, the
body still leaves as signed plaintext, because that form is the floor
every older peer depends on: the first request to a provider this
device has never met is readable by the hops that carry it. Discovery
(__SVC_DISC_Q__ / __SVC_DISC_R__) is never sealed, since every hop
must read it to match and forward.
Gates advertising, recording and emitting. Routing an inbound sealed service frame is unconditional, so switching this off never makes a peer’s sealed request surface as chat text.
Implementations§
Trait Implementations§
Source§impl Clone for EncryptionConfig
impl Clone for EncryptionConfig
Source§impl Debug for EncryptionConfig
impl Debug for EncryptionConfig
Source§impl Default for EncryptionConfig
impl Default for EncryptionConfig
Source§impl<'de> Deserialize<'de> for EncryptionConfig
impl<'de> Deserialize<'de> for EncryptionConfig
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for EncryptionConfig
impl RefUnwindSafe for EncryptionConfig
impl Send for EncryptionConfig
impl Sync for EncryptionConfig
impl Unpin for EncryptionConfig
impl UnsafeUnpin for EncryptionConfig
impl UnwindSafe for EncryptionConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Declassify for T
impl<T> Declassify for T
type Declassified = T
fn declassify(self) -> T
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);