@unicitylabs/nostr-js-sdk 0.3.0 → 0.3.2

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.
package/README.md CHANGED
@@ -5,6 +5,7 @@ A TypeScript SDK for Nostr protocol with Unicity extensions. Works in both Node.
5
5
  ## Features
6
6
 
7
7
  - **NIP-17 Private Messages** - Gift-wrapped private direct messages with sender anonymity
8
+ - **NIP-42 Client Authentication** - Automatic relay authentication for protected relays
8
9
  - **NIP-44 Encryption** - Modern ChaCha20-Poly1305 AEAD encryption with HKDF
9
10
  - **BIP-340 Schnorr Signatures** - Full support for secp256k1 Schnorr signatures
10
11
  - **NIP-04 Encryption** - Legacy AES-256-CBC encryption with ECDH key agreement
@@ -470,6 +471,7 @@ const parsed = await PaymentRequestProtocol.parsePaymentRequest(event, keyManage
470
471
  | 14 | CHAT_MESSAGE | Private direct message rumor (NIP-17) |
471
472
  | 15 | READ_RECEIPT | Read receipt rumor (NIP-17) |
472
473
  | 1059 | GIFT_WRAP | Gift-wrapped message (NIP-17) |
474
+ | 22242 | AUTH | Client authentication to relay (NIP-42) |
473
475
  | 30078 | APP_DATA | Application-specific data (nametag bindings) |
474
476
  | 31111 | AGENT_PROFILE | Agent profile information |
475
477
  | 31112 | AGENT_LOCATION | Agent GPS location |
@@ -5373,6 +5373,7 @@ class Filter {
5373
5373
  '#p';
5374
5374
  '#t';
5375
5375
  '#d';
5376
+ '#h';
5376
5377
  since;
5377
5378
  until;
5378
5379
  limit;
@@ -5396,6 +5397,8 @@ class Filter {
5396
5397
  this['#t'] = [...data['#t']];
5397
5398
  if (data['#d'])
5398
5399
  this['#d'] = [...data['#d']];
5400
+ if (data['#h'])
5401
+ this['#h'] = [...data['#h']];
5399
5402
  if (data.since !== undefined)
5400
5403
  this.since = data.since;
5401
5404
  if (data.until !== undefined)
@@ -5432,6 +5435,8 @@ class Filter {
5432
5435
  result['#t'] = this['#t'];
5433
5436
  if (this['#d'] && this['#d'].length > 0)
5434
5437
  result['#d'] = this['#d'];
5438
+ if (this['#h'] && this['#h'].length > 0)
5439
+ result['#h'] = this['#h'];
5435
5440
  if (this.since !== undefined)
5436
5441
  result.since = this.since;
5437
5442
  if (this.until !== undefined)
@@ -5518,6 +5523,15 @@ class FilterBuilder {
5518
5523
  }
5519
5524
  return this;
5520
5525
  }
5526
+ hTags(hTagsOrFirst, ...rest) {
5527
+ if (Array.isArray(hTagsOrFirst)) {
5528
+ this.data['#h'] = [...hTagsOrFirst];
5529
+ }
5530
+ else {
5531
+ this.data['#h'] = [hTagsOrFirst, ...rest];
5532
+ }
5533
+ return this;
5534
+ }
5521
5535
  /**
5522
5536
  * Set minimum timestamp (inclusive).
5523
5537
  * @param since Unix timestamp in seconds
@@ -5585,6 +5599,8 @@ const REACTION = 7;
5585
5599
  const GIFT_WRAP = 1059;
5586
5600
  /** NIP-65: Relay list metadata */
5587
5601
  const RELAY_LIST = 10002;
5602
+ /** NIP-42: Client authentication to relay */
5603
+ const AUTH = 22242;
5588
5604
  /** NIP-78: Application-specific data (parameterized replaceable) */
5589
5605
  const APP_DATA = 30078;
5590
5606
  // ============================================================================
@@ -5698,6 +5714,7 @@ var EventKinds = /*#__PURE__*/Object.freeze({
5698
5714
  AGENT_LOCATION: AGENT_LOCATION,
5699
5715
  AGENT_PROFILE: AGENT_PROFILE,
5700
5716
  APP_DATA: APP_DATA,
5717
+ AUTH: AUTH,
5701
5718
  CHAT_MESSAGE: CHAT_MESSAGE,
5702
5719
  CONTACTS: CONTACTS,
5703
5720
  DELETION: DELETION,
@@ -5998,6 +6015,13 @@ const DEFAULT_QUERY_TIMEOUT_MS = 5000;
5998
6015
  const DEFAULT_RECONNECT_INTERVAL_MS = 1000;
5999
6016
  const DEFAULT_MAX_RECONNECT_INTERVAL_MS = 30000;
6000
6017
  const DEFAULT_PING_INTERVAL_MS = 30000;
6018
+ /**
6019
+ * Delay before resubscribing after NIP-42 authentication.
6020
+ * This gives the relay time to process the AUTH response before we send
6021
+ * subscription requests. Without this delay, some relays may still reject
6022
+ * the subscriptions as the AUTH hasn't been fully processed yet.
6023
+ */
6024
+ const AUTH_RESUBSCRIBE_DELAY_MS = 100;
6001
6025
  /**
6002
6026
  * NostrClient provides the main interface for Nostr protocol operations.
6003
6027
  */
@@ -6342,6 +6366,9 @@ class NostrClient {
6342
6366
  case 'CLOSED':
6343
6367
  this.handleClosedMessage(json);
6344
6368
  break;
6369
+ case 'AUTH':
6370
+ this.handleAuthMessage(_url, json);
6371
+ break;
6345
6372
  }
6346
6373
  }
6347
6374
  catch {
@@ -6422,6 +6449,33 @@ class NostrClient {
6422
6449
  subscription.listener.onError(subscriptionId, `Subscription closed: ${message}`);
6423
6450
  }
6424
6451
  }
6452
+ /**
6453
+ * Handle AUTH message from relay (NIP-42 authentication challenge).
6454
+ */
6455
+ handleAuthMessage(relayUrl, json) {
6456
+ if (json.length < 2)
6457
+ return;
6458
+ const challenge = json[1];
6459
+ const relay = this.relays.get(relayUrl);
6460
+ if (!relay?.socket || !relay.connected)
6461
+ return;
6462
+ // Create and sign the auth event (kind 22242)
6463
+ const authEvent = Event.create(this.keyManager, {
6464
+ kind: AUTH,
6465
+ tags: [
6466
+ ['relay', relayUrl],
6467
+ ['challenge', challenge],
6468
+ ],
6469
+ content: '',
6470
+ });
6471
+ // Send AUTH response
6472
+ const message = JSON.stringify(['AUTH', authEvent.toJSON()]);
6473
+ relay.socket.send(message);
6474
+ // Re-send subscriptions after auth (relay may have ignored pre-auth requests)
6475
+ setTimeout(() => {
6476
+ this.resubscribeAll(relayUrl);
6477
+ }, AUTH_RESUBSCRIBE_DELAY_MS);
6478
+ }
6425
6479
  /**
6426
6480
  * Disconnect from all relays.
6427
6481
  */
@@ -10654,5 +10708,5 @@ function isReadReceipt(message) {
10654
10708
  return message.kind === 15;
10655
10709
  }
10656
10710
 
10657
- export { AGENT_LOCATION, AGENT_PROFILE, APP_DATA, bech32 as Bech32, CHAT_MESSAGE, CLOSED, CLOSING, CONNECTING, CONTACTS, CallbackEventListener, DELETION, ENCRYPTED_DM, Event, EventKinds, FILE_METADATA, Filter, FilterBuilder, GIFT_WRAP, nip04 as NIP04, nip17 as NIP17, nip44 as NIP44, NametagBinding, NametagUtils, NostrClient, NostrKeyManager, OPEN, PAYMENT_REQUEST, PAYMENT_REQUEST_RESPONSE, PROFILE, PaymentRequestProtocol, REACTION, READ_RECEIPT, RECOMMEND_RELAY, RELAY_LIST, SEAL, schnorr as SchnorrSigner, TEXT_NOTE, TOKEN_TRANSFER, TokenTransferProtocol, areSameNametag, createBindingEvent, createGiftWrap, createNametagToPubkeyFilter, createPubkeyToNametagFilter, createReadReceipt, createWebSocket, decode, decodeNpub, decodeNsec, encode, encodeNpub, encodeNsec, extractMessageData, formatForDisplay, getName, getPublicKey, getPublicKeyHex, hashNametag, isChatMessage, isEphemeral, isParameterizedReplaceable, isPhoneNumber, isReadReceipt, isReplaceable, isValidBindingEvent, normalizeNametag, parseAddressFromEvent, parseNametagHashFromEvent, sign, signHex, unwrap, verify, verifyHex };
10711
+ export { AGENT_LOCATION, AGENT_PROFILE, APP_DATA, AUTH, bech32 as Bech32, CHAT_MESSAGE, CLOSED, CLOSING, CONNECTING, CONTACTS, CallbackEventListener, DELETION, ENCRYPTED_DM, Event, EventKinds, FILE_METADATA, Filter, FilterBuilder, GIFT_WRAP, nip04 as NIP04, nip17 as NIP17, nip44 as NIP44, NametagBinding, NametagUtils, NostrClient, NostrKeyManager, OPEN, PAYMENT_REQUEST, PAYMENT_REQUEST_RESPONSE, PROFILE, PaymentRequestProtocol, REACTION, READ_RECEIPT, RECOMMEND_RELAY, RELAY_LIST, SEAL, schnorr as SchnorrSigner, TEXT_NOTE, TOKEN_TRANSFER, TokenTransferProtocol, areSameNametag, createBindingEvent, createGiftWrap, createNametagToPubkeyFilter, createPubkeyToNametagFilter, createReadReceipt, createWebSocket, decode, decodeNpub, decodeNsec, encode, encodeNpub, encodeNsec, extractMessageData, formatForDisplay, getName, getPublicKey, getPublicKeyHex, hashNametag, isChatMessage, isEphemeral, isParameterizedReplaceable, isPhoneNumber, isReadReceipt, isReplaceable, isValidBindingEvent, normalizeNametag, parseAddressFromEvent, parseNametagHashFromEvent, sign, signHex, unwrap, verify, verifyHex };
10658
10712
  //# sourceMappingURL=index.js.map