@wishknish/knishio-client-js 0.9.3 → 1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishknish/knishio-client-js",
3
- "version": "0.9.3",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "productName": "Knish.IO Javascript SDK Client",
6
6
  "description": "JavaScript implementation of the Knish.IO SDK to consume Knish.IO GraphQL APIs.",
@@ -48,16 +48,16 @@
48
48
  "quantum-safe",
49
49
  "sdk",
50
50
  "javascript",
51
- "ml-kem768",
51
+ "ml-kem1024",
52
52
  "cryptography",
53
53
  "crystals-kyber",
54
54
  "fips-202",
55
55
  "fips-203"
56
56
  ],
57
57
  "dependencies": {
58
- "@noble/post-quantum": "^0.5.4",
59
- "@thumbmarkjs/thumbmarkjs": "^0.19.1",
60
- "@urql/core": "^5.2.0",
58
+ "@noble/post-quantum": "^0.7.1",
59
+ "@thumbmarkjs/thumbmarkjs": "^1.11.0",
60
+ "@urql/core": "^6.0.3",
61
61
  "graphql": "^16.12.0",
62
62
  "graphql-ws": "^6.0.7",
63
63
  "isomorphic-fetch": "^3.0.0",
@@ -66,17 +66,13 @@
66
66
  },
67
67
  "devDependencies": {
68
68
  "@jest/globals": "^30.4.1",
69
- "@rollup/plugin-babel": "^6.1.0",
70
- "@rollup/plugin-commonjs": "^28.0.9",
71
- "@rollup/plugin-node-resolve": "^16.0.3",
72
69
  "@swc/core": "^1.15.43",
73
70
  "@swc/jest": "^0.2.39",
74
71
  "buffer": "^6.0.3",
75
72
  "eslint": "^9.39.0",
76
73
  "jest": "^30.4.2",
77
74
  "neostandard": "^0.13.0",
78
- "rollup": "^4.57.1",
79
- "vite": "^7.3.5"
75
+ "vite": "^8.2.2"
80
76
  },
81
77
  "browserslist": [
82
78
  "> 1%",
@@ -96,5 +92,8 @@
96
92
  "/src",
97
93
  "/dist"
98
94
  ],
99
- "packageManager": "yarn@4.9.2"
95
+ "packageManager": "yarn@4.15.0",
96
+ "engines": {
97
+ "node": ">=20.0.0"
98
+ }
100
99
  }
package/src/AuthToken.js CHANGED
@@ -83,6 +83,26 @@ export default class AuthToken {
83
83
  return authToken
84
84
  }
85
85
 
86
+ /**
87
+ * ML-KEM parameter set a restored session must use, resolved in three tiers:
88
+ * an explicit snapshot field, then the stored validator key's length, then ML-KEM-768.
89
+ *
90
+ * The final tier is deliberately NOT the constructor default. A snapshot with neither an
91
+ * explicit field nor a recognisable key can only have come from a pre-bump build, and every
92
+ * pre-bump build was 768-only — defaulting to 1024 would make the restored wallet advertise
93
+ * a public key the validator never recorded for that token.
94
+ *
95
+ * @param {object} snapshot
96
+ * @return {number}
97
+ */
98
+ static resolveMlKemParameterSet (snapshot) {
99
+ const explicit = snapshot.wallet && snapshot.wallet.mlKemParameterSet
100
+ if (explicit) {
101
+ return Number(explicit)
102
+ }
103
+ return Wallet.mlKemParameterSetFromPubkey(snapshot.pubkey) || 768
104
+ }
105
+
86
106
  /**
87
107
  *
88
108
  * @param {object} snapshot
@@ -94,7 +114,8 @@ export default class AuthToken {
94
114
  secret,
95
115
  token: 'AUTH',
96
116
  position: snapshot.wallet.position,
97
- characters: snapshot.wallet.characters
117
+ characters: snapshot.wallet.characters,
118
+ mlKemParameterSet: AuthToken.resolveMlKemParameterSet(snapshot)
98
119
  })
99
120
  return AuthToken.create({
100
121
  token: snapshot.token,
@@ -122,7 +143,7 @@ export default class AuthToken {
122
143
 
123
144
  /**
124
145
  *
125
- * @return {{wallet: {characters, position}, encrypt, expiresAt, token, pubkey}}
146
+ * @return {{wallet: {characters, position, mlKemParameterSet}, encrypt, expiresAt, token, pubkey}}
126
147
  */
127
148
  getSnapshot () {
128
149
  return {
@@ -132,7 +153,8 @@ export default class AuthToken {
132
153
  encrypt: this.$__encrypt,
133
154
  wallet: {
134
155
  position: this.$__wallet.position,
135
- characters: this.$__wallet.characters
156
+ characters: this.$__wallet.characters,
157
+ mlKemParameterSet: this.$__wallet.mlKemParameterSet
136
158
  }
137
159
  }
138
160
  }
@@ -46,6 +46,7 @@ Please visit https://github.com/WishKnish/KnishIO-Client-JS for information.
46
46
  License: https://github.com/WishKnish/KnishIO-Client-JS/blob/master/LICENSE
47
47
  */
48
48
  import Dot from './libraries/Dot.js'
49
+ import MemorySecretStorageProvider from './storage/MemorySecretStorageProvider.js'
49
50
  import Decimal from './libraries/Decimal.js'
50
51
  import {
51
52
  generateBatchId,
@@ -127,7 +128,9 @@ export default class KnishIOClient {
127
128
  socket = null,
128
129
  serverSdkVersion = 3,
129
130
  logging = false,
130
- defaultRequestPolicy = null
131
+ defaultRequestPolicy = null,
132
+ secretStorage = null,
133
+ mlKemParameterSet = 1024
131
134
  }) {
132
135
  this.initialize({
133
136
  uri,
@@ -136,7 +139,9 @@ export default class KnishIOClient {
136
139
  client,
137
140
  serverSdkVersion,
138
141
  logging,
139
- defaultRequestPolicy
142
+ defaultRequestPolicy,
143
+ secretStorage,
144
+ mlKemParameterSet
140
145
  })
141
146
  }
142
147
 
@@ -157,15 +162,19 @@ export default class KnishIOClient {
157
162
  client = null,
158
163
  serverSdkVersion = 3,
159
164
  logging = false,
160
- defaultRequestPolicy = null
165
+ defaultRequestPolicy = null,
166
+ secretStorage = null,
167
+ mlKemParameterSet = 1024
161
168
  }) {
162
169
  this.reset()
163
170
 
171
+ this.$__secretStorage = secretStorage
164
172
  this.$__logging = logging
165
173
  // Client-level urql request policy applied to reads that omit a per-call
166
174
  // policy. A long-lived server/sync client set to 'network-only' never serves
167
175
  // a stale cache-first read; browser/SPA consumers leave this null (cache-first).
168
176
  this.$__defaultRequestPolicy = defaultRequestPolicy
177
+ this.setMlKemParameterSet(mlKemParameterSet)
169
178
  this.$__authTokenObjects = {}
170
179
  this.$__authInProcess = false
171
180
  this.abortControllers = new Map()
@@ -197,6 +206,30 @@ export default class KnishIOClient {
197
206
  this.$__serverSdkVersion = serverSdkVersion
198
207
  }
199
208
 
209
+ /**
210
+ * Get active ML-KEM parameter set (1024 default or 768 step-back)
211
+ *
212
+ * @return {number}
213
+ */
214
+ getMlKemParameterSet () {
215
+ return this.$__mlKemParameterSet || 1024
216
+ }
217
+
218
+ /**
219
+ * Set active ML-KEM parameter set (1024 default or 768 step-back)
220
+ *
221
+ * @param {number|string} parameterSet
222
+ * @return {KnishIOClient}
223
+ */
224
+ setMlKemParameterSet (parameterSet) {
225
+ const paramNum = Number(parameterSet)
226
+ if (![1024, 768].includes(paramNum)) {
227
+ throw new Error(`KnishIO: unsupported ML-KEM parameter set ${parameterSet}; expected 1024 or 768.`)
228
+ }
229
+ this.$__mlKemParameterSet = paramNum
230
+ return this
231
+ }
232
+
200
233
  /**
201
234
  * Get random uri from specified this.$__uris
202
235
  *
@@ -278,6 +311,7 @@ export default class KnishIOClient {
278
311
  reset () {
279
312
  this.$__secret = ''
280
313
  this.$__bundle = ''
314
+ this.$__secretStorage = null
281
315
  this.remainderWallet = null
282
316
  this.$__capabilityCache = {}
283
317
  this.$__defaultRequestPolicy = null
@@ -376,7 +410,7 @@ export default class KnishIOClient {
376
410
  * @return {boolean}
377
411
  */
378
412
  hasSecret () {
379
- return !!this.$__secret
413
+ return (!!this.$__secret && this.$__secret.length > 0) || (!!this.$__secretStorage && !!this.$__bundle && this.$__bundle.length > 0)
380
414
  }
381
415
 
382
416
  /**
@@ -387,6 +421,13 @@ export default class KnishIOClient {
387
421
  setSecret (secret) {
388
422
  this.$__secret = secret
389
423
  this.$__bundle = this.hashSecret(secret, 'setSecret')
424
+ if (!this.$__secretStorage) {
425
+ const memStorage = new MemorySecretStorageProvider()
426
+ memStorage.storeSecret(this.$__bundle, secret)
427
+ this.$__secretStorage = memStorage
428
+ } else {
429
+ this.$__secretStorage.storeSecret(this.$__bundle, secret)
430
+ }
390
431
  }
391
432
 
392
433
  /**
@@ -412,6 +453,44 @@ export default class KnishIOClient {
412
453
  return this.$__secret
413
454
  }
414
455
 
456
+ /**
457
+ * Sets the secret storage provider and optionally sets the bundle hash
458
+ *
459
+ * @param {object} storage
460
+ * @param {string|null} [bundleHash]
461
+ */
462
+ setSecretStorage (storage, bundleHash = null) {
463
+ this.$__secretStorage = storage
464
+ if (bundleHash) {
465
+ this.$__bundle = bundleHash
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Returns current secret storage provider
471
+ *
472
+ * @returns {object|null}
473
+ */
474
+ getSecretStorage () {
475
+ return this.$__secretStorage
476
+ }
477
+
478
+ /**
479
+ * Asynchronously retrieves the secret from storage or returns in-memory secret
480
+ *
481
+ * @param {object} [options]
482
+ * @returns {Promise<string|null>}
483
+ */
484
+ async retrieveSecret (options = {}) {
485
+ if (this.$__secret && this.$__secret.length > 0) {
486
+ return this.$__secret
487
+ }
488
+ if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
489
+ return await this.$__secretStorage.retrieveSecret(this.$__bundle, options)
490
+ }
491
+ return null
492
+ }
493
+
415
494
  /**
416
495
  * Returns whether a bundle hash is being stored for this session
417
496
  *
@@ -458,7 +537,8 @@ export default class KnishIOClient {
458
537
 
459
538
  if (!sourceWallet) {
460
539
  sourceWallet = new Wallet({
461
- secret: this.getSecret()
540
+ secret: this.getSecret(),
541
+ mlKemParameterSet: this.getMlKemParameterSet()
462
542
  })
463
543
  } else {
464
544
  sourceWallet.key = Wallet.generateKey({
@@ -496,10 +576,16 @@ export default class KnishIOClient {
496
576
  remainderWallet = null
497
577
  }) {
498
578
  this.log('info', 'KnishIOClient::createMolecule() - Creating a new molecule...')
579
+ if (!secret) {
580
+ if (this.$__secret && this.$__secret.length > 0) {
581
+ secret = this.getSecret()
582
+ } else if (this.$__secretStorage && this.$__bundle && this.$__bundle.length > 0) {
583
+ secret = await this.$__secretStorage.retrieveSecret(this.$__bundle)
584
+ }
585
+ }
499
586
 
500
587
  secret = secret || this.getSecret()
501
588
  bundle = bundle || this.getBundle()
502
-
503
589
  // For non-USER source wallets (V/B isotopes), capture the current USER
504
590
  // ContinuID position BEFORE overwriting the client's remainder wallet.
505
591
  // This position is needed by addContinuIdAtom() for previousPosition metadata.
@@ -541,16 +627,19 @@ export default class KnishIOClient {
541
627
  bundle,
542
628
  token: 'USER',
543
629
  batchId: sourceWallet.batchId,
544
- characters: sourceWallet.characters
630
+ characters: sourceWallet.characters,
631
+ mlKemParameterSet: this.getMlKemParameterSet()
545
632
  })
546
633
 
547
634
  return new Molecule({
548
635
  secret,
636
+ bundle,
549
637
  sourceWallet,
550
638
  remainderWallet: this.getRemainderWallet(),
551
639
  cellSlug: this.getCellSlug(),
552
640
  version: this.getServerSdkVersion(),
553
- continuIdPosition
641
+ continuIdPosition,
642
+ mlKemParameterSet: this.getMlKemParameterSet()
554
643
  })
555
644
  }
556
645
 
@@ -611,8 +700,9 @@ export default class KnishIOClient {
611
700
  // Guard with $__authInProcess to prevent concurrent auth requests
612
701
  if (this.$__authToken && this.$__authToken.isExpired() && !this.$__authInProcess) {
613
702
  this.log('info', 'KnishIOClient::executeQuery() - Access token is expired. Getting new one...')
703
+ const authSecret = this.$__secret || (await this.retrieveSecret()) || ''
614
704
  await this.requestAuthToken({
615
- secret: this.$__secret,
705
+ secret: authSecret,
616
706
  cellSlug: this.$__cellSlug,
617
707
  encrypt: this.$__encrypt
618
708
  })
@@ -1178,7 +1268,8 @@ export default class KnishIOClient {
1178
1268
  }) {
1179
1269
  const newWallet = new Wallet({
1180
1270
  secret: this.getSecret(),
1181
- token
1271
+ token,
1272
+ mlKemParameterSet: this.getMlKemParameterSet()
1182
1273
  })
1183
1274
 
1184
1275
  /**
@@ -1310,7 +1401,8 @@ export default class KnishIOClient {
1310
1401
  secret: this.getSecret(),
1311
1402
  bundle: this.getBundle(),
1312
1403
  token,
1313
- batchId
1404
+ batchId,
1405
+ mlKemParameterSet: this.getMlKemParameterSet()
1314
1406
  })
1315
1407
 
1316
1408
  /**
@@ -1724,7 +1816,8 @@ export default class KnishIOClient {
1724
1816
  } else {
1725
1817
  to = Wallet.create({
1726
1818
  secret: to,
1727
- token
1819
+ token,
1820
+ mlKemParameterSet: this.getMlKemParameterSet()
1728
1821
  })
1729
1822
  }
1730
1823
  }
@@ -1876,7 +1969,8 @@ export default class KnishIOClient {
1876
1969
  // Attempt to get the recipient's wallet, if not provided
1877
1970
  const recipientWallet = Wallet.create({
1878
1971
  bundle: bundleHash,
1879
- token
1972
+ token,
1973
+ mlKemParameterSet: this.getMlKemParameterSet()
1880
1974
  })
1881
1975
 
1882
1976
  // Compute the batch ID for the recipient
@@ -1976,7 +2070,8 @@ export default class KnishIOClient {
1976
2070
  const recipientWallets = recipients.map(recipient => {
1977
2071
  const recipientWallet = Wallet.create({
1978
2072
  bundle: recipient.bundleHash,
1979
- token
2073
+ token,
2074
+ mlKemParameterSet: this.getMlKemParameterSet()
1980
2075
  })
1981
2076
 
1982
2077
  // Compute the batch ID for the recipient (typically used by stackable tokens)
@@ -2275,7 +2370,8 @@ export default class KnishIOClient {
2275
2370
  // Generate new recipient wallet if only recipient secret has been passed
2276
2371
  const recipientWallet = Wallet.create({
2277
2372
  bundle: bundleHash,
2278
- token: tokenSlug
2373
+ token: tokenSlug,
2374
+ mlKemParameterSet: this.getMlKemParameterSet()
2279
2375
  })
2280
2376
 
2281
2377
  // Set batch ID
@@ -2331,7 +2427,8 @@ export default class KnishIOClient {
2331
2427
  // Create a wallet for encryption
2332
2428
  const wallet = new Wallet({
2333
2429
  secret: generateSecret(await this.getFingerprint()),
2334
- token: 'AUTH'
2430
+ token: 'AUTH',
2431
+ mlKemParameterSet: this.getMlKemParameterSet()
2335
2432
  })
2336
2433
 
2337
2434
  /**
@@ -2384,7 +2481,8 @@ export default class KnishIOClient {
2384
2481
  // Generate a signing wallet
2385
2482
  const wallet = new Wallet({
2386
2483
  secret,
2387
- token: 'AUTH'
2484
+ token: 'AUTH',
2485
+ mlKemParameterSet: this.getMlKemParameterSet()
2388
2486
  })
2389
2487
 
2390
2488
  // Create a wallet with a signing wallet
@@ -2401,7 +2499,7 @@ export default class KnishIOClient {
2401
2499
  molecule
2402
2500
  })
2403
2501
 
2404
- // PQ-transport Phase E (cycle 163): convey the AUTH source wallet's ML-KEM768 public key as a
2502
+ // PQ-transport Phase E (cycle 163): convey the AUTH source wallet's ML-KEM public key as a
2405
2503
  // SIGNED `walletPubkey` meta on the U-atom (fillMolecule → initAuthorization → sign), so the
2406
2504
  // validator can encrypt CipherHash responses back to THIS wallet (the one that decrypts them).
2407
2505
  // Signed → tamper-proof. Only when present (PQ-capable wallet).
@@ -2464,6 +2562,11 @@ export default class KnishIOClient {
2464
2562
  this.setCellSlug(cellSlug)
2465
2563
  }
2466
2564
 
2565
+ // Retrieve secret from storage provider if available
2566
+ if (secret === null && this.$__secretStorage && this.$__bundle) {
2567
+ secret = await this.$__secretStorage.retrieveSecret(this.$__bundle)
2568
+ }
2569
+
2467
2570
  // Auth in process...
2468
2571
  this.$__authInProcess = true
2469
2572
 
package/src/Molecule.js CHANGED
@@ -87,8 +87,10 @@ export default class Molecule {
87
87
  remainderWallet = null,
88
88
  cellSlug = null,
89
89
  version = null,
90
- continuIdPosition = null
90
+ continuIdPosition = null,
91
+ mlKemParameterSet = null
91
92
  }) {
93
+ this.mlKemParameterSet = mlKemParameterSet || (sourceWallet && sourceWallet.mlKemParameterSet) || 1024
92
94
  this.status = null
93
95
  this.molecularHash = null
94
96
  this.createdAt = String(+new Date())
@@ -110,7 +112,8 @@ export default class Molecule {
110
112
  bundle,
111
113
  token: sourceWallet.token,
112
114
  batchId: sourceWallet.batchId,
113
- characters: sourceWallet.characters
115
+ characters: sourceWallet.characters,
116
+ mlKemParameterSet: this.mlKemParameterSet
114
117
  })
115
118
  }
116
119
  }
@@ -343,7 +346,8 @@ export default class Molecule {
343
346
  if (!this.remainderWallet || this.remainderWallet.token !== 'USER') {
344
347
  this.remainderWallet = Wallet.create({
345
348
  secret: this.secret,
346
- bundle: this.bundle
349
+ bundle: this.bundle,
350
+ mlKemParameterSet: this.mlKemParameterSet
347
351
  })
348
352
  }
349
353
 
@@ -479,7 +483,8 @@ export default class Molecule {
479
483
  // Create burn address wallet (null bundle = token destruction)
480
484
  const burnWallet = new Wallet({
481
485
  bundle: '0000000000000000000000000000000000000000000000000000000000000000',
482
- token: this.sourceWallet.token
486
+ token: this.sourceWallet.token,
487
+ mlKemParameterSet: this.mlKemParameterSet
483
488
  })
484
489
 
485
490
  // V-atom 1: Debit full balance from source
@@ -685,7 +690,8 @@ export default class Molecule {
685
690
  secret: this.secret,
686
691
  bundle: this.bundle,
687
692
  token: this.sourceWallet.token,
688
- batchId: this.sourceWallet.batchId
693
+ batchId: this.sourceWallet.batchId,
694
+ mlKemParameterSet: this.mlKemParameterSet
689
695
  })
690
696
  bufferWallet.tradeRates = tradeRates
691
697
 
@@ -1080,7 +1086,7 @@ export default class Molecule {
1080
1086
  bundle = null,
1081
1087
  anonymous = false,
1082
1088
  compressed = true
1083
- }) {
1089
+ } = {}) {
1084
1090
  // Do we have atoms?
1085
1091
  if (this.atoms.length === 0 || this.atoms.filter(atom => !(atom instanceof Atom)).length !== 0) {
1086
1092
  throw new AtomsMissingException()
@@ -1329,7 +1335,8 @@ export default class Molecule {
1329
1335
  position: data.sourceWallet.position,
1330
1336
  bundle: data.sourceWallet.bundle,
1331
1337
  batchId: data.sourceWallet.batchId,
1332
- characters: data.sourceWallet.characters
1338
+ characters: data.sourceWallet.characters,
1339
+ mlKemParameterSet: molecule.mlKemParameterSet
1333
1340
  })
1334
1341
 
1335
1342
  // Set additional properties for validation context
@@ -1351,7 +1358,8 @@ export default class Molecule {
1351
1358
  position: data.remainderWallet.position,
1352
1359
  bundle: data.remainderWallet.bundle,
1353
1360
  batchId: data.remainderWallet.batchId,
1354
- characters: data.remainderWallet.characters
1361
+ characters: data.remainderWallet.characters,
1362
+ mlKemParameterSet: molecule.mlKemParameterSet
1355
1363
  })
1356
1364
 
1357
1365
  // Set additional properties for validation context