1use crate::{
2 AuthorizedDevice, DeviceMembership, DevicePubkey, DeviceRoster, DomainError, Error, Invite,
3 InviteResponse, InviteResponseEnvelope, MessageEnvelope, OwnerPubkey, ProtocolContext, Result,
4 RosterSnapshotDecision, Session, SessionState, UnixSeconds, VerifiedAppKeysIndex,
5};
6use rand::{CryptoRng, RngCore};
7use serde::{Deserialize, Serialize};
8use std::collections::{BTreeMap, BTreeSet};
9
10const MAX_INACTIVE_SESSIONS: usize = 10;
11#[derive(Debug, Clone)]
12pub struct SessionManager {
13 local_owner_pubkey: OwnerPubkey,
14 local_device_pubkey: DevicePubkey,
15 local_device_secret_key: [u8; 32],
16 local_invite: Option<Invite>,
17 verified_peer_app_keys: VerifiedAppKeysIndex,
18 users: BTreeMap<OwnerPubkey, UserRecord>,
19}
20
21#[derive(Debug, Clone)]
22struct UserRecord {
23 owner_pubkey: OwnerPubkey,
24 roster: Option<DeviceRoster>,
25 devices: BTreeMap<DevicePubkey, DeviceRecord>,
26}
27
28#[derive(Debug, Clone)]
29struct DeviceRecord {
30 device_pubkey: DevicePubkey,
31 authorized: bool,
32 is_stale: bool,
33 stale_since: Option<UnixSeconds>,
34 claimed_owner_pubkey: Option<OwnerPubkey>,
35 public_invite: Option<Invite>,
36 invite_response_generated: bool,
37 active_session: Option<Session>,
38 inactive_sessions: Vec<Session>,
39 last_activity: Option<UnixSeconds>,
40 created_at: UnixSeconds,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
44pub struct SessionManagerSnapshot {
45 pub local_owner_pubkey: OwnerPubkey,
46 pub local_device_pubkey: DevicePubkey,
47 pub local_invite: Option<Invite>,
48 #[serde(default)]
49 pub verified_peer_app_keys_events: Vec<nostr::Event>,
50 pub users: Vec<UserRecordSnapshot>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
54pub struct UserRecordSnapshot {
55 pub owner_pubkey: OwnerPubkey,
56 pub roster: Option<DeviceRoster>,
57 pub devices: Vec<DeviceRecordSnapshot>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61pub struct DeviceRecordSnapshot {
62 pub device_pubkey: DevicePubkey,
63 pub authorized: bool,
64 pub is_stale: bool,
65 pub stale_since: Option<UnixSeconds>,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub claimed_owner_pubkey: Option<OwnerPubkey>,
68 pub public_invite: Option<Invite>,
69 #[serde(default)]
70 pub invite_response_generated: bool,
71 pub active_session: Option<SessionState>,
72 pub inactive_sessions: Vec<SessionState>,
73 pub last_activity: Option<UnixSeconds>,
74 pub created_at: UnixSeconds,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct PreparedSend {
79 pub recipient_owner: OwnerPubkey,
80 pub payload: Vec<u8>,
81 pub deliveries: Vec<Delivery>,
82 pub invite_responses: Vec<InviteResponseEnvelope>,
83 pub relay_gaps: Vec<RelayGap>,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct Delivery {
88 pub owner_pubkey: OwnerPubkey,
89 pub device_pubkey: DevicePubkey,
90 pub envelope: MessageEnvelope,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct ProcessedInviteResponse {
95 pub owner_pubkey: OwnerPubkey,
96 pub device_pubkey: DevicePubkey,
97 pub claimed_owner_pubkey: Option<OwnerPubkey>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct ReceivedMessage {
102 pub owner_pubkey: OwnerPubkey,
103 pub device_pubkey: DevicePubkey,
104 pub payload: Vec<u8>,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct MessageSenderRecord {
110 pub owner_pubkey: OwnerPubkey,
111 pub device_pubkey: DevicePubkey,
112 pub claimed_owner_pubkey: Option<OwnerPubkey>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
116pub enum RelayGap {
117 MissingRoster {
118 owner_pubkey: OwnerPubkey,
119 },
120 MissingDeviceInvite {
121 owner_pubkey: OwnerPubkey,
122 device_pubkey: DevicePubkey,
123 },
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
127pub struct PruneReport {
128 pub removed_devices: Vec<(OwnerPubkey, DevicePubkey)>,
129 pub removed_users: Vec<OwnerPubkey>,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
133struct TargetDevice {
134 owner_pubkey: OwnerPubkey,
135 device_pubkey: DevicePubkey,
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139enum SendSessionSource {
140 Active,
141 Inactive(usize),
142}
143
144mod lifecycle;
145mod records;
146mod rosters;
147mod sending;
148
149fn apply_roster_snapshot(
150 current_roster: Option<&DeviceRoster>,
151 incoming_roster: &DeviceRoster,
152) -> (RosterSnapshotDecision, DeviceRoster) {
153 let Some(current_roster) = current_roster else {
154 return (RosterSnapshotDecision::Advanced, incoming_roster.clone());
155 };
156
157 if incoming_roster.created_at > current_roster.created_at {
158 return (RosterSnapshotDecision::Advanced, incoming_roster.clone());
159 }
160
161 if incoming_roster.created_at < current_roster.created_at {
162 return (RosterSnapshotDecision::Stale, current_roster.clone());
163 }
164
165 (
166 RosterSnapshotDecision::MergedEqualTimestamp,
167 current_roster.merge(incoming_roster),
168 )
169}
170
171fn session_priority(session: &Session) -> (u8, u32, u32) {
172 let can_send = session.can_send();
173 let can_receive = session.state.receiving_chain_key.is_some()
174 || session.state.their_current_nostr_public_key.is_some()
175 || session.state.receiving_chain_message_number > 0;
176
177 let directionality = match (can_send, can_receive) {
178 (true, true) => 3,
179 (true, false) => 2,
180 (false, true) => 1,
181 (false, false) => 0,
182 };
183
184 (
185 directionality,
186 session.state.receiving_chain_message_number,
187 session.state.sending_chain_message_number,
188 )
189}
190
191fn is_one_way_bootstrap_session(session: &Session) -> bool {
192 session.state.receiving_chain_key.is_none()
193 && session.state.their_current_nostr_public_key.is_none()
194}
195
196fn merge_created_at(current: UnixSeconds, observed: UnixSeconds) -> UnixSeconds {
197 match (current.get(), observed.get()) {
198 (0, _) => observed,
199 (_, 0) => current,
200 _ => current.min(observed),
201 }
202}