atagen

malice

public
Star0
Watch0

Commit 35a820422568

tarball

atagen <boss@atagen.co>2026-10-24 10:27 UTC

6 files changed+88-19
core: match macltok enums without wildcards

A new macltok variant is now a compile error here, not a silent Upstream or no-op. Spelling out the arms exposed that restore_error sent network, server and rate-limit failures to RestoreError::Stale, which removes the session with Retain::Nothing, so a homeserver blip during restore wiped the login and its crypto store. Those now report Unavailable. RecoveryRejected maps to BadRequest, InteractiveAuthRequired to Forbidden, and VerificationUnsupported and NoCrossSigningIdentity to Conflict, none of which were upstream faults.
Modified file: crates/malice-core/src/error.rs+12 additions−2 deletions
@@ -60,8 +60,18 @@ impl From<macltok::Error> for CoreError {
6060 ErrorKind::Invalid | ErrorKind::EmptyBody => Failure::BadRequest,
6161 ErrorKind::RoomNotOpen
6262 | ErrorKind::TimelineNotOpen
63- | ErrorKind::VerificationInactive => Failure::Conflict,
64- _ => Failure::Upstream,
63+ | ErrorKind::VerificationInactive
64+ | ErrorKind::VerificationUnsupported
65+ | ErrorKind::NoCrossSigningIdentity => Failure::Conflict,
66+ ErrorKind::RecoveryRejected => Failure::BadRequest,
67+ ErrorKind::InteractiveAuthRequired => Failure::Forbidden,
68+ ErrorKind::Unauthenticated
69+ | ErrorKind::Network
70+ | ErrorKind::Server
71+ | ErrorKind::Store
72+ | ErrorKind::Timeline
73+ | ErrorKind::Encryption
74+ | ErrorKind::Other => Failure::Upstream,
6575 };
6676 Self::new(kind, error.message)
6777 }
Modified file: crates/malice-core/src/feed.rs+11 additions−7 deletions
@@ -89,7 +89,7 @@ pub fn snapshot_event(state: &State) -> WireEvent {
8989 TimelineId::Room(room_id) => {
9090 Some((room_id.as_str().to_owned(), timeline.items.clone()))
9191 }
92- _ => None,
92+ TimelineId::Thread { .. } | TimelineId::Pinned(_) => None,
9393 })
9494 .collect();
9595 let pinned = state
@@ -99,7 +99,7 @@ pub fn snapshot_event(state: &State) -> WireEvent {
9999 TimelineId::Pinned(room_id) => {
100100 Some((room_id.as_str().to_owned(), timeline.items.clone()))
101101 }
102- _ => None,
102+ TimelineId::Room(_) | TimelineId::Thread { .. } => None,
103103 })
104104 .collect();
105105 WireEvent::Snapshot {
@@ -146,7 +146,7 @@ pub fn wire_events(change: &StateChange) -> Vec<WireEvent> {
146146 TimelineChange::Closed => vec![WireEvent::TimelineClosed {
147147 room_id: room_id.as_str().to_owned(),
148148 }],
149- _ => vec![],
149+ TimelineChange::Diverged => vec![],
150150 },
151151 StateChange::Timeline {
152152 timeline_id: TimelineId::Pinned(room_id),
@@ -161,7 +161,7 @@ pub fn wire_events(change: &StateChange) -> Vec<WireEvent> {
161161 TimelineChange::Closed => vec![WireEvent::PinnedClosed {
162162 room_id: room_id.as_str().to_owned(),
163163 }],
164- _ => vec![],
164+ TimelineChange::Diverged => vec![],
165165 },
166166 StateChange::Timeline { .. } => vec![],
167167 StateChange::Connection(state) => vec![WireEvent::Connection {
@@ -192,13 +192,18 @@ pub fn wire_events(change: &StateChange) -> Vec<WireEvent> {
192192 UploadOutcome::Cancelled => vec![error("upload cancelled")],
193193 UploadOutcome::RoomForgotten => vec![error("upload cancelled, room forgotten")],
194194 UploadOutcome::Missed => vec![error("upload status unknown")],
195- _ => vec![error("upload did not complete")],
196195 },
197196 StateChange::Upload { upload, .. } => match &upload.failure {
198197 Some(failure) if !failure.will_retry => vec![error("upload failed")],
199198 _ => vec![],
200199 },
201- _ => vec![],
200+ StateChange::Authenticated(_)
201+ | StateChange::Session(_)
202+ | StateChange::SessionSaved
203+ | StateChange::VerificationTarget(_)
204+ | StateChange::NotificationSettings
205+ | StateChange::History { .. }
206+ | StateChange::Typing(_) => vec![],
202207 }
203208 }
204209
@@ -216,7 +221,6 @@ pub fn connection_label(state: &ConnectionState) -> String {
216221 ConnectionState::Online => "online",
217222 ConnectionState::Offline => "offline",
218223 ConnectionState::Stopped => "stopped",
219- _ => "unknown",
220224 }
221225 .to_owned()
222226 }
Modified file: crates/malice-core/src/login.rs+10 additions−2 deletions
@@ -34,8 +34,16 @@ pub fn login_error(error: &AccountError, password: &str) -> CoreError {
3434 let (kind, message) = match error {
3535 AccountError::InvalidHomeserver(_) => (Failure::BadRequest, error.to_string()),
3636 AccountError::AlreadyLoggedIn(_) => (Failure::Conflict, error.to_string()),
37- AccountError::StoreInUse => (Failure::Busy, error.to_string()),
38- error => match error.server() {
37+ AccountError::StoreInUse | AccountError::StoreDeletePending => {
38+ (Failure::Busy, error.to_string())
39+ }
40+ error @ (AccountError::Operation { .. }
41+ | AccountError::MissingSession
42+ | AccountError::UnsupportedAuthentication
43+ | AccountError::OAuthCallbackAlreadySubmitted
44+ | AccountError::NoPendingOAuth
45+ | AccountError::Unsaved { .. }
46+ | AccountError::Engine(_)) => match error.server() {
3947 Some(server) if matches!(server.status, 401 | 403) => {
4048 (Failure::Unauthorized, server.message.clone())
4149 }
Modified file: crates/malice-server/src/state.rs+35 additions−5 deletions
@@ -7,9 +7,9 @@ use std::{
77
88 use futures_util::StreamExt;
99 use macltok::{
10- AccountError, ClientHandle, EncryptedSessionStore, EngineConfig, EngineError, Mirror,
11- MirrorHandle, PasswordLogin, Restore, Retain, Session, SessionStatus, SessionStore, SoftLogout,
12- StateChange, StoreConfig, Termination,
10+ AccountError, ClientHandle, EncryptedSessionStore, EngineConfig, EngineError, ErrorKind,
11+ Mirror, MirrorHandle, PasswordLogin, Restore, Retain, Session, SessionStatus, SessionStore,
12+ SoftLogout, StateChange, StoreConfig, Termination,
1313 };
1414 use rand_core::{OsRng, RngCore};
1515 use sha2::{Digest, Sha256};
@@ -340,7 +340,22 @@ fn restore_error(error: AccountError) -> RestoreError {
340340 AccountError::StoreInUse => {
341341 RestoreError::Unavailable("another connection is using this session".to_owned())
342342 }
343- error => RestoreError::Stale(format!("restoring persisted session failed: {error}")),
343+ AccountError::Operation {
344+ kind: ErrorKind::Network | ErrorKind::Server | ErrorKind::RateLimited,
345+ ..
346+ } => RestoreError::Unavailable(format!("restoring persisted session failed: {error}")),
347+ error @ (AccountError::Operation { .. }
348+ | AccountError::MissingSession
349+ | AccountError::UnsupportedAuthentication
350+ | AccountError::OAuthCallbackAlreadySubmitted
351+ | AccountError::NoPendingOAuth
352+ | AccountError::InvalidHomeserver(_)
353+ | AccountError::AlreadyLoggedIn(_)
354+ | AccountError::StoreDeletePending
355+ | AccountError::Unsaved { .. }
356+ | AccountError::Engine(_)) => {
357+ RestoreError::Stale(format!("restoring persisted session failed: {error}"))
358+ }
344359 }
345360 }
346361
@@ -382,7 +397,22 @@ async fn wait_authenticated(mirror: &MirrorHandle) -> Option<String> {
382397 }
383398 StateChange::Authenticated(user_id) => return Some(user_id.to_string()),
384399 StateChange::Session(status) if status != SessionStatus::Active => return None,
385- _ => {}
400+ StateChange::Session(_)
401+ | StateChange::SessionSaveFailed(_)
402+ | StateChange::SessionSaved
403+ | StateChange::Connection(_)
404+ | StateChange::Encryption(_)
405+ | StateChange::Verification(_)
406+ | StateChange::VerificationTarget(_)
407+ | StateChange::NotificationSettings
408+ | StateChange::Rooms(_)
409+ | StateChange::ImagePacks(_)
410+ | StateChange::Timeline { .. }
411+ | StateChange::History { .. }
412+ | StateChange::Typing(_)
413+ | StateChange::Upload { .. }
414+ | StateChange::UploadFinished { .. }
415+ | StateChange::Failed(_) => {}
386416 }
387417 }
388418 })
Modified file: crates/malice-worker/src/account.rs+16 additions−1 deletions
@@ -98,7 +98,22 @@ async fn wait_authenticated(mirror: &MirrorHandle) -> Option<String> {
9898 }
9999 StateChange::Authenticated(user_id) => return Some(user_id.to_string()),
100100 StateChange::Session(status) if status != SessionStatus::Active => return None,
101- _ => {}
101+ StateChange::Session(_)
102+ | StateChange::SessionSaveFailed(_)
103+ | StateChange::SessionSaved
104+ | StateChange::Connection(_)
105+ | StateChange::Encryption(_)
106+ | StateChange::Verification(_)
107+ | StateChange::VerificationTarget(_)
108+ | StateChange::NotificationSettings
109+ | StateChange::Rooms(_)
110+ | StateChange::ImagePacks(_)
111+ | StateChange::Timeline { .. }
112+ | StateChange::History { .. }
113+ | StateChange::Typing(_)
114+ | StateChange::Upload { .. }
115+ | StateChange::UploadFinished { .. }
116+ | StateChange::Failed(_) => {}
102117 }
103118 }
104119 }
Modified file: crates/malice-worker/src/lib.rs+4 additions−2 deletions
@@ -79,7 +79,9 @@ impl Host {
7979 StoreConfig::indexeddb(store_name(&account), passphrase),
8080 ));
8181 match Session::restore(sessions.clone()).await {
82- Ok(None | Some(Restore::OAuthPending)) => Ok(to_js_value(&serde_json::json!({ "status": "none" }))),
82+ Ok(None | Some(Restore::OAuthPending)) => {
83+ Ok(to_js_value(&serde_json::json!({ "status": "none" })))
84+ }
8385 Ok(Some(Restore::SoftLoggedOut(soft))) => Ok(to_js_value(&serde_json::json!({
8486 "status": "soft_logged_out",
8587 "user_id": soft.user_id.to_string(),
@@ -218,7 +220,7 @@ impl Host {
218220 .map_err(|error| core_error_value(CoreError::from(error)))?;
219221 let token = match &location {
220222 MediaLocation::Local => None,
221- _ => handle.access_token(),
223+ MediaLocation::File(_) | MediaLocation::ServerThumbnail { .. } => handle.access_token(),
222224 };
223225 let location = serde_json::to_value(&location)
224226 .map_err(|error| error_value("upstream", error.to_string()))?;