@unicitylabs/nostr-js-sdk 0.5.0-dev.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9548,6 +9548,25 @@
9548
9548
  * "do not pick this name."
9549
9549
  */
9550
9550
  const PING_SUB_ID = '__nostr-sdk-keepalive__';
9551
+ /**
9552
+ * Filter id used by the keepalive REQ. We need a filter the relay can
9553
+ * resolve immediately (so EOSE comes back fast = relay is alive), but
9554
+ * which can NOT match any real event past EOSE (so the live tail stays
9555
+ * empty).
9556
+ *
9557
+ * Scoping by `authors:[selfPubkey]` was the original approach but it
9558
+ * matched every event the wallet itself published — including kind-31113
9559
+ * token transfers — which the relay then echoed back on the keepalive
9560
+ * sub. Some relays dedupe events across overlapping subs, so the
9561
+ * wallet's own consumer subscription would not receive its own echo and
9562
+ * any flow waiting on that echo would time out.
9563
+ *
9564
+ * The filter `{ ids: ['00...00'] }` asks the relay for a single event
9565
+ * whose id is exactly the all-zero hash. Real Nostr event ids are
9566
+ * SHA-256 over a canonical JSON serialization, so the all-zero hash is
9567
+ * unreachable in practice. Result: instant EOSE, empty live tail.
9568
+ */
9569
+ const KEEPALIVE_NEVER_MATCH_ID = '0'.repeat(64);
9551
9570
  /**
9552
9571
  * Delay before resubscribing after NIP-42 authentication.
9553
9572
  * This gives the relay time to process the AUTH response before we send
@@ -9925,14 +9944,21 @@
9925
9944
  return;
9926
9945
  }
9927
9946
  // Send a subscription request as a ping (relays respond with EOSE).
9928
- // The filter MUST be tightly scoped an open `{ limit: 1 }` filter
9929
- // with no kinds/authors/#p will, after EOSE, stream every event the
9930
- // relay receives (NIP-01 live tail), saturating the connection and
9931
- // exhausting per-connection subscription slots on busy relays.
9932
- // Scoping by `authors:[self]` keeps the live tail empty in practice
9933
- // (the relay would only forward our own future events).
9947
+ // The filter MUST be tightly scoped to something no real event can
9948
+ // match both for the initial query AND the post-EOSE live tail.
9949
+ //
9950
+ // Earlier iterations used `authors:[self]` reasoning that "the
9951
+ // relay would only forward our own future events". That reasoning
9952
+ // was wrong: it precisely DOES forward every event the wallet
9953
+ // publishes, including kind-31113 token transfers. Some relays
9954
+ // dedupe events across overlapping subs, so the wallet's own
9955
+ // consumer subscription would miss its echo and any flow waiting
9956
+ // on it would time out.
9957
+ //
9958
+ // {@link KEEPALIVE_NEVER_MATCH_ID} (the all-zero SHA-256 hash) is
9959
+ // unreachable in real event-id space, so the relay returns EOSE
9960
+ // immediately and the live tail never matches.
9934
9961
  try {
9935
- const selfPubkey = this.keyManager.getPublicKeyHex();
9936
9962
  // First close any existing ping subscription to ensure we don't accumulate
9937
9963
  const closeMessage = JSON.stringify(['CLOSE', PING_SUB_ID]);
9938
9964
  relay.socket.send(closeMessage);
@@ -9940,7 +9966,7 @@
9940
9966
  const pingMessage = JSON.stringify([
9941
9967
  'REQ',
9942
9968
  PING_SUB_ID,
9943
- { authors: [selfPubkey], limit: 1 },
9969
+ { ids: [KEEPALIVE_NEVER_MATCH_ID], limit: 1 },
9944
9970
  ]);
9945
9971
  relay.socket.send(pingMessage);
9946
9972
  relay.unansweredPings++;