[Add] Better key rotation, more relays (Sealed Sender, not yet fully)

This commit is contained in:
Alex Emmet 2026-09-05 23:34:44 +02:00
commit d8ef068a48
No known key found for this signature in database
9 changed files with 924 additions and 125 deletions

View file

@ -14,6 +14,26 @@ pub struct PendingRelay {
pub relay_signer_id: Option<i64>,
pub relay_destination_user_id: Option<i64>,
pub relay_message_id: Option<String>,
pub delivery_state: PendingRelayDeliveryState,
pub last_error: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PendingRelayDeliveryState {
Ready,
WaitingClient,
Quarantined,
}
impl PendingRelayDeliveryState {
fn from_db(value: String) -> Result<Self, rusqlite::Error> {
match value.as_str() {
"ready" => Ok(Self::Ready),
"waiting_client" => Ok(Self::WaitingClient),
"quarantined" => Ok(Self::Quarantined),
_ => Err(rusqlite::Error::InvalidQuery),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -31,6 +51,15 @@ pub fn enqueue(
frame_id: u32,
type_map_version: &str,
) -> Result<(), StorageError> {
if let RouteTarget::User(destination) = target {
let destination = i64::try_from(destination)
.map_err(|_| StorageError::Other("relay destination ID exceeds SQLite range".into()))?;
if destination != relay.destination_user_id {
return Err(StorageError::Other(
"user relay target does not match final recipient".into(),
));
}
}
db::with_immediate_transaction(|tx| {
enqueue_in_tx(
tx,
@ -79,7 +108,7 @@ pub fn enqueue_in_tx(
pub fn list(limit: i64) -> Result<Vec<PendingRelay>, StorageError> {
db::with_db(|connection| {
let mut statement = connection.prepare(
"SELECT id, destination_id, target_kind, frame, created_at, frame_id, type_map_version, relay_signer_id, relay_destination_user_id, relay_message_id FROM pending_relays ORDER BY id LIMIT ?1",
"SELECT id, destination_id, target_kind, frame, created_at, frame_id, type_map_version, relay_signer_id, relay_destination_user_id, relay_message_id, delivery_state, last_error FROM pending_relays ORDER BY id LIMIT ?1",
)?;
let rows = statement.query_map(params![limit.clamp(1, 500)], |row| {
let destination_id = row.get::<_, i64>(1)?;
@ -118,12 +147,26 @@ pub fn list(limit: i64) -> Result<Vec<PendingRelay>, StorageError> {
relay_signer_id: row.get(7)?,
relay_destination_user_id: row.get(8)?,
relay_message_id: row.get(9)?,
delivery_state: PendingRelayDeliveryState::from_db(row.get(10)?)?,
last_error: row.get(11)?,
})
})?;
rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
})
}
/* Select only relays whose target and signer remain locally managed. This is
* the delivery-authority check after user release, independent of row state. */
pub fn list_active(limit: i64) -> Result<Vec<PendingRelay>, StorageError> {
db::with_db(|connection| {
let mut statement = connection.prepare(
"SELECT p.id, p.destination_id, p.target_kind, p.frame, p.created_at, p.frame_id, p.type_map_version, p.relay_signer_id, p.relay_destination_user_id, p.relay_message_id, p.delivery_state, p.last_error FROM pending_relays p WHERE p.delivery_state = 'ready' AND ((p.target_kind = 0 AND EXISTS (SELECT 1 FROM users u WHERE u.user_id = p.destination_id)) OR (p.target_kind = 1 AND EXISTS (SELECT 1 FROM users u WHERE u.user_id = p.relay_signer_id))) ORDER BY p.id LIMIT ?1",
)?;
let rows = statement.query_map(params![limit.clamp(1, 500)], pending_relay_from_row)?;
rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
})
}
pub fn list_without_relay_identity() -> Result<Vec<PendingRelay>, StorageError> {
list_without_relay_identity_after(0, i64::MAX)
}
@ -133,7 +176,7 @@ pub fn list_without_relay_identity_after(
limit: i64,
) -> Result<Vec<PendingRelay>, StorageError> {
db::with_db(|connection| {
let mut statement = connection.prepare("SELECT id, destination_id, target_kind, frame, created_at, frame_id, type_map_version, relay_signer_id, relay_destination_user_id, relay_message_id FROM pending_relays WHERE id > ?1 AND (relay_signer_id IS NULL OR relay_destination_user_id IS NULL OR relay_message_id IS NULL) ORDER BY id LIMIT ?2")?;
let mut statement = connection.prepare("SELECT id, destination_id, target_kind, frame, created_at, frame_id, type_map_version, relay_signer_id, relay_destination_user_id, relay_message_id, delivery_state, last_error FROM pending_relays WHERE id > ?1 AND (relay_signer_id IS NULL OR relay_destination_user_id IS NULL OR relay_message_id IS NULL) ORDER BY id LIMIT ?2")?;
let rows = statement.query_map(
params![after_id, limit.clamp(1, 500)],
pending_relay_from_row,
@ -161,11 +204,68 @@ fn pending_relay_from_row(row: &rusqlite::Row<'_>) -> Result<PendingRelay, rusql
relay_signer_id: row.get(7)?,
relay_destination_user_id: row.get(8)?,
relay_message_id: row.get(9)?,
delivery_state: PendingRelayDeliveryState::from_db(row.get(10)?)?,
last_error: row.get(11)?,
})
}
pub fn mark_client_offline(destination_user_id: i64, frame_id: u32) -> Result<(), StorageError> {
db::with_immediate_transaction(|tx| {
tx.execute("UPDATE pending_relays SET delivery_state = 'waiting_client', last_error = 'client_offline' WHERE target_kind = 0 AND destination_id = ?1 AND frame_id = ?2", params![destination_user_id, i64::from(frame_id)])?;
Ok(())
})
}
pub fn pause_client_deliveries(destination_user_id: i64) -> Result<(), StorageError> {
db::with_immediate_transaction(|tx| {
tx.execute(
"UPDATE pending_relays SET delivery_state = 'waiting_client', last_error = 'awaiting_state_ack' WHERE target_kind = 0 AND destination_id = ?1 AND delivery_state = 'ready'",
[destination_user_id],
)?;
Ok(())
})
}
pub fn resume_client_deliveries(destination_user_id: i64) -> Result<usize, StorageError> {
db::with_db(|connection| {
Ok(connection.execute("UPDATE pending_relays SET delivery_state = 'ready', last_error = NULL WHERE target_kind = 0 AND destination_id = ?1 AND delivery_state = 'waiting_client'", [destination_user_id])?)
})
}
pub fn resume_managed_client_deliveries() -> Result<usize, StorageError> {
db::with_db(|connection| {
Ok(connection.execute("UPDATE pending_relays SET delivery_state = 'ready', last_error = NULL WHERE target_kind = 0 AND delivery_state = 'waiting_client' AND EXISTS (SELECT 1 FROM users u WHERE u.user_id = pending_relays.destination_id)", [])?)
})
}
pub fn quarantine_target_mismatch(id: i64) -> Result<(), StorageError> {
db::with_db(|connection| {
connection.execute("UPDATE pending_relays SET delivery_state = 'quarantined', last_error = 'target_recipient_mismatch' WHERE id = ?1", [id])?;
Ok(())
})
}
pub fn set_relay_identity(id: i64, relay: &RelayIdentity) -> Result<(), StorageError> {
db::with_db(|connection| {
let target_kind = connection.query_row(
"SELECT target_kind FROM pending_relays WHERE id = ?1",
[id],
|row| row.get::<_, i64>(0),
)?;
if target_kind == 0 {
let destination_id = connection.query_row(
"SELECT destination_id FROM pending_relays WHERE id = ?1",
[id],
|row| row.get::<_, i64>(0),
)?;
if destination_id != relay.destination_user_id {
connection.execute(
"UPDATE pending_relays SET delivery_state = 'quarantined', last_error = 'target_recipient_mismatch' WHERE id = ?1",
[id],
)?;
return Ok(());
}
}
connection.execute("UPDATE pending_relays SET relay_signer_id = ?2, relay_destination_user_id = ?3, relay_message_id = ?4 WHERE id = ?1", params![id, relay.signer_id, relay.destination_user_id, relay.message_id])?;
Ok(())
})