From 3697d528a5af5eee3f720536760c70018477c0c9 Mon Sep 17 00:00:00 2001 From: Tom Brow Date: Tue, 27 Oct 2026 09:55:59 -0700 Subject: [PATCH] fix(mobile): publish agent registrations over wss and clean up safely Relay publication and removal now derive the WebSocket URL through RelayConfig, so communities saved with an https relay URL work. Finished and discarded setups remove their pending record before deleting its instructions, so an interrupted cleanup cannot strand the account. Co-authored-by: george <973be878edd6d4f79692a3827f20a6f7fe0ed0362f269ff6b6ba8867033d7d8e@sprout-oss.stage.blox.sqprod.co> Co-Authored-By: Claude Opus 5.5 Signed-off-by: Tom Brow --- mobile/lib/shared/agents/remote_agents.dart | 29 ++++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/mobile/lib/shared/agents/remote_agents.dart b/mobile/lib/shared/agents/remote_agents.dart index c5ed1b8576..6743a6aa13 100644 --- a/mobile/lib/shared/agents/remote_agents.dart +++ b/mobile/lib/shared/agents/remote_agents.dart @@ -388,12 +388,16 @@ class RemoteAgentController extends ChangeNotifier { // Retain the completed registration if the listing refresh fails, so Retry // resumes the same identity instead of creating another agent. await refresh(); + // Drop the record before its instructions, so an interrupted cleanup never + // leaves a pending setup whose instructions are gone. final prefs = await SharedPreferences.getInstance(); + if (!await prefs.remove('$scope:pending')) { + throw StateError('Could not clear the finished setup. Retry.'); + } + pending = null; await accountSecureStorage.delete( key: '$scope:instructions:${setup['request']}', ); - await prefs.remove('$scope:pending'); - pending = null; status = '${setup['name']} is ready. Mention it in a channel.'; }); @@ -407,14 +411,14 @@ class RemoteAgentController extends ChangeNotifier { throw StateError('This setup already has an agent. Resume it.'); } final scope = _storageScope; - await accountSecureStorage.delete( - key: '$scope:instructions:${setup['request']}', - ); final prefs = await SharedPreferences.getInstance(); if (!await prefs.remove('$scope:pending')) { throw StateError('Could not discard unfinished setup.'); } pending = null; + await accountSecureStorage.delete( + key: '$scope:instructions:${setup['request']}', + ); status = ''; }); @@ -444,11 +448,11 @@ class RemoteAgentController extends ChangeNotifier { throw StateError('Agent deletion is pending. Retry Delete.'); } await keys.withKeys((key) async { + // Saved communities may store an https relay URL; sockets need wss. + final config = RelayConfig(baseUrl: relay, nsec: key.nsec); await submitSignedEventOnce( - wsUrl: relay, - headers: await relayAuth.headers( - RelayConfig(baseUrl: relay, nsec: key.nsec), - ), + wsUrl: config.wsUrl, + headers: await relayAuth.headers(config), nsec: key.nsec, kind: 30177, content: jsonEncode({ @@ -467,11 +471,10 @@ class RemoteAgentController extends ChangeNotifier { /// Removes only this owner's registration in [relay]. Future removePolicy(String relay, String pubkey) => keys.withKeys((key) async { + final config = RelayConfig(baseUrl: relay, nsec: key.nsec); await submitSignedEventOnce( - wsUrl: relay, - headers: await relayAuth.headers( - RelayConfig(baseUrl: relay, nsec: key.nsec), - ), + wsUrl: config.wsUrl, + headers: await relayAuth.headers(config), nsec: key.nsec, kind: 5, content: '',