pub struct GroupConfig {
pub max_group_members: usize,
pub relay_enabled: bool,
pub relay_broadcast_enabled: bool,
pub enforce_admin_commits: bool,
}Expand description
Configuration for group messaging.
Fields§
§max_group_members: usizeMaximum number of members allowed in a single group.
Per-member fan-out is O(N) when using mesh transports. This cap prevents unbounded groups from overwhelming the network.
relay_enabled: boolWhether to attempt relay server registration for groups.
When true (default), groups are registered with the relay server
for optimized fan-out when Internet transport is available.
When false, groups always use per-member fan-out regardless of
transport.
This gates registration only — the relay group registry is what
invite links resolve against, so it stays on independently of
Self::relay_broadcast_enabled.
relay_broadcast_enabled: boolWhether a relay-synced group may send via a single O(1) relay broadcast instead of per-member fan-out.
Default true, and additionally gated on the relay’s advertised
capabilities. The flag alone does not select the broadcast: the
path is taken only when the connected relay also advertised the
group_delivery_v3 capability in its Authenticated answer (v3 =
the v2 settled-report contract plus an address-aware group path —
members named in the identifier space the roster was registered in,
so the report is comparable against the MLS roster). Against
such a relay the broadcast keeps a delivery contract — the relay
answers every broadcast with a settled per-recipient delivery
report (GroupMessageSent) naming who took the message over a
socket and who took a device push, and the SDK re-sends a per-member
copy through the ordinary outbox/ACK/park ladder to every MLS roster
member the report does not account for. A lost report re-sends the
broadcast under the same logical message id (receiver and push dedup
absorb the duplicate fan-out) a bounded number of times, then
downgrades the message to full per-member fan-out. Against an older
relay the capability gate fails and every send takes per-member
fan-out — the fire-and-forget broadcast of the v1 relay (no presence
check, no push fallback, no persistence, “sent” answered before
delivery was known) is never taken, regardless of this flag.
The one gap on the broadcast path: the tracker awaiting the report
lives in memory only, so a process death inside the report window
(up to RELAY_BROADCAST_REPORT_TIMEOUT_SECS per attempt) loses the
backstop with it — members the relay could not reach get no
per-member re-send, and nothing retries on restart. It needs both a
missed member and a kill in that window, and is strictly smaller than
the pre-report broadcast’s exposure, but it is a real difference from
per-member fan-out, whose outbox entries persist for 7 days.
Set false to force per-member fan-out even against a v2 relay —
the right choice for an app that must not lose a group message to a
mid-flight process kill.
Per-member fan-out (__GRP_MLS_MSG__ as ordinary SendMessage
frames) inherits the full DM ladder end to end — outbox + ACK +
retry, relay write-ack + successor retry + offline push carrying the
ciphertext, park-on-unreachable with presence-driven flush, and
receiver-side deferred-ACK-after-decrypt — at the cost of O(N)
uplink frames per send.
O(N) fan-out does not risk tripping the relay’s rate limiter at
any group size. The platform bridge meters every relay-bound frame
through a client-side token bucket (RelayRateLimiter: 28 capacity,
9/s refill) deliberately tighter than the relay’s (30 burst, 10/s),
and a frame that cannot take a token is deferred to a later poll
tick, never dropped — so client spend stays at 28 + 9t against a
server budget of 30 + 10t and the fan-out self-paces below it
regardless of member count. Core enqueues all N frames at once; the
bridge drains that queue.
What large groups actually cost on the per-member path is drain
latency. Frame N reaches the wire at roughly (N - 28) / 9 seconds.
Since the ACK timer starts at local enqueue (handle_send_success)
and not at wire-confirm — on_transport_send_confirmed advances only
the welcome lifecycle — past roughly 118 members the tail of a single
fan-out exceeds the 10s ACK timeout and is retransmitted by the
ladder before it was ever written. That is wasted duplicate frames,
absorbed by receiver and push dedup via the stable outbox id, not
lost messages. Presence checks, typing, and receipts draw on the same
bucket and lower the threshold. This is the strongest reason to leave
the broadcast enabled for large groups.
enforce_admin_commits: boolWhether an incoming MLS membership commit is refused when the local admin overlay does not authorize its committer.
Off by default, and turning it on is a decision about partition risk, not a hardening toggle. Refusing a commit means declining the MLS merge: our epoch stays behind every member who did merge, and MLS has no way back — the application has to re-invite us. Enforcement is therefore fork-free only if every member reaches the same verdict, and the admin overlay is replicated best-effort (role changes ride unreconciled mesh notifications; a joiner gets a point-in-time snapshot). Two members whose role maps merely disagree will partition each other with no attacker involved.
The check fails open on every absent input — no metadata, no admin role stored, an unreadable roster — so the common lag case stays harmless. It cannot detect divergent views, which is the residual risk that keeps this opt-in. Enable it only for a closed deployment that controls role distribution.
With it off (the default), unauthorized membership changes are still
applied and reported: group_unauthorized_membership_change fires and
the roster events carry authorized: false. That reporting is
unaffected by this flag; the flag only decides whether the commit is
also refused.
Rejection is receiver-local. The sender’s message is still ACKed, so a committer gets no signal that anyone refused, and members with the flag off will apply a commit that members with it on rejected — do not enable it on part of a fleet.
Trait Implementations§
Source§impl Clone for GroupConfig
impl Clone for GroupConfig
Source§impl Debug for GroupConfig
impl Debug for GroupConfig
Source§impl Default for GroupConfig
impl Default for GroupConfig
Source§impl<'de> Deserialize<'de> for GroupConfig
impl<'de> Deserialize<'de> for GroupConfig
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 GroupConfig
impl RefUnwindSafe for GroupConfig
impl Send for GroupConfig
impl Sync for GroupConfig
impl Unpin for GroupConfig
impl UnsafeUnpin for GroupConfig
impl UnwindSafe for GroupConfig
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);