engine: log out local sessions on a gossiped account suspension
All checks were successful
CI / check (push) Successful in 3m43s

A local SUSPEND logs out the account's active sessions, but a suspension
arriving by gossip only set the flag — a session already identified on a
remote node stayed logged in despite the network-wide suspension. ingest
now signals AccountChange::Suspended, and gossip_ingest ends those sessions
(the account and its channels are kept). Factors the per-session logout into
a shared logout_uid helper.
This commit is contained in:
Jean Chevronnet 2026-07-16 09:13:53 +00:00
parent 58af1187fc
commit 886fe65e4d
No known key found for this signature in database
3 changed files with 84 additions and 10 deletions

View file

@ -888,6 +888,9 @@ impl EventLog {
pub enum AccountChange {
TakenOver(String),
Dropped(String),
// A gossiped suspension that is now in force: local sessions must be logged
// out, but the account (and its channels) stay put.
Suspended(String),
}
// A fingerprint is hex (hash digest), optionally colon-separated. Bound the
@ -1027,7 +1030,13 @@ impl Db {
Event::AccountDropped { account } => Some((account.clone(), self.account(account).map(|c| c.home.clone()))),
_ => None,
};
if let Some(event) = self.log.ingest(entry)? {
let applied = self.log.ingest(entry)?;
// A freshly applied suspension may need to terminate local sessions.
let suspended = applied.as_ref().and_then(|e| match e {
Event::AccountSuspended { account, .. } => Some(account.clone()),
_ => None,
});
if let Some(event) = applied {
apply(&mut self.accounts, &mut self.channels, &mut self.grouped, &mut self.bots, &mut self.host_cfg, &mut self.net, event);
}
if let Some((name, prev_home)) = watched {
@ -1037,6 +1046,12 @@ impl Db {
_ => {}
}
}
if let Some(account) = suspended {
// Only if the suspension is actually in force (not an already-expired one).
if self.is_suspended(&account) {
return Ok(Some(AccountChange::Suspended(account)));
}
}
Ok(None)
}