pub struct SessionManager<K: NostrKeypair> { /* private fields */ }Expand description
Routes double-ratchet sessions across many peers and devices.
Generic over the in-process keypair K exactly like Session, so it
works with the production K256Keypair and any test signer alike.
§Inbound routing index
A naive router trial-decrypts an inbound event against every held session
— O(sessions) Schnorr verifies per event, which a stranger can weaponise.
Instead we keep sender_index: a map from each peer DH key a session will
accept (see Session::accepted_senders) to that session’s
(peer, device). Because DH ephemeral keys are globally unique, the map is
an exact index — a message’s sender routes to at most one session in
O(log n), and a foreign sender misses instantly. The index is refreshed
from a session’s accepted set whenever it is installed or advances on
receive.
Implementations§
Source§impl<K: NostrKeypair> SessionManager<K>
impl<K: NostrKeypair> SessionManager<K>
Sourcepub fn new(identity: K) -> Self
pub fn new(identity: K) -> Self
Create a manager owning our long-term identity keypair (used to
authenticate invite handshakes).
Sourcepub fn our_pubkey(&self) -> &str
pub fn our_pubkey(&self) -> &str
Our identity public key (x-only hex).
Sourcepub fn has_session(&self, peer: &str) -> bool
pub fn has_session(&self, peer: &str) -> bool
Whether we currently hold at least one session with peer.
Sourcepub fn peers(&self) -> impl Iterator<Item = &String>
pub fn peers(&self) -> impl Iterator<Item = &String>
The peers (owner pubkeys) we hold sessions with, sorted.
Sourcepub fn devices(&self, peer: &str) -> Vec<String>
pub fn devices(&self, peer: &str) -> Vec<String>
The device ids we hold sessions with for peer, sorted.
Sourcepub fn session_count(&self) -> usize
pub fn session_count(&self) -> usize
Total number of sessions across all peers and devices.
Sourcepub fn install_session(
&mut self,
peer: &str,
device_id: &str,
session: Session<K>,
)
pub fn install_session( &mut self, peer: &str, device_id: &str, session: Session<K>, )
Install (or replace) a session for (peer, device_id) directly — for
restoring persisted state or wiring an externally-bootstrapped session.
Sourcepub fn sessions(&self) -> impl Iterator<Item = ((&str, &str), &SessionState)>
pub fn sessions(&self) -> impl Iterator<Item = ((&str, &str), &SessionState)>
Enumerate every held session as ((peer, device_id), &SessionState),
in sorted (peer, device) order — the inverse of [install_session].
Exposed for persistence: a caller can snapshot each session’s
[SessionState] (all-public, Session::from_state-restorable) and
later replay them through [install_session] to revive the manager.
Sourcepub fn accept_invite(
&mut self,
invite: &Invite,
owner_pubkey: Option<&str>,
created_at: i64,
) -> Result<NostrNote, Nip104Error>
pub fn accept_invite( &mut self, invite: &Invite, owner_pubkey: Option<&str>, created_at: i64, ) -> Result<NostrNote, Nip104Error>
Invitee side. Accept invite, install the resulting initiator
session (keyed under the inviter), and return the signed
invite-response event to publish back.
owner_pubkey is our optional multi-device owner key.
§Errors
Propagates Invite::accept crypto/signing failures.
Sourcepub fn receive_invite_response(
&mut self,
invite: &Invite,
event: &NostrNote,
) -> Result<String, Nip104Error>
pub fn receive_invite_response( &mut self, invite: &Invite, event: &NostrNote, ) -> Result<String, Nip104Error>
Inviter side. Consume an invite-response event, install the mirror
responder session, and return the peer (owner) pubkey we now have a
session with.
The session is keyed under the invitee’s owner pubkey when supplied (so all of a multi-device peer’s devices group together), else under their identity; the device id is always the invitee’s identity pubkey.
§Errors
Propagates Invite::receive failures (bad signature, wrong kind,
missing ephemeral secret, crypto).
Sourcepub fn process_event(&mut self, event: &NostrNote) -> Option<ReceivedMessage>
pub fn process_event(&mut self, event: &NostrNote) -> Option<ReceivedMessage>
Route an inbound kind-MESSAGE_EVENT_KIND event to the session that
accepts its sender, commit that session’s ratchet advance, and return
the decrypted message.
Routing is O(log n) via the sender_index: the
event’s pubkey (the sender’s current DH key) is looked up directly,
so a foreign or forged event misses without touching any ratchet.
Returns None if the event is not a message event, names a sender we
don’t hold, or fails to decrypt (replay/tamper — the codec verifies the
signature).
Sourcepub fn send(
&mut self,
peer: &str,
payload: &[u8],
created_at: i64,
) -> Result<Vec<NostrNote>, Nip104Error>
pub fn send( &mut self, peer: &str, payload: &[u8], created_at: i64, ) -> Result<Vec<NostrNote>, Nip104Error>
Fan out: encrypt payload to every device session held for peer,
committing each ratchet advance, and return one signed kind-1060 event
per device ready to publish.
Devices that cannot send yet (their first inbound message hasn’t arrived) are skipped. The returned order follows sorted device id.
§Errors
Nip104Error::UnknownPeer if we hold no sessions for peer; plus any
per-session send failure.
Trait Implementations§
Source§impl<K: Clone + NostrKeypair> Clone for SessionManager<K>
impl<K: Clone + NostrKeypair> Clone for SessionManager<K>
Source§fn clone(&self) -> SessionManager<K>
fn clone(&self) -> SessionManager<K>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more