fix(serverless): build ChannelInfo locally on create/open_dm (fixes 'nothing happened')

The serverless create_channel and open_dm re-queried the relay for the
kind:39000 they had just published, to build ChannelInfo. On a public relay
that immediate round-trip races against propagation/indexing lag and can
return empty, throwing 'metadata not yet available' — surfacing as a private
channel / DM that silently fails to appear in the UI.

Both now derive ChannelInfo directly from the locally-signed metadata event
(we created it, so is_member = true), with no relay round-trip and no race.
This commit is contained in:
Michael Neale
2026-06-02 13:37:12 +10:00
parent a568920cf9
commit 6f5f6df71c
2 changed files with 22 additions and 7 deletions

View File

@@ -363,10 +363,12 @@ pub async fn create_channel(
if state.is_serverless() {
// No relay to process a kind:9007 command — publish the kind:39000
// metadata and a kind:39002 membership (self) directly so the channel
// is discoverable via get_channels.
let my_pubkey = {
// is discoverable via get_channels. Build ChannelInfo from the locally
// signed metadata event rather than re-querying the relay, which can
// race against propagation/indexing lag (the "nothing happened" bug).
let (my_pubkey, keys) = {
let keys = state.keys.lock().map_err(|e| e.to_string())?;
keys.public_key().to_hex()
(keys.public_key().to_hex(), keys.clone())
};
let meta = events::build_channel_metadata_serverless(
&channel_uuid_string,
@@ -376,9 +378,16 @@ pub async fn create_channel(
description.as_deref(),
&[],
)?;
let meta_event = meta
.clone()
.sign_with_keys(&keys)
.map_err(|e| format!("failed to sign channel metadata: {e}"))?;
submit_event(meta, &state).await?;
let members = events::build_channel_members_serverless(&channel_uuid_string, &[my_pubkey])?;
submit_event(members, &state).await?;
// Return ChannelInfo derived from the event we just published (member,
// since we created it). No relay round-trip required.
return nostr_convert::channel_info_from_event(&meta_event, None, Some(true));
} else {
let builder = events::build_create_channel(
channel_uuid,

View File

@@ -36,10 +36,12 @@ pub async fn open_dm(
let channel_id = if state.is_serverless() {
// No relay to assign a channel id. Derive a deterministic id from the
// sorted participant set (including self) so both sides converge on the
// same DM channel, then publish 39000 + 39002 directly.
let my_pubkey = {
// same DM channel, then publish 39000 + 39002 directly. Return
// ChannelInfo from the locally signed event rather than re-querying the
// relay (which races against propagation lag).
let (my_pubkey, keys) = {
let keys = state.keys.lock().map_err(|e| e.to_string())?;
keys.public_key().to_hex()
(keys.public_key().to_hex(), keys.clone())
};
let mut participants: Vec<String> = pubkeys
.iter()
@@ -59,11 +61,15 @@ pub async fn open_dm(
None,
&participants,
)?;
let meta_event = meta
.clone()
.sign_with_keys(&keys)
.map_err(|e| format!("failed to sign DM metadata: {e}"))?;
submit_event(meta, &state).await?;
let members = events::build_channel_members_serverless(&channel_id, &participants)?;
submit_event(members, &state).await?;
channel_id
return nostr_convert::channel_info_from_event(&meta_event, None, Some(true));
} else {
// Submit a kind:41010 dm-open event; the relay replies with the channel
// id in its OK message payload.