@rougechain/sdk 0.8.4 → 0.9.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.
package/README.md CHANGED
@@ -337,7 +337,7 @@ const batch = await rc.getRollupBatch(1);
337
337
 
338
338
  ## Mail (`rc.mail`)
339
339
 
340
- On-chain encrypted email with `@rouge.quant` / `@qwalla.mail` addresses.
340
+ On-chain encrypted email with `@rouge.quant` / `@qwalla.mail` addresses. All write operations require a `wallet` parameter for ML-DSA-65 request signing — requests are authenticated via `/api/v2/` endpoints with anti-replay nonce protection.
341
341
 
342
342
  ### Name Registry
343
343
 
@@ -345,17 +345,17 @@ Register a mail name so other users can send you encrypted email. Third-party ap
345
345
 
346
346
  ```typescript
347
347
  // Step 1: Register wallet on the node (required for encryption keys)
348
- await rc.messenger.registerWallet({
348
+ await rc.messenger.registerWallet(wallet, {
349
349
  id: wallet.publicKey,
350
350
  displayName: "Alice",
351
351
  signingPublicKey: wallet.publicKey,
352
352
  encryptionPublicKey: encPubKey,
353
353
  });
354
354
 
355
- // Step 2: Register a mail name
356
- await rc.mail.registerName("alice", wallet.publicKey);
355
+ // Step 2: Register a mail name (signed request)
356
+ await rc.mail.registerName(wallet, "alice", wallet.publicKey);
357
357
 
358
- // Resolve a name → wallet info (includes encryption key)
358
+ // Resolve a name → wallet info (includes encryption key, public data only)
359
359
  const resolved = await rc.mail.resolveName("alice");
360
360
  // { entry: { name, wallet_id }, wallet: { id, signing_public_key, encryption_public_key } }
361
361
 
@@ -363,15 +363,15 @@ const resolved = await rc.mail.resolveName("alice");
363
363
  const name = await rc.mail.reverseLookup(wallet.publicKey);
364
364
  // "alice"
365
365
 
366
- // Release a name
367
- await rc.mail.releaseName("alice", wallet.publicKey);
366
+ // Release a name (signed request)
367
+ await rc.mail.releaseName(wallet, "alice", wallet.publicKey);
368
368
  ```
369
369
 
370
370
  ### Sending & Reading Mail
371
371
 
372
372
  ```typescript
373
- // Send an encrypted email
374
- await rc.mail.send({
373
+ // Send an encrypted email (signed, multi-recipient CEK encryption)
374
+ await rc.mail.send(wallet, {
375
375
  from: wallet.publicKey,
376
376
  to: recipientPubKey,
377
377
  subject: "Hello",
@@ -380,52 +380,55 @@ await rc.mail.send({
380
380
  encrypted_body: encryptedBody,
381
381
  });
382
382
 
383
- // Read inbox
384
- const inbox = await rc.mail.getInbox(wallet.publicKey);
383
+ // Read inbox (signed request)
384
+ const inbox = await rc.mail.getInbox(wallet);
385
385
 
386
- // Move to trash
387
- await rc.mail.move(messageId, "trash");
386
+ // Move to trash (signed request)
387
+ await rc.mail.move(wallet, messageId, "trash");
388
388
 
389
- // Mark as read
390
- await rc.mail.markRead(messageId);
389
+ // Mark as read (signed request)
390
+ await rc.mail.markRead(wallet, messageId);
391
+
392
+ // Delete (signed request)
393
+ await rc.mail.delete(wallet, messageId);
391
394
  ```
392
395
 
393
396
  ## Messenger (`rc.messenger`)
394
397
 
395
- End-to-end encrypted messaging with media and self-destruct support.
398
+ End-to-end encrypted messaging with media and self-destruct support. All operations use ML-DSA-65 signed requests via `/api/v2/` endpoints with nonce-based anti-replay protection.
396
399
 
397
400
  ```typescript
398
- // Register wallet for messaging
399
- await rc.messenger.registerWallet({
401
+ // Register wallet for messaging (signed request)
402
+ await rc.messenger.registerWallet(wallet, {
400
403
  id: wallet.publicKey,
401
404
  displayName: "Alice",
402
405
  signingPublicKey: wallet.publicKey,
403
406
  encryptionPublicKey: encPubKey,
404
407
  });
405
408
 
406
- // Create a conversation
407
- const result = await rc.messenger.createConversation([
409
+ // Create a conversation (signed request)
410
+ const result = await rc.messenger.createConversation(wallet, [
408
411
  wallet.publicKey,
409
412
  recipientPubKey,
410
413
  ]);
411
414
 
412
- // Fetch conversations (with extended key matching)
413
- const convos = await rc.messenger.getConversations(wallet.publicKey, {
414
- signingPublicKey: wallet.publicKey,
415
- encryptionPublicKey: encPubKey,
416
- });
415
+ // Fetch conversations (signed request)
416
+ const convos = await rc.messenger.getConversations(wallet);
417
417
 
418
- // Send an encrypted message (with optional self-destruct)
419
- await rc.messenger.sendMessage(conversationId, wallet.publicKey, encryptedContent, {
418
+ // Send an encrypted message (signed, with optional self-destruct)
419
+ await rc.messenger.sendMessage(wallet, conversationId, encryptedContent, {
420
420
  selfDestruct: true,
421
421
  destructAfterSeconds: 30,
422
422
  });
423
423
 
424
- // Read messages
425
- const messages = await rc.messenger.getMessages(conversationId);
424
+ // Read messages (signed request)
425
+ const messages = await rc.messenger.getMessages(wallet, conversationId);
426
+
427
+ // Delete a message (signed request)
428
+ await rc.messenger.deleteMessage(wallet, messageId);
426
429
 
427
- // Delete a message
428
- await rc.messenger.deleteMessage(messageId);
430
+ // Delete a conversation (signed request)
431
+ await rc.messenger.deleteConversation(wallet, conversationId);
429
432
  ```
430
433
 
431
434
  ## Shielded Transactions (`rc.shielded`)
@@ -553,6 +556,9 @@ import type {
553
556
  - **Post-quantum cryptography** — All signatures use ML-DSA-65 (CRYSTALS-Dilithium), resistant to quantum computer attacks
554
557
  - **Client-side signing** — Private keys never leave your application
555
558
  - **No key storage** — The SDK does not store or transmit keys
559
+ - **Signed API requests** — All mail, messenger, and name registry operations use ML-DSA-65 signed requests with timestamp validation and nonce-based anti-replay protection
560
+ - **Multi-recipient CEK encryption** — Mail content is encrypted once with a random AES-256 key, KEM-wrapped individually per recipient via ML-KEM-768
561
+ - **TOFU key verification** — Public key fingerprints (SHA-256) are tracked for key change detection
556
562
 
557
563
  ## Links
558
564
 
package/dist/index.cjs CHANGED
@@ -286,6 +286,9 @@ function createSignedPushUnregister(wallet) {
286
286
  type: "push_unregister"
287
287
  });
288
288
  }
289
+ function signRequest(wallet, payload) {
290
+ return buildAndSign(wallet, payload);
291
+ }
289
292
  var COMMITMENT_DOMAIN = new TextEncoder().encode("ROUGECHAIN_COMMITMENT_V1");
290
293
  var NULLIFIER_DOMAIN = new TextEncoder().encode("ROUGECHAIN_NULLIFIER_V1");
291
294
  function generateRandomness() {
@@ -959,17 +962,10 @@ var MailClient = class {
959
962
  constructor(rc) {
960
963
  this.rc = rc;
961
964
  }
962
- // --- Name Registry ---
963
- async registerName(name, walletId) {
964
- try {
965
- const data = await this.rc.post("/names/register", {
966
- name,
967
- walletId
968
- });
969
- return { success: data.success === true, error: data.error, data };
970
- } catch (e) {
971
- return { success: false, error: e instanceof Error ? e.message : String(e) };
972
- }
965
+ // --- Name Registry (signed) ---
966
+ async registerName(wallet, name, walletId) {
967
+ const signed = signRequest(wallet, { name, walletId });
968
+ return this.rc.submitTx("/v2/names/register", signed);
973
969
  }
974
970
  async resolveName(name) {
975
971
  try {
@@ -992,85 +988,71 @@ var MailClient = class {
992
988
  return null;
993
989
  }
994
990
  }
995
- async releaseName(name, walletId) {
996
- try {
997
- const res = await this.rc.fetchFn(
998
- `${this.rc.baseUrl}/names/release`,
999
- {
1000
- method: "DELETE",
1001
- headers: { ...this.rc.headers, "Content-Type": "application/json" },
1002
- body: JSON.stringify({ name, walletId })
1003
- }
1004
- );
1005
- const data = await res.json();
1006
- return { success: data.success === true, error: data.error };
1007
- } catch (e) {
1008
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1009
- }
991
+ async releaseName(wallet, name) {
992
+ const signed = signRequest(wallet, { name });
993
+ return this.rc.submitTx("/v2/names/release", signed);
994
+ }
995
+ // --- Mail (signed) ---
996
+ async send(wallet, params) {
997
+ const signed = signRequest(wallet, {
998
+ fromWalletId: params.from,
999
+ toWalletIds: [params.to],
1000
+ subjectEncrypted: params.encrypted_subject,
1001
+ bodyEncrypted: params.encrypted_body,
1002
+ contentSignature: params.body,
1003
+ replyToId: params.reply_to_id,
1004
+ hasAttachment: false
1005
+ });
1006
+ return this.rc.submitTx("/v2/mail/send", signed);
1010
1007
  }
1011
- // --- Mail ---
1012
- async send(params) {
1008
+ async getInbox(wallet) {
1009
+ const signed = signRequest(wallet, { folder: "inbox" });
1013
1010
  try {
1014
- const data = await this.rc.post("/mail/send", params);
1015
- return { success: data.success === true, error: data.error, data };
1016
- } catch (e) {
1017
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1011
+ const data = await this.rc.post("/v2/mail/folder", signed);
1012
+ return data.messages ?? [];
1013
+ } catch {
1014
+ return [];
1018
1015
  }
1019
1016
  }
1020
- async getInbox(walletId) {
1021
- const data = await this.rc.get(
1022
- `/mail/inbox?walletId=${encodeURIComponent(walletId)}`
1023
- );
1024
- return data.messages ?? [];
1025
- }
1026
- async getSent(walletId) {
1027
- const data = await this.rc.get(
1028
- `/mail/sent?walletId=${encodeURIComponent(walletId)}`
1029
- );
1030
- return data.messages ?? [];
1031
- }
1032
- async getTrash(walletId) {
1033
- const data = await this.rc.get(
1034
- `/mail/trash?walletId=${encodeURIComponent(walletId)}`
1035
- );
1036
- return data.messages ?? [];
1037
- }
1038
- async getMessage(id) {
1039
- return this.rc.get(`/mail/message/${encodeURIComponent(id)}`);
1040
- }
1041
- async move(messageId, folder) {
1017
+ async getSent(wallet) {
1018
+ const signed = signRequest(wallet, { folder: "sent" });
1042
1019
  try {
1043
- const data = await this.rc.post("/mail/move", {
1044
- messageId,
1045
- folder
1046
- });
1047
- return { success: data.success === true, error: data.error };
1048
- } catch (e) {
1049
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1020
+ const data = await this.rc.post("/v2/mail/folder", signed);
1021
+ return data.messages ?? [];
1022
+ } catch {
1023
+ return [];
1050
1024
  }
1051
1025
  }
1052
- async markRead(messageId) {
1026
+ async getTrash(wallet) {
1027
+ const signed = signRequest(wallet, { folder: "trash" });
1053
1028
  try {
1054
- const data = await this.rc.post("/mail/read", {
1055
- messageId
1056
- });
1057
- return { success: data.success === true, error: data.error };
1058
- } catch (e) {
1059
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1029
+ const data = await this.rc.post("/v2/mail/folder", signed);
1030
+ return data.messages ?? [];
1031
+ } catch {
1032
+ return [];
1060
1033
  }
1061
1034
  }
1062
- async delete(id) {
1035
+ async getMessage(wallet, messageId) {
1036
+ const signed = signRequest(wallet, { messageId });
1063
1037
  try {
1064
- const res = await this.rc.fetchFn(
1065
- `${this.rc.baseUrl}/mail/${encodeURIComponent(id)}`,
1066
- { method: "DELETE", headers: this.rc.headers }
1067
- );
1068
- const data = await res.json();
1069
- return { success: data.success === true, error: data.error };
1070
- } catch (e) {
1071
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1038
+ const data = await this.rc.post("/v2/mail/message", signed);
1039
+ return data.message ?? null;
1040
+ } catch {
1041
+ return null;
1072
1042
  }
1073
1043
  }
1044
+ async move(wallet, messageId, folder) {
1045
+ const signed = signRequest(wallet, { messageId, folder });
1046
+ return this.rc.submitTx("/v2/mail/move", signed);
1047
+ }
1048
+ async markRead(wallet, messageId) {
1049
+ const signed = signRequest(wallet, { messageId });
1050
+ return this.rc.submitTx("/v2/mail/read", signed);
1051
+ }
1052
+ async delete(wallet, messageId) {
1053
+ const signed = signRequest(wallet, { messageId });
1054
+ return this.rc.submitTx("/v2/mail/delete", signed);
1055
+ }
1074
1056
  };
1075
1057
  var MessengerClient = class {
1076
1058
  constructor(rc) {
@@ -1080,77 +1062,71 @@ var MessengerClient = class {
1080
1062
  const data = await this.rc.get("/messenger/wallets");
1081
1063
  return data.wallets ?? [];
1082
1064
  }
1083
- async registerWallet(opts) {
1065
+ async registerWallet(wallet, opts) {
1066
+ const signed = signRequest(wallet, {
1067
+ id: opts.id,
1068
+ displayName: opts.displayName,
1069
+ signingPublicKey: opts.signingPublicKey,
1070
+ encryptionPublicKey: opts.encryptionPublicKey,
1071
+ discoverable: opts.discoverable ?? true
1072
+ });
1073
+ return this.rc.submitTx("/v2/messenger/wallets/register", signed);
1074
+ }
1075
+ async getConversations(wallet) {
1076
+ const signed = signRequest(wallet, {});
1084
1077
  try {
1085
- const data = await this.rc.post("/messenger/wallets/register", opts);
1086
- return { success: data.success === true, error: data.error, data };
1087
- } catch (e) {
1088
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1078
+ const data = await this.rc.post(
1079
+ "/v2/messenger/conversations/list",
1080
+ signed
1081
+ );
1082
+ return data.conversations ?? [];
1083
+ } catch {
1084
+ return [];
1089
1085
  }
1090
1086
  }
1091
- async getConversations(walletId, opts = {}) {
1092
- const params = new URLSearchParams({ walletId });
1093
- if (opts.signingPublicKey) params.set("signingPublicKey", opts.signingPublicKey);
1094
- if (opts.encryptionPublicKey) params.set("encryptionPublicKey", opts.encryptionPublicKey);
1095
- const data = await this.rc.get(
1096
- `/messenger/conversations?${params.toString()}`
1097
- );
1098
- return data.conversations ?? [];
1087
+ async createConversation(wallet, participantIds, opts = {}) {
1088
+ const signed = signRequest(wallet, {
1089
+ participantIds,
1090
+ name: opts.name,
1091
+ isGroup: opts.isGroup ?? false
1092
+ });
1093
+ return this.rc.submitTx("/v2/messenger/conversations", signed);
1099
1094
  }
1100
- async createConversation(participants) {
1095
+ async getMessages(wallet, conversationId) {
1096
+ const signed = signRequest(wallet, { conversationId });
1101
1097
  try {
1102
- const data = await this.rc.post("/messenger/conversations", {
1103
- participants
1104
- });
1105
- return { success: data.success === true, error: data.error, data };
1106
- } catch (e) {
1107
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1098
+ const data = await this.rc.post(
1099
+ "/v2/messenger/messages/list",
1100
+ signed
1101
+ );
1102
+ return data.messages ?? [];
1103
+ } catch {
1104
+ return [];
1108
1105
  }
1109
1106
  }
1110
- async getMessages(conversationId) {
1111
- const data = await this.rc.get(
1112
- `/messenger/messages?conversationId=${encodeURIComponent(conversationId)}`
1113
- );
1114
- return data.messages ?? [];
1107
+ async sendMessage(wallet, conversationId, encryptedContent, opts = {}) {
1108
+ const signed = signRequest(wallet, {
1109
+ conversationId,
1110
+ encryptedContent,
1111
+ contentSignature: opts.contentSignature ?? "",
1112
+ messageType: opts.messageType ?? "text",
1113
+ selfDestruct: opts.selfDestruct ?? false,
1114
+ destructAfterSeconds: opts.destructAfterSeconds,
1115
+ spoiler: opts.spoiler ?? false
1116
+ });
1117
+ return this.rc.submitTx("/v2/messenger/messages", signed);
1115
1118
  }
1116
- async sendMessage(conversationId, senderWalletId, encryptedContent, opts = {}) {
1117
- try {
1118
- const data = await this.rc.post("/messenger/messages", {
1119
- conversationId,
1120
- senderWalletId,
1121
- encryptedContent,
1122
- signature: opts.signature ?? "",
1123
- messageType: opts.messageType ?? "text",
1124
- selfDestruct: opts.selfDestruct ?? false,
1125
- destructAfterSeconds: opts.destructAfterSeconds,
1126
- spoiler: opts.spoiler ?? false
1127
- });
1128
- return { success: data.success === true, error: data.error, data };
1129
- } catch (e) {
1130
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1131
- }
1119
+ async deleteMessage(wallet, messageId, conversationId) {
1120
+ const signed = signRequest(wallet, { messageId, conversationId });
1121
+ return this.rc.submitTx("/v2/messenger/messages/delete", signed);
1132
1122
  }
1133
- async deleteMessage(messageId) {
1134
- try {
1135
- const res = await this.rc.fetchFn(
1136
- `${this.rc.baseUrl}/messenger/messages/${encodeURIComponent(messageId)}`,
1137
- { method: "DELETE", headers: this.rc.headers }
1138
- );
1139
- const data = await res.json();
1140
- return { success: data.success === true, error: data.error };
1141
- } catch (e) {
1142
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1143
- }
1123
+ async deleteConversation(wallet, conversationId) {
1124
+ const signed = signRequest(wallet, { conversationId });
1125
+ return this.rc.submitTx("/v2/messenger/conversations/delete", signed);
1144
1126
  }
1145
- async markRead(messageId) {
1146
- try {
1147
- const data = await this.rc.post("/messenger/messages/read", {
1148
- messageId
1149
- });
1150
- return { success: data.success === true, error: data.error };
1151
- } catch (e) {
1152
- return { success: false, error: e instanceof Error ? e.message : String(e) };
1153
- }
1127
+ async markRead(wallet, messageId, conversationId) {
1128
+ const signed = signRequest(wallet, { messageId, conversationId });
1129
+ return this.rc.submitTx("/v2/messenger/messages/read", signed);
1154
1130
  }
1155
1131
  };
1156
1132
  var ShieldedClient = class {
@@ -1486,6 +1462,7 @@ exports.keypairFromMnemonic = keypairFromMnemonic;
1486
1462
  exports.mnemonicToMLDSASeed = mnemonicToMLDSASeed;
1487
1463
  exports.pubkeyToAddress = pubkeyToAddress;
1488
1464
  exports.serializePayload = serializePayload;
1465
+ exports.signRequest = signRequest;
1489
1466
  exports.signTransaction = signTransaction;
1490
1467
  exports.validateMnemonic = validateMnemonic;
1491
1468
  exports.verifyTransaction = verifyTransaction;