Skip to main content

SecurityWarningCode

Enum SecurityWarningCode 

Source
pub enum SecurityWarningCode {
Show 14 variants SenderAddressMismatch, TransportIdentityMismatch, ControlSignatureInvalid, UnsignedControlRejected, MediaSenderGroupMismatch, PlaintextSend, PlaintextReceiveRejected, SessionSenderGroupMismatch, SessionRekeyTriggered, NostrKeyPackageSlotExhausted, PushKeyPackagePoolExhausted, RelayAddressBindingMismatch, RelayAddressDeclarationRefused, GroupLeafIdentityUnproven,
}
Expand description

Machine-readable taxonomy for Event::SecurityWarning, so consumers can branch on a stable code instead of matching the human-readable reason string (which is for logs/UI and may change between versions).

Variants§

§

SenderAddressMismatch

A control message’s signature verified, but the key that signed it does not derive to the address the frame claims to come from.

There is no benign reading. An address is the hash of its identity key, so a legitimate peer’s frames always re-derive to their own address: a rotated key is a different address, i.e. a different peer, not the same peer with new bytes. This is either an impersonation attempt or a peer running a build with a broken derivation.

Replaces TOFU_KEY_MISMATCH, whose remedy (resetTofuForPeer) no longer exists because there is no pin to reset.

§

TransportIdentityMismatch

A control message’s signed sender did not match its transport peer identity.

§

ControlSignatureInvalid

A control message’s signature failed verification (invalid signature or malformed metadata).

§

UnsignedControlRejected

An unsigned control message was rejected.

All security-gated control traffic must be signed by the key its sender address derives from. This is unconditional: it does not depend on require_transport_identity, nor on whether the sender was seen before. A peer that will not sign is making a claim that cannot be checked.

§

MediaSenderGroupMismatch

An encrypted media chunk’s MLS group did not match the 1:1 session group of the claimed wire sender — a valid ciphertext (from some session) delivered under a different sender’s name is a media forgery/misattribution attempt.

§

PlaintextSend

An outbound message left this node as plaintext because encryption is disabled or MLS is uninitialized while require_encryption is false (an explicit opt-out — the default fails closed). Emitted at most once per peer per protocol instance.

§

PlaintextReceiveRejected

Inbound plaintext content (text or legacy media) was rejected by the encryption policy: either require_encryption is enabled, or a confirmed MLS session exists with the claimed sender and plaintext from that peer is a downgrade/forgery attempt (plaintext carries no sender authentication). Emitted at most once per peer; the per-peer tracking set is bounded, so after a flood of forged sender ids a peer may warn again.

§

SessionSenderGroupMismatch

An encrypted 1:1 envelope named an MLS group that is not the session slot shared with the claimed wire sender. The text-path counterpart to Self::MediaSenderGroupMismatch: it catches the same misattribution on paths where MLS cannot, because the envelope failed before it authenticated anything.

§

SessionRekeyTriggered

A 1:1 MLS session was torn down and re-advertised after an epoch desync. Expected occasionally (a genuine fork heals this way), but the triggering frame is not authenticated — see schedule_session_rekey — so a sustained rate of these for one peer indicates injected frames rather than a real fork.

§

NostrKeyPackageSlotExhausted

A Nostr key-package publication slot could not be refilled, so cold first contact over Nostr is degraded or unavailable until it succeeds.

Slots are consumed locally — an init key leaves provider storage only when this node processes a Welcome built against it — so consumption itself is normal traffic, and a stranger can drive it only by actually establishing sessions with us. What this warns about is the refill failing (MLS or storage error), which is the failure the publication design must never absorb silently: a stale published record makes every stranger who fetches it build a Welcome that can never be processed.

§

PushKeyPackagePoolExhausted

The per-peer key-package pool hit its ceiling, so a peer was advertised an init key another peer already holds.

The push path normally gives each peer its own single-use init key. Past the ceiling it reuses one instead of growing the pool without bound — weakening forward secrecy at session establishment (one compromised init key opens every Welcome built against it) and re-introducing the race where the second peer to use a package finds it already consumed. Both clear as packages are consumed or expire.

Reaching it means a device has accumulated unconsumed advertisements: many peers met that never established a session. Sustained emission is the signal that the ceiling, not peer churn, is now the binding constraint.

§

RelayAddressBindingMismatch

The relay acknowledged an address declaration naming an address that is not this node’s.

There is no benign reading. The bridge declares local_address() and signs a proof over a per-connection challenge; the relay verifies that the declared address derives from the key that signed it, and echoes back what it bound. So the echo can only differ if the relay bound something other than what it verified — a broken or hostile relay, or a frame injected into the socket.

Nothing is torn down: a relay that controls the socket already controls everything a local refusal could protect, so the mitigation is that the signal is loud rather than that the connection dies. What it means in practice is that this connection’s frames are attributed to an identity this node cannot prove, so receivers strict-matching the sender against the transport identity will reject its security-gated control traffic.

§

RelayAddressDeclarationRefused

The relay refused this connection’s address declaration, so it stays attributed by account name.

Operational degradation, not an attack signal: the refusal path is deliberately non-fatal on both sides, and the connection keeps working exactly as it did before addresses existed. What is lost is new MLS session establishment over the relay — __MLS_KEY_PKG__ and __MLS_WELCOME__ are security-gated, so a receiver that strict-matches the account name against the off1… sender rejects them, while already established sessions keep working because __MLS_ENC__ is data-plane.

The relay’s own text is not carried on the event. It is remote-chosen, and this event’s reason is shipped verbatim by the telemetry scrubber, so the wording stays bounded in the device log while the code above is the classification. The causes it distinguishes range from unusable key material to this socket having been displaced by a newer login; none of them changes what an app should do. No retry is attempted — the next reconnect declares from scratch.

§

GroupLeafIdentityUnproven

A group leaf carries a credential its own signature key does not derive to — an identity claimed inside a group without the key to prove it.

There is no benign reading, and unlike most codes here it accuses someone the user is already in a room with. An address is the hash of its identity key, so every honest leaf reproduces its own credential; producing one that does not means deliberately building a leaf around someone else’s name.

peer_id is who the finding concerns, not always who to blame. On the three refusal sites it is the peer that delivered the forgery — the Welcome’s inviter, or the sender of the frame carrying the commit — and never the address the forged leaf claimed. That attribution is worth something because it is proved independently: __GRP_MLS_WELCOME__, __GRP_MLS_COMMIT__ and __MLS_WELCOME__ are all security-gated, so the sender signed with the key its own address derives from. On the fourth site there is no delivering peer at all and peer_id is this device’s own id.

reason is diagnostic text, must not be parsed, and deliberately carries no identifier — the impersonated address appears only in this device’s logs. Telemetry scrubbing hashes peer_id and passes reason through verbatim, and the identity at stake belongs to a third party who is not even part of the exchange.

Emitted from four sites. The first three refuse a claim arriving, so nothing is installed and the frame is dropped:

  1. joining a group Welcome whose ratchet tree contains such a leaf (the invite is declined outright);
  2. processing a membership commit that would install one (the commit is not merged, and is never buffered for retry);
  3. joining a 1:1 session Welcome whose ratchet tree contains one — including when we already hold a session with that peer, where the refusal is non-destructive and the existing session stays live (so a secure_session_failed alongside this does not mean the working session ended).

The fourth is different in kind and needs a different response:

  1. a roster read skipping a leaf already seated in local group state. No wire gate can have admitted it, so it arrived by a direct write to this device’s secure store or via a group joined by a build predating those gates. Such a leaf is kept out of every roster and cannot speak — but it holds live group secrets and reads everything sent to the group, which no later refusal undoes. The remedy is to abandon the group, not to evict a member of it.

Apps should treat a group that produces this as untrusted for attribution — a message shown as coming from a member is exactly what the forged leaf was for.

Implementations§

Source§

impl SecurityWarningCode

Source

pub fn as_str(&self) -> &'static str

Returns the stable machine-readable reason code.

Trait Implementations§

Source§

impl Clone for SecurityWarningCode

Source§

fn clone(&self) -> SecurityWarningCode

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for SecurityWarningCode

Source§

impl Debug for SecurityWarningCode

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for SecurityWarningCode

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for SecurityWarningCode

Source§

impl PartialEq for SecurityWarningCode

Source§

fn eq(&self, other: &SecurityWarningCode) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for SecurityWarningCode

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for SecurityWarningCode

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Classify for T

Source§

type Classified = T

Source§

fn classify(self) -> T

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Declassify for T

Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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 more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more