@calimero-network/mero-js 13.3.0 → 14.0.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/dist/index.cjs CHANGED
@@ -55,12 +55,14 @@ __export(index_exports, {
55
55
  createUniversalHttpClient: () => createUniversalHttpClient,
56
56
  discoverLocalNodes: () => discoverLocalNodes,
57
57
  hasCap: () => hasCap,
58
+ intentHash: () => intentHash,
58
59
  jsonCodec: () => jsonCodec,
59
60
  localNodeUrl: () => localNodeUrl,
60
61
  nodeEndpoint: () => nodeEndpoint,
61
62
  parseAuthCallback: () => parseAuthCallback,
62
63
  probeNodeHealth: () => probeNodeHealth,
63
64
  sameAccount: () => sameAccount,
65
+ signWarrant: () => signWarrant,
64
66
  toAccountBase58: () => toAccountBase58,
65
67
  toAccountHex: () => toAccountHex,
66
68
  withCap: () => withCap,
@@ -1190,7 +1192,8 @@ var AdminApiClient = class {
1190
1192
  * CAN_AUTHOR_ON_BEHALF to …", or that the nonce is already spent — so a caller
1191
1193
  * can tell which precondition failed rather than only that one did.
1192
1194
  *
1193
- * This SDK does not mint warrants; see {@link PerformIntentRequest.warrant}.
1195
+ * Mint the warrant with {@link signWarrant}; see
1196
+ * {@link PerformIntentRequest.warrant}.
1194
1197
  */
1195
1198
  async performIntent(contextId, request) {
1196
1199
  return unwrap(
@@ -1296,14 +1299,16 @@ var AdminApiClient = class {
1296
1299
  }
1297
1300
  /**
1298
1301
  * `identity` is the member's ACCOUNT (64 hex), as returned by
1299
- * {@link listGroupMembers} — not the bs58 signing key that added them.
1302
+ * {@link listGroupMembers} — not the signing key that added them. Both are
1303
+ * 64 hex, so nothing but the source tells them apart.
1300
1304
  */
1301
1305
  async updateMemberRole(groupId, identity, request) {
1302
1306
  await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/role`, request);
1303
1307
  }
1304
1308
  /**
1305
1309
  * `identity` is the member's ACCOUNT (64 hex), as returned by
1306
- * {@link listGroupMembers} — not the bs58 signing key that added them.
1310
+ * {@link listGroupMembers} — not the signing key that added them. Both are
1311
+ * 64 hex, so nothing but the source tells them apart.
1307
1312
  */
1308
1313
  async getMemberCapabilities(groupId, identity) {
1309
1314
  return unwrap(
@@ -1314,7 +1319,8 @@ var AdminApiClient = class {
1314
1319
  }
1315
1320
  /**
1316
1321
  * `identity` is the member's ACCOUNT (64 hex), as returned by
1317
- * {@link listGroupMembers} — not the bs58 signing key that added them.
1322
+ * {@link listGroupMembers} — not the signing key that added them. Both are
1323
+ * 64 hex, so nothing but the source tells them apart.
1318
1324
  */
1319
1325
  async setMemberCapabilities(groupId, identity, request) {
1320
1326
  await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/capabilities`, request);
@@ -1347,7 +1353,8 @@ var AdminApiClient = class {
1347
1353
  }
1348
1354
  /**
1349
1355
  * `identity` is the member's ACCOUNT (64 hex), as returned by
1350
- * {@link listGroupMembers} — not the bs58 signing key that added them.
1356
+ * {@link listGroupMembers} — not the signing key that added them. Both are
1357
+ * 64 hex, so nothing but the source tells them apart.
1351
1358
  */
1352
1359
  async getMemberMetadata(groupId, identity) {
1353
1360
  const response = await this.httpClient.get(
@@ -1501,7 +1508,8 @@ var AdminApiClient = class {
1501
1508
  * /admin-api/groups/:group_id/members/:identity/auto-follow).
1502
1509
  *
1503
1510
  * `identity` is the member's ACCOUNT (64 hex), as returned by
1504
- * {@link listGroupMembers} — not the bs58 signing key that added them.
1511
+ * {@link listGroupMembers} — not the signing key that added them. Both are
1512
+ * 64 hex, so nothing but the source tells them apart.
1505
1513
  */
1506
1514
  async setMemberAutoFollow(groupId, identity, request) {
1507
1515
  await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/auto-follow`, request);
@@ -2566,10 +2574,10 @@ function toHex(bytes) {
2566
2574
  for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
2567
2575
  return out;
2568
2576
  }
2569
- function fromHex(hex) {
2570
- const bytes = new Uint8Array(hex.length / 2);
2577
+ function fromHex(hex2) {
2578
+ const bytes = new Uint8Array(hex2.length / 2);
2571
2579
  for (let i = 0; i < bytes.length; i++) {
2572
- bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
2580
+ bytes[i] = parseInt(hex2.slice(i * 2, i * 2 + 2), 16);
2573
2581
  }
2574
2582
  return bytes;
2575
2583
  }
@@ -2674,6 +2682,140 @@ var CloudClient = class {
2674
2682
  }
2675
2683
  };
2676
2684
 
2685
+ // src/warrant/warrant.ts
2686
+ var SIGN_DOMAIN = new TextEncoder().encode("calimero.warrant.v1");
2687
+ var INTENT_DOMAIN = new TextEncoder().encode("calimero.warrant.intent.v1");
2688
+ var PKCS8_ED25519_PREFIX = new Uint8Array([
2689
+ 48,
2690
+ 46,
2691
+ 2,
2692
+ 1,
2693
+ 0,
2694
+ 48,
2695
+ 5,
2696
+ 6,
2697
+ 3,
2698
+ 43,
2699
+ 101,
2700
+ 112,
2701
+ 4,
2702
+ 34,
2703
+ 4,
2704
+ 32
2705
+ ]);
2706
+ function hex(bytes) {
2707
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2708
+ }
2709
+ function fromHex2(value, label, expectedBytes) {
2710
+ const clean = value.trim();
2711
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
2712
+ throw new Error(
2713
+ `${label} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
2714
+ );
2715
+ }
2716
+ const bytes = new Uint8Array(expectedBytes);
2717
+ for (let i = 0; i < expectedBytes; i += 1) {
2718
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2719
+ }
2720
+ return bytes;
2721
+ }
2722
+ function u64le(value) {
2723
+ const out = new Uint8Array(8);
2724
+ new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
2725
+ return out;
2726
+ }
2727
+ function concat(...parts) {
2728
+ const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
2729
+ let offset = 0;
2730
+ for (const part of parts) {
2731
+ out.set(part, offset);
2732
+ offset += part.length;
2733
+ }
2734
+ return out;
2735
+ }
2736
+ async function domainHash(domain, parts) {
2737
+ const chunks = [u64le(domain.length), domain];
2738
+ for (const part of parts) {
2739
+ chunks.push(u64le(part.length), part);
2740
+ }
2741
+ return new Uint8Array(
2742
+ await crypto.subtle.digest("SHA-256", concat(...chunks))
2743
+ );
2744
+ }
2745
+ async function intentHash(method, argsJson) {
2746
+ const args = new TextEncoder().encode(JSON.stringify(argsJson));
2747
+ return domainHash(INTENT_DOMAIN, [new TextEncoder().encode(method), args]);
2748
+ }
2749
+ async function importSigningKey(secretHex) {
2750
+ const seed = fromHex2(secretHex, "deviceSecret", 32);
2751
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2752
+ try {
2753
+ return await crypto.subtle.importKey(
2754
+ "pkcs8",
2755
+ pkcs8,
2756
+ { name: "Ed25519" },
2757
+ false,
2758
+ ["sign"]
2759
+ );
2760
+ } catch (cause) {
2761
+ const detail = cause instanceof Error ? `: ${cause.message}` : "";
2762
+ throw new Error(
2763
+ "this runtime has no WebCrypto Ed25519, so a warrant cannot be signed here. Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it; otherwise mint the warrant with `merod account warrant`" + detail
2764
+ );
2765
+ }
2766
+ }
2767
+ async function signWarrant(input) {
2768
+ const context = fromHex2(input.context, "context", 32);
2769
+ const authorAccount = fromHex2(input.authorAccount, "authorAccount", 32);
2770
+ const executor = fromHex2(input.executor, "executor", 32);
2771
+ const key = await importSigningKey(input.deviceSecret);
2772
+ const publicKey = await derivePublicKey(input.deviceSecret);
2773
+ const commitment = await intentHash(input.method, input.argsJson);
2774
+ const nonce = u64le(input.nonce);
2775
+ const notAfter = u64le(input.notAfter);
2776
+ const preimage = await domainHash(SIGN_DOMAIN, [
2777
+ context,
2778
+ authorAccount,
2779
+ publicKey,
2780
+ executor,
2781
+ commitment,
2782
+ nonce,
2783
+ notAfter
2784
+ ]);
2785
+ const signature = new Uint8Array(
2786
+ await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
2787
+ );
2788
+ return hex(
2789
+ concat(
2790
+ context,
2791
+ authorAccount,
2792
+ publicKey,
2793
+ executor,
2794
+ commitment,
2795
+ nonce,
2796
+ notAfter,
2797
+ signature
2798
+ )
2799
+ );
2800
+ }
2801
+ async function derivePublicKey(secretHex) {
2802
+ const seed = fromHex2(secretHex, "deviceSecret", 32);
2803
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2804
+ const key = await crypto.subtle.importKey(
2805
+ "pkcs8",
2806
+ pkcs8,
2807
+ { name: "Ed25519" },
2808
+ true,
2809
+ ["sign"]
2810
+ );
2811
+ const jwk = await crypto.subtle.exportKey("jwk", key);
2812
+ const raw = jwk.x;
2813
+ return Uint8Array.from(
2814
+ atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2815
+ (c) => c.charCodeAt(0)
2816
+ );
2817
+ }
2818
+
2677
2819
  // src/capabilities.ts
2678
2820
  var CAPABILITIES = {
2679
2821
  CAN_CREATE_CONTEXT: 1 << 0,