@agirails/sdk 2.5.2 → 2.5.4

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.
Files changed (172) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +67 -22
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +12 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +30 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +45 -11
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/cli/utils/client.d.ts.map +1 -1
  20. package/dist/cli/utils/client.js +1 -0
  21. package/dist/cli/utils/client.js.map +1 -1
  22. package/dist/config/networks.d.ts +2 -2
  23. package/dist/config/networks.d.ts.map +1 -1
  24. package/dist/config/networks.js +27 -22
  25. package/dist/config/networks.js.map +1 -1
  26. package/dist/level0/request.d.ts.map +1 -1
  27. package/dist/level0/request.js +2 -1
  28. package/dist/level0/request.js.map +1 -1
  29. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  30. package/dist/runtime/BlockchainRuntime.js +11 -5
  31. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  32. package/dist/runtime/MockStateManager.d.ts.map +1 -1
  33. package/dist/runtime/MockStateManager.js +2 -1
  34. package/dist/runtime/MockStateManager.js.map +1 -1
  35. package/dist/utils/IPFSClient.d.ts +3 -1
  36. package/dist/utils/IPFSClient.d.ts.map +1 -1
  37. package/dist/utils/IPFSClient.js +27 -7
  38. package/dist/utils/IPFSClient.js.map +1 -1
  39. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  40. package/dist/wallet/AutoWalletProvider.js +52 -18
  41. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  42. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  43. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  44. package/dist/wallet/SmartWalletRouter.js +212 -0
  45. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  46. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  47. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  48. package/dist/wallet/aa/DualNonceManager.js +100 -5
  49. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  50. package/package.json +3 -6
  51. package/src/ACTPClient.ts +0 -1579
  52. package/src/abi/ACTPKernel.json +0 -1356
  53. package/src/abi/AgentRegistry.json +0 -915
  54. package/src/abi/ERC20.json +0 -40
  55. package/src/abi/EscrowVault.json +0 -134
  56. package/src/abi/IdentityRegistry.json +0 -316
  57. package/src/adapters/AdapterRegistry.ts +0 -173
  58. package/src/adapters/AdapterRouter.ts +0 -416
  59. package/src/adapters/BaseAdapter.ts +0 -498
  60. package/src/adapters/BasicAdapter.ts +0 -514
  61. package/src/adapters/IAdapter.ts +0 -292
  62. package/src/adapters/StandardAdapter.ts +0 -555
  63. package/src/adapters/X402Adapter.ts +0 -731
  64. package/src/adapters/index.ts +0 -60
  65. package/src/builders/DeliveryProofBuilder.ts +0 -327
  66. package/src/builders/QuoteBuilder.ts +0 -483
  67. package/src/builders/index.ts +0 -17
  68. package/src/cli/commands/balance.ts +0 -110
  69. package/src/cli/commands/batch.ts +0 -487
  70. package/src/cli/commands/config.ts +0 -231
  71. package/src/cli/commands/deploy-check.ts +0 -364
  72. package/src/cli/commands/deploy-env.ts +0 -120
  73. package/src/cli/commands/diff.ts +0 -141
  74. package/src/cli/commands/init.ts +0 -469
  75. package/src/cli/commands/mint.ts +0 -116
  76. package/src/cli/commands/pay.ts +0 -113
  77. package/src/cli/commands/publish.ts +0 -475
  78. package/src/cli/commands/pull.ts +0 -124
  79. package/src/cli/commands/register.ts +0 -247
  80. package/src/cli/commands/simulate.ts +0 -345
  81. package/src/cli/commands/time.ts +0 -302
  82. package/src/cli/commands/tx.ts +0 -448
  83. package/src/cli/commands/watch.ts +0 -211
  84. package/src/cli/index.ts +0 -134
  85. package/src/cli/utils/client.ts +0 -251
  86. package/src/cli/utils/config.ts +0 -389
  87. package/src/cli/utils/output.ts +0 -465
  88. package/src/cli/utils/wallet.ts +0 -109
  89. package/src/config/agirailsmd.ts +0 -262
  90. package/src/config/networks.ts +0 -275
  91. package/src/config/pendingPublish.ts +0 -237
  92. package/src/config/publishPipeline.ts +0 -359
  93. package/src/config/syncOperations.ts +0 -279
  94. package/src/erc8004/ERC8004Bridge.ts +0 -462
  95. package/src/erc8004/ReputationReporter.ts +0 -468
  96. package/src/erc8004/index.ts +0 -61
  97. package/src/errors/index.ts +0 -427
  98. package/src/index.ts +0 -364
  99. package/src/level0/Provider.ts +0 -117
  100. package/src/level0/ServiceDirectory.ts +0 -131
  101. package/src/level0/index.ts +0 -10
  102. package/src/level0/provide.ts +0 -132
  103. package/src/level0/request.ts +0 -432
  104. package/src/level1/Agent.ts +0 -1426
  105. package/src/level1/index.ts +0 -10
  106. package/src/level1/pricing/PriceCalculator.ts +0 -255
  107. package/src/level1/pricing/PricingStrategy.ts +0 -198
  108. package/src/level1/types/Job.ts +0 -179
  109. package/src/level1/types/Options.ts +0 -291
  110. package/src/level1/types/index.ts +0 -8
  111. package/src/protocol/ACTPKernel.ts +0 -808
  112. package/src/protocol/AgentRegistry.ts +0 -559
  113. package/src/protocol/DIDManager.ts +0 -629
  114. package/src/protocol/DIDResolver.ts +0 -554
  115. package/src/protocol/EASHelper.ts +0 -378
  116. package/src/protocol/EscrowVault.ts +0 -255
  117. package/src/protocol/EventMonitor.ts +0 -204
  118. package/src/protocol/MessageSigner.ts +0 -510
  119. package/src/protocol/ProofGenerator.ts +0 -339
  120. package/src/protocol/QuoteBuilder.ts +0 -15
  121. package/src/registry/AgentRegistryClient.ts +0 -202
  122. package/src/runtime/BlockchainRuntime.ts +0 -1015
  123. package/src/runtime/IACTPRuntime.ts +0 -306
  124. package/src/runtime/MockRuntime.ts +0 -1298
  125. package/src/runtime/MockStateManager.ts +0 -576
  126. package/src/runtime/index.ts +0 -25
  127. package/src/runtime/types/MockState.ts +0 -237
  128. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  129. package/src/storage/ArweaveClient.ts +0 -946
  130. package/src/storage/FilebaseClient.ts +0 -790
  131. package/src/storage/index.ts +0 -96
  132. package/src/storage/types.ts +0 -348
  133. package/src/types/adapter.ts +0 -310
  134. package/src/types/agent.ts +0 -79
  135. package/src/types/did.ts +0 -223
  136. package/src/types/eip712.ts +0 -175
  137. package/src/types/erc8004.ts +0 -293
  138. package/src/types/escrow.ts +0 -27
  139. package/src/types/index.ts +0 -17
  140. package/src/types/message.ts +0 -145
  141. package/src/types/state.ts +0 -87
  142. package/src/types/transaction.ts +0 -69
  143. package/src/types/x402.ts +0 -251
  144. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  145. package/src/utils/Helpers.ts +0 -688
  146. package/src/utils/IPFSClient.ts +0 -368
  147. package/src/utils/Logger.ts +0 -484
  148. package/src/utils/NonceManager.ts +0 -591
  149. package/src/utils/RateLimiter.ts +0 -534
  150. package/src/utils/ReceivedNonceTracker.ts +0 -567
  151. package/src/utils/SDKLifecycle.ts +0 -416
  152. package/src/utils/SecureNonce.ts +0 -78
  153. package/src/utils/Semaphore.ts +0 -276
  154. package/src/utils/UsedAttestationTracker.ts +0 -385
  155. package/src/utils/canonicalJson.ts +0 -38
  156. package/src/utils/circuitBreaker.ts +0 -324
  157. package/src/utils/computeTypeHash.ts +0 -48
  158. package/src/utils/fsSafe.ts +0 -80
  159. package/src/utils/index.ts +0 -80
  160. package/src/utils/retry.ts +0 -364
  161. package/src/utils/security.ts +0 -418
  162. package/src/utils/validation.ts +0 -540
  163. package/src/wallet/AutoWalletProvider.ts +0 -299
  164. package/src/wallet/EOAWalletProvider.ts +0 -69
  165. package/src/wallet/IWalletProvider.ts +0 -135
  166. package/src/wallet/aa/BundlerClient.ts +0 -274
  167. package/src/wallet/aa/DualNonceManager.ts +0 -173
  168. package/src/wallet/aa/PaymasterClient.ts +0 -174
  169. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  170. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  171. package/src/wallet/aa/constants.ts +0 -60
  172. package/src/wallet/keystore.ts +0 -240
@@ -1,591 +0,0 @@
1
- /**
2
- * Nonce Manager Implementation
3
- * Tracks nonces per DID + message type for AIP-4 delivery proofs
4
- * Reference: AIP-4 §3.2 (nonce field requirement)
5
- *
6
- * SECURITY FIXES:
7
- * - C-2: Added atomic nonce allocation with locking
8
- * - H-1: Added persistent nonce storage option
9
- * - H-5: Added nonce upper bound validation
10
- */
11
-
12
- import { assertSafeFileForRead, ensureSafeDir, ensureSafeFile } from './fsSafe';
13
- import { sdkLogger } from './Logger';
14
-
15
- /**
16
- * Maximum allowed nonce value.
17
- * SECURITY FIX (H-5): Prevents nonce overflow attacks.
18
- * Using Number.MAX_SAFE_INTEGER (2^53 - 1) to ensure safe JavaScript integer operations.
19
- */
20
- export const MAX_NONCE_VALUE = Number.MAX_SAFE_INTEGER;
21
-
22
- /**
23
- * Nonce Manager Interface (from DeliveryProofBuilder)
24
- */
25
- export interface NonceManager {
26
- /**
27
- * Get next nonce for message type
28
- * @param messageType - Message type identifier (e.g., "agirails.delivery.v1")
29
- * @returns Monotonically increasing nonce
30
- */
31
- getNextNonce(messageType: string): number;
32
-
33
- /**
34
- * Record nonce usage
35
- * @param messageType - Message type identifier
36
- * @param nonce - Nonce used
37
- */
38
- recordNonce(messageType: string, nonce: number): void;
39
-
40
- /**
41
- * Get current nonce (last used)
42
- * @param messageType - Message type identifier
43
- * @returns Current nonce or 0 if none used
44
- */
45
- getCurrentNonce(messageType: string): number;
46
-
47
- /**
48
- * Reset nonce for message type
49
- * @param messageType - Message type identifier
50
- */
51
- resetNonce(messageType: string): void;
52
- }
53
-
54
- /**
55
- * In-Memory Nonce Manager
56
- * Simple implementation using Map for per-message-type nonce tracking
57
- *
58
- * SECURITY FIXES:
59
- * - C-2: Added atomic getAndIncrementNonce() to prevent race conditions
60
- * - H-5: Added nonce upper bound validation
61
- *
62
- * ⚠️ WARNING: Nonces are lost on process restart. For production:
63
- * - Use persistent storage (Redis, PostgreSQL, etc.)
64
- * - Implement nonce recovery from blockchain events
65
- * - Add DID-scoped nonce tracking
66
- */
67
- export class InMemoryNonceManager implements NonceManager {
68
- private nonces: Map<string, number> = new Map();
69
- // SECURITY FIX (C-2): Mutex for atomic nonce operations
70
- // Store both the promise and its resolver for proper lock release
71
- private locks: Map<string, { promise: Promise<void>; resolve: () => void }> = new Map();
72
-
73
- /**
74
- * Create in-memory nonce manager
75
- * @param initialNonces - Optional initial nonce values (for recovery)
76
- */
77
- constructor(initialNonces?: Record<string, number>) {
78
- if (initialNonces) {
79
- Object.entries(initialNonces).forEach(([messageType, nonce]) => {
80
- // SECURITY FIX (H-5): Validate initial nonces
81
- if (nonce > MAX_NONCE_VALUE) {
82
- throw new Error(
83
- `Initial nonce ${nonce} for ${messageType} exceeds maximum allowed value ${MAX_NONCE_VALUE}`
84
- );
85
- }
86
- this.nonces.set(messageType, nonce);
87
- });
88
- }
89
- }
90
-
91
- /**
92
- * SECURITY FIX (C-2 + DEADLOCK-FIX): Acquire lock for message type
93
- * Ensures atomic nonce operations.
94
- *
95
- * FIXED: Previous implementation had a deadlock bug where:
96
- * - The resolver was stored in a closure but never accessible to releaseLock()
97
- * - releaseLock() just deleted the entry without resolving waiting Promises
98
- *
99
- * New implementation stores both promise AND resolver together.
100
- */
101
- private async acquireLock(messageType: string): Promise<void> {
102
- // Wait for any existing lock to be released
103
- while (this.locks.has(messageType)) {
104
- const existingLock = this.locks.get(messageType);
105
- if (existingLock) {
106
- await existingLock.promise;
107
- }
108
- }
109
-
110
- // Create new lock with stored resolver
111
- let resolver: () => void = () => {};
112
- const lockPromise = new Promise<void>((resolve) => {
113
- resolver = resolve;
114
- });
115
- this.locks.set(messageType, { promise: lockPromise, resolve: resolver });
116
- }
117
-
118
- /**
119
- * SECURITY FIX (C-2 + DEADLOCK-FIX): Release lock for message type
120
- *
121
- * FIXED: Now properly resolves the Promise before deleting,
122
- * so any waiting acquireLock() calls can proceed.
123
- */
124
- private releaseLock(messageType: string): void {
125
- const lock = this.locks.get(messageType);
126
- if (lock) {
127
- lock.resolve(); // Resolve the promise first
128
- this.locks.delete(messageType); // Then delete the entry
129
- }
130
- }
131
-
132
- /**
133
- * Get next nonce for message type
134
- * @param messageType - Message type identifier
135
- * @returns Monotonically increasing nonce
136
- */
137
- getNextNonce(messageType: string): number {
138
- const current = this.nonces.get(messageType) || 0;
139
- const next = current + 1;
140
-
141
- // SECURITY FIX (H-5): Check upper bound
142
- if (next > MAX_NONCE_VALUE) {
143
- throw new Error(
144
- `Nonce overflow: next nonce ${next} exceeds maximum allowed value ${MAX_NONCE_VALUE}. ` +
145
- `Consider resetting nonces or using a larger storage type.`
146
- );
147
- }
148
-
149
- return next;
150
- }
151
-
152
- /**
153
- * SECURITY FIX (C-2): Atomic get-and-increment nonce
154
- * Returns the next nonce and records it atomically to prevent race conditions.
155
- *
156
- * @param messageType - Message type identifier
157
- * @returns Atomically allocated nonce
158
- */
159
- async getAndIncrementNonce(messageType: string): Promise<number> {
160
- await this.acquireLock(messageType);
161
- try {
162
- const current = this.nonces.get(messageType) || 0;
163
- const next = current + 1;
164
-
165
- // SECURITY FIX (H-5): Check upper bound
166
- if (next > MAX_NONCE_VALUE) {
167
- throw new Error(
168
- `Nonce overflow: next nonce ${next} exceeds maximum allowed value ${MAX_NONCE_VALUE}`
169
- );
170
- }
171
-
172
- this.nonces.set(messageType, next);
173
- return next;
174
- } finally {
175
- this.releaseLock(messageType);
176
- }
177
- }
178
-
179
- /**
180
- * Record nonce usage
181
- * @param messageType - Message type identifier
182
- * @param nonce - Nonce used
183
- */
184
- recordNonce(messageType: string, nonce: number): void {
185
- const current = this.nonces.get(messageType) || 0;
186
-
187
- // SECURITY FIX (H-5): Check upper bound
188
- if (nonce > MAX_NONCE_VALUE) {
189
- throw new Error(
190
- `Nonce ${nonce} exceeds maximum allowed value ${MAX_NONCE_VALUE}`
191
- );
192
- }
193
-
194
- // Ensure monotonic increase
195
- if (nonce <= current) {
196
- throw new Error(
197
- `Nonce must be strictly increasing: attempted ${nonce}, current is ${current}`
198
- );
199
- }
200
-
201
- this.nonces.set(messageType, nonce);
202
- }
203
-
204
- /**
205
- * Get current nonce (last used)
206
- * @param messageType - Message type identifier
207
- * @returns Current nonce or 0 if none used
208
- */
209
- getCurrentNonce(messageType: string): number {
210
- return this.nonces.get(messageType) || 0;
211
- }
212
-
213
- /**
214
- * Reset nonce for message type
215
- * @param messageType - Message type identifier
216
- */
217
- resetNonce(messageType: string): void {
218
- this.nonces.delete(messageType);
219
- }
220
-
221
- /**
222
- * Get all nonces (for persistence)
223
- * @returns Record of all message type nonces
224
- */
225
- getAllNonces(): Record<string, number> {
226
- return Object.fromEntries(this.nonces.entries());
227
- }
228
-
229
- /**
230
- * Clear all nonces
231
- */
232
- clearAll(): void {
233
- this.nonces.clear();
234
- }
235
- }
236
-
237
- /**
238
- * DID-Scoped Nonce Manager
239
- * Tracks nonces per DID + message type combination
240
- * Recommended for multi-agent scenarios
241
- */
242
- export class DIDScopedNonceManager implements NonceManager {
243
- private nonces: Map<string, Map<string, number>> = new Map();
244
- private currentDID: string;
245
-
246
- /**
247
- * Create DID-scoped nonce manager
248
- * @param did - DID to track nonces for
249
- * @param initialNonces - Optional initial nonce values
250
- */
251
- constructor(did: string, initialNonces?: Record<string, number>) {
252
- this.currentDID = did;
253
-
254
- if (initialNonces) {
255
- const didNonces = new Map<string, number>();
256
- Object.entries(initialNonces).forEach(([messageType, nonce]) => {
257
- didNonces.set(messageType, nonce);
258
- });
259
- this.nonces.set(did, didNonces);
260
- }
261
- }
262
-
263
- /**
264
- * Get next nonce for message type (current DID)
265
- * @param messageType - Message type identifier
266
- * @returns Monotonically increasing nonce
267
- */
268
- getNextNonce(messageType: string): number {
269
- return this.getNextNonceForDID(this.currentDID, messageType);
270
- }
271
-
272
- /**
273
- * Record nonce usage (current DID)
274
- * @param messageType - Message type identifier
275
- * @param nonce - Nonce used
276
- */
277
- recordNonce(messageType: string, nonce: number): void {
278
- this.recordNonceForDID(this.currentDID, messageType, nonce);
279
- }
280
-
281
- /**
282
- * Get current nonce (current DID)
283
- * @param messageType - Message type identifier
284
- * @returns Current nonce or 0 if none used
285
- */
286
- getCurrentNonce(messageType: string): number {
287
- return this.getCurrentNonceForDID(this.currentDID, messageType);
288
- }
289
-
290
- /**
291
- * Reset nonce for message type (current DID)
292
- * @param messageType - Message type identifier
293
- */
294
- resetNonce(messageType: string): void {
295
- this.resetNonceForDID(this.currentDID, messageType);
296
- }
297
-
298
- /**
299
- * Get next nonce for specific DID + message type
300
- * @param did - DID identifier
301
- * @param messageType - Message type identifier
302
- * @returns Monotonically increasing nonce
303
- */
304
- getNextNonceForDID(did: string, messageType: string): number {
305
- const didNonces = this.nonces.get(did);
306
- if (!didNonces) {
307
- return 1;
308
- }
309
- const current = didNonces.get(messageType) || 0;
310
- return current + 1;
311
- }
312
-
313
- /**
314
- * Record nonce usage for specific DID + message type
315
- * @param did - DID identifier
316
- * @param messageType - Message type identifier
317
- * @param nonce - Nonce used
318
- */
319
- recordNonceForDID(did: string, messageType: string, nonce: number): void {
320
- let didNonces = this.nonces.get(did);
321
-
322
- if (!didNonces) {
323
- didNonces = new Map<string, number>();
324
- this.nonces.set(did, didNonces);
325
- }
326
-
327
- const current = didNonces.get(messageType) || 0;
328
-
329
- // Ensure monotonic increase
330
- if (nonce <= current) {
331
- throw new Error(
332
- `Nonce must be strictly increasing for ${did}: attempted ${nonce}, current is ${current}`
333
- );
334
- }
335
-
336
- didNonces.set(messageType, nonce);
337
- }
338
-
339
- /**
340
- * Get current nonce for specific DID + message type
341
- * @param did - DID identifier
342
- * @param messageType - Message type identifier
343
- * @returns Current nonce or 0 if none used
344
- */
345
- getCurrentNonceForDID(did: string, messageType: string): number {
346
- const didNonces = this.nonces.get(did);
347
- return didNonces?.get(messageType) || 0;
348
- }
349
-
350
- /**
351
- * Reset nonce for specific DID + message type
352
- * @param did - DID identifier
353
- * @param messageType - Message type identifier
354
- */
355
- resetNonceForDID(did: string, messageType: string): void {
356
- const didNonces = this.nonces.get(did);
357
- if (didNonces) {
358
- didNonces.delete(messageType);
359
- }
360
- }
361
-
362
- /**
363
- * Switch current DID context
364
- * @param did - New DID to track
365
- */
366
- switchDID(did: string): void {
367
- this.currentDID = did;
368
- }
369
-
370
- /**
371
- * Get all nonces for all DIDs (for persistence)
372
- * @returns Nested record of DID → message type → nonce
373
- */
374
- getAllNonces(): Record<string, Record<string, number>> {
375
- const result: Record<string, Record<string, number>> = {};
376
-
377
- this.nonces.forEach((didNonces, did) => {
378
- result[did] = Object.fromEntries(didNonces.entries());
379
- });
380
-
381
- return result;
382
- }
383
-
384
- /**
385
- * Clear all nonces for all DIDs
386
- */
387
- clearAll(): void {
388
- this.nonces.clear();
389
- }
390
- }
391
-
392
- /**
393
- * File-based Nonce Manager for Persistent Storage
394
- *
395
- * SECURITY FIX (H-1): Persists nonces to disk to survive process restarts.
396
- * SECURITY FIX (NEW-H-4): File locking to prevent concurrent write corruption.
397
- * Uses atomic file writes (temp file + rename) for crash safety.
398
- *
399
- * @module utils/NonceManager
400
- */
401
- export class FileBasedNonceManager implements NonceManager {
402
- private inMemory: InMemoryNonceManager;
403
- private filePath: string;
404
- private fs: typeof import('fs');
405
- private path: typeof import('path');
406
- private lockfile: typeof import('proper-lockfile');
407
-
408
- /**
409
- * Create file-based nonce manager
410
- * @param stateDirectory - Directory to store nonces file
411
- */
412
- constructor(stateDirectory: string) {
413
- this.fs = require('fs');
414
- this.path = require('path');
415
- // SECURITY FIX (NEW-H-4): File locking to prevent race conditions
416
- this.lockfile = require('proper-lockfile');
417
-
418
- // Ensure .actp directory exists
419
- const actpDir = this.path.join(stateDirectory, '.actp');
420
- ensureSafeDir(actpDir, 0o755);
421
-
422
- this.filePath = this.path.join(actpDir, 'nonces.json');
423
-
424
- // Load existing nonces
425
- const initialNonces = this.loadFromFile();
426
- this.inMemory = new InMemoryNonceManager(initialNonces);
427
- }
428
-
429
- /**
430
- * Load nonces from file
431
- */
432
- private loadFromFile(): Record<string, number> | undefined {
433
- if (!this.fs.existsSync(this.filePath)) {
434
- return undefined;
435
- }
436
-
437
- try {
438
- // SECURITY: Refuse to read from symlinked nonce files
439
- assertSafeFileForRead(this.filePath);
440
-
441
- const MAX_NONCE_FILE_SIZE = 5 * 1024 * 1024; // 5MB
442
- const st = this.fs.statSync(this.filePath);
443
- if (st.size > MAX_NONCE_FILE_SIZE) {
444
- throw new Error(
445
- `nonces.json exceeds ${MAX_NONCE_FILE_SIZE / 1024 / 1024}MB limit: ${this.filePath}`
446
- );
447
- }
448
-
449
- const data = JSON.parse(this.fs.readFileSync(this.filePath, 'utf-8'));
450
- return data as Record<string, number>;
451
- } catch (e: any) {
452
- // Fail closed: nonce resets can enable replay.
453
- throw new Error(
454
- `Failed to parse nonces.json (replay protection would be weakened). ` +
455
- `Fix/delete the file: ${this.filePath}. Error: ${e?.message || String(e)}`
456
- );
457
- }
458
- }
459
-
460
- /**
461
- * Save nonces to file atomically with file locking
462
- *
463
- * SECURITY FIX (NEW-H-4): File locking prevents concurrent write corruption
464
- */
465
- private async saveToFile(): Promise<void> {
466
- const data = this.inMemory.getAllNonces();
467
- const tempPath = `${this.filePath}.tmp`;
468
-
469
- // SECURITY FIX: Ensure file exists before locking (proper-lockfile requirement)
470
- ensureSafeFile(this.filePath, '{}', 0o644);
471
-
472
- // SECURITY FIX (NEW-H-4): Acquire file lock before writing
473
- let release: (() => Promise<void>) | null = null;
474
- try {
475
- release = await this.lockfile.lock(this.filePath, {
476
- stale: 10000, // Lock expires after 10 seconds if process crashes
477
- retries: {
478
- retries: 5,
479
- minTimeout: 100,
480
- maxTimeout: 500
481
- }
482
- });
483
-
484
- // Atomic write: temp file + rename
485
- if (this.fs.existsSync(tempPath)) {
486
- this.fs.unlinkSync(tempPath);
487
- }
488
- this.fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), {
489
- encoding: 'utf-8',
490
- mode: 0o644,
491
- flag: 'wx'
492
- });
493
- this.fs.renameSync(tempPath, this.filePath);
494
- } catch (error) {
495
- // Clean up temp file on error
496
- if (this.fs.existsSync(tempPath)) {
497
- try {
498
- this.fs.unlinkSync(tempPath);
499
- } catch {
500
- // Ignore cleanup errors
501
- }
502
- }
503
- throw error;
504
- } finally {
505
- if (release) {
506
- await release();
507
- }
508
- }
509
- }
510
-
511
- getNextNonce(messageType: string): number {
512
- return this.inMemory.getNextNonce(messageType);
513
- }
514
-
515
- /**
516
- * Atomic get and increment with persistence
517
- */
518
- async getAndIncrementNonce(messageType: string): Promise<number> {
519
- const nonce = await this.inMemory.getAndIncrementNonce(messageType);
520
- // SECURITY FIX (NEW-H-4): saveToFile is now async
521
- await this.saveToFile();
522
- return nonce;
523
- }
524
-
525
- recordNonce(messageType: string, nonce: number): void {
526
- this.inMemory.recordNonce(messageType, nonce);
527
- // Fire-and-forget to maintain sync interface
528
- this.saveToFile().catch((err) => {
529
- sdkLogger.error('Failed to save nonce manager state', { error: err instanceof Error ? err.message : String(err) });
530
- });
531
- }
532
-
533
- getCurrentNonce(messageType: string): number {
534
- return this.inMemory.getCurrentNonce(messageType);
535
- }
536
-
537
- resetNonce(messageType: string): void {
538
- this.inMemory.resetNonce(messageType);
539
- // Fire-and-forget to maintain sync interface
540
- this.saveToFile().catch((err) => {
541
- sdkLogger.error('Failed to save nonce manager state', { error: err instanceof Error ? err.message : String(err) });
542
- });
543
- }
544
-
545
- getAllNonces(): Record<string, number> {
546
- return this.inMemory.getAllNonces();
547
- }
548
-
549
- clearAll(): void {
550
- this.inMemory.clearAll();
551
- if (this.fs.existsSync(this.filePath)) {
552
- this.fs.unlinkSync(this.filePath);
553
- }
554
- }
555
- }
556
-
557
- /**
558
- * Create nonce manager based on environment
559
- * @param options - Configuration options
560
- * @returns NonceManager instance
561
- *
562
- * @example
563
- * ```typescript
564
- * // In-memory (default)
565
- * const manager = createNonceManager();
566
- *
567
- * // DID-scoped
568
- * const manager = createNonceManager({ did: 'did:ethr:0x...' });
569
- *
570
- * // Persistent (survives restarts)
571
- * const manager = createNonceManager({ stateDirectory: '/path/to/project' });
572
- * ```
573
- */
574
- export function createNonceManager(
575
- options?: {
576
- did?: string;
577
- initialNonces?: Record<string, number>;
578
- stateDirectory?: string;
579
- }
580
- ): NonceManager {
581
- // SECURITY FIX (H-1): Support persistent storage
582
- if (options?.stateDirectory) {
583
- return new FileBasedNonceManager(options.stateDirectory);
584
- }
585
-
586
- if (options?.did) {
587
- return new DIDScopedNonceManager(options.did, options?.initialNonces);
588
- }
589
-
590
- return new InMemoryNonceManager(options?.initialNonces);
591
- }