@rainfall-devkit/sdk 0.2.1 → 0.2.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.
@@ -0,0 +1,269 @@
1
+ // src/security/edge-node.ts
2
+ import * as sodium from "libsodium-wrappers-sumo";
3
+ var EdgeNodeSecurity = class {
4
+ sodiumReady;
5
+ backendSecret;
6
+ keyPair;
7
+ constructor(options = {}) {
8
+ this.sodiumReady = sodium.ready;
9
+ this.backendSecret = options.backendSecret;
10
+ this.keyPair = options.keyPair;
11
+ }
12
+ /**
13
+ * Initialize libsodium
14
+ */
15
+ async initialize() {
16
+ await this.sodiumReady;
17
+ }
18
+ // ============================================================================
19
+ // JWT Token Management
20
+ // ============================================================================
21
+ /**
22
+ * Generate a JWT token for an edge node
23
+ * Note: In production, this is done by the backend. This is for testing.
24
+ */
25
+ generateJWT(edgeNodeId, subscriberId, expiresInDays = 30) {
26
+ if (!this.backendSecret) {
27
+ throw new Error("Backend secret not configured");
28
+ }
29
+ const now = Math.floor(Date.now() / 1e3);
30
+ const exp = now + expiresInDays * 24 * 60 * 60;
31
+ const jti = this.generateTokenId();
32
+ const payload = {
33
+ sub: edgeNodeId,
34
+ iss: "rainfall-backend",
35
+ iat: now,
36
+ exp,
37
+ jti,
38
+ scope: ["edge:heartbeat", "edge:claim", "edge:submit", "edge:queue"]
39
+ };
40
+ const header = { alg: "HS256", typ: "JWT" };
41
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
42
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
43
+ const signature = this.hmacSha256(
44
+ `${encodedHeader}.${encodedPayload}`,
45
+ this.backendSecret
46
+ );
47
+ const encodedSignature = this.base64UrlEncode(signature);
48
+ return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
49
+ }
50
+ /**
51
+ * Validate a JWT token
52
+ */
53
+ validateJWT(token) {
54
+ const parts = token.split(".");
55
+ if (parts.length !== 3) {
56
+ throw new Error("Invalid JWT format");
57
+ }
58
+ const [encodedHeader, encodedPayload, encodedSignature] = parts;
59
+ if (this.backendSecret) {
60
+ const expectedSignature = this.hmacSha256(
61
+ `${encodedHeader}.${encodedPayload}`,
62
+ this.backendSecret
63
+ );
64
+ const expectedEncoded = this.base64UrlEncode(expectedSignature);
65
+ if (!this.timingSafeEqual(encodedSignature, expectedEncoded)) {
66
+ throw new Error("Invalid JWT signature");
67
+ }
68
+ }
69
+ const payload = JSON.parse(this.base64UrlDecode(encodedPayload));
70
+ const now = Math.floor(Date.now() / 1e3);
71
+ if (payload.exp < now) {
72
+ throw new Error("JWT token expired");
73
+ }
74
+ if (payload.iss !== "rainfall-backend") {
75
+ throw new Error("Invalid JWT issuer");
76
+ }
77
+ return {
78
+ edgeNodeId: payload.sub,
79
+ subscriberId: payload.sub,
80
+ // Same as edge node ID for now
81
+ scopes: payload.scope,
82
+ expiresAt: payload.exp
83
+ };
84
+ }
85
+ /**
86
+ * Extract bearer token from Authorization header
87
+ */
88
+ extractBearerToken(authHeader) {
89
+ if (!authHeader) return null;
90
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
91
+ return match ? match[1] : null;
92
+ }
93
+ // ============================================================================
94
+ // ACL Enforcement
95
+ // ============================================================================
96
+ /**
97
+ * Check if an edge node is allowed to perform an action on a job
98
+ * Rule: Edge nodes can only access jobs for their own subscriber
99
+ */
100
+ checkACL(check) {
101
+ if (check.subscriberId !== check.jobSubscriberId) {
102
+ return {
103
+ allowed: false,
104
+ reason: `Edge node ${check.edgeNodeId} cannot access jobs from subscriber ${check.jobSubscriberId}`
105
+ };
106
+ }
107
+ const allowedActions = ["heartbeat", "claim", "submit", "queue"];
108
+ if (!allowedActions.includes(check.action)) {
109
+ return {
110
+ allowed: false,
111
+ reason: `Unknown action: ${check.action}`
112
+ };
113
+ }
114
+ return { allowed: true };
115
+ }
116
+ /**
117
+ * Middleware-style ACL check for job operations
118
+ */
119
+ requireSameSubscriber(edgeNodeSubscriberId, jobSubscriberId, operation) {
120
+ const result = this.checkACL({
121
+ edgeNodeId: edgeNodeSubscriberId,
122
+ subscriberId: edgeNodeSubscriberId,
123
+ jobSubscriberId,
124
+ action: operation
125
+ });
126
+ if (!result.allowed) {
127
+ throw new Error(result.reason);
128
+ }
129
+ }
130
+ // ============================================================================
131
+ // Encryption (Libsodium)
132
+ // ============================================================================
133
+ /**
134
+ * Generate a new Ed25519 key pair for an edge node
135
+ */
136
+ async generateKeyPair() {
137
+ await this.sodiumReady;
138
+ const keyPair = sodium.crypto_box_keypair();
139
+ return {
140
+ publicKey: this.bytesToBase64(keyPair.publicKey),
141
+ privateKey: this.bytesToBase64(keyPair.privateKey)
142
+ };
143
+ }
144
+ /**
145
+ * Encrypt job parameters for a target edge node using its public key
146
+ */
147
+ async encryptForEdgeNode(plaintext, targetPublicKeyBase64) {
148
+ await this.sodiumReady;
149
+ if (!this.keyPair) {
150
+ throw new Error("Local key pair not configured");
151
+ }
152
+ const targetPublicKey = this.base64ToBytes(targetPublicKeyBase64);
153
+ const ephemeralKeyPair = sodium.crypto_box_keypair();
154
+ const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
155
+ const message = new TextEncoder().encode(plaintext);
156
+ const ciphertext = sodium.crypto_box_easy(
157
+ message,
158
+ nonce,
159
+ targetPublicKey,
160
+ ephemeralKeyPair.privateKey
161
+ );
162
+ return {
163
+ ciphertext: this.bytesToBase64(ciphertext),
164
+ nonce: this.bytesToBase64(nonce),
165
+ ephemeralPublicKey: this.bytesToBase64(ephemeralKeyPair.publicKey)
166
+ };
167
+ }
168
+ /**
169
+ * Decrypt job parameters received from the backend
170
+ */
171
+ async decryptFromBackend(encrypted) {
172
+ await this.sodiumReady;
173
+ if (!this.keyPair) {
174
+ throw new Error("Local key pair not configured");
175
+ }
176
+ const privateKey = this.base64ToBytes(this.keyPair.privateKey);
177
+ const ephemeralPublicKey = this.base64ToBytes(encrypted.ephemeralPublicKey);
178
+ const nonce = this.base64ToBytes(encrypted.nonce);
179
+ const ciphertext = this.base64ToBytes(encrypted.ciphertext);
180
+ const decrypted = sodium.crypto_box_open_easy(
181
+ ciphertext,
182
+ nonce,
183
+ ephemeralPublicKey,
184
+ privateKey
185
+ );
186
+ if (!decrypted) {
187
+ throw new Error("Decryption failed - invalid ciphertext or keys");
188
+ }
189
+ return new TextDecoder().decode(decrypted);
190
+ }
191
+ /**
192
+ * Encrypt job parameters for local storage (using secretbox)
193
+ */
194
+ async encryptLocal(plaintext, key) {
195
+ await this.sodiumReady;
196
+ const keyBytes = this.deriveKey(key);
197
+ const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
198
+ const message = new TextEncoder().encode(plaintext);
199
+ const ciphertext = sodium.crypto_secretbox_easy(message, nonce, keyBytes);
200
+ return {
201
+ ciphertext: this.bytesToBase64(ciphertext),
202
+ nonce: this.bytesToBase64(nonce)
203
+ };
204
+ }
205
+ /**
206
+ * Decrypt locally stored job parameters
207
+ */
208
+ async decryptLocal(encrypted, key) {
209
+ await this.sodiumReady;
210
+ const keyBytes = this.deriveKey(key);
211
+ const nonce = this.base64ToBytes(encrypted.nonce);
212
+ const ciphertext = this.base64ToBytes(encrypted.ciphertext);
213
+ const decrypted = sodium.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
214
+ if (!decrypted) {
215
+ throw new Error("Local decryption failed");
216
+ }
217
+ return new TextDecoder().decode(decrypted);
218
+ }
219
+ // ============================================================================
220
+ // Utility Methods
221
+ // ============================================================================
222
+ generateTokenId() {
223
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
224
+ }
225
+ base64UrlEncode(str) {
226
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
227
+ }
228
+ base64UrlDecode(str) {
229
+ const padding = "=".repeat((4 - str.length % 4) % 4);
230
+ const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + padding;
231
+ return atob(base64);
232
+ }
233
+ hmacSha256(message, secret) {
234
+ const key = new TextEncoder().encode(secret);
235
+ const msg = new TextEncoder().encode(message);
236
+ const hash = sodium.crypto_auth(msg, key);
237
+ return this.bytesToBase64(hash);
238
+ }
239
+ timingSafeEqual(a, b) {
240
+ if (a.length !== b.length) return false;
241
+ let result = 0;
242
+ for (let i = 0; i < a.length; i++) {
243
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
244
+ }
245
+ return result === 0;
246
+ }
247
+ bytesToBase64(bytes) {
248
+ const binString = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
249
+ return btoa(binString);
250
+ }
251
+ base64ToBytes(base64) {
252
+ const binString = atob(base64);
253
+ return Uint8Array.from(binString, (m) => m.charCodeAt(0));
254
+ }
255
+ deriveKey(password) {
256
+ const passwordBytes = new TextEncoder().encode(password);
257
+ return sodium.crypto_generichash(32, passwordBytes, null);
258
+ }
259
+ };
260
+ async function createEdgeNodeSecurity(options = {}) {
261
+ const security = new EdgeNodeSecurity(options);
262
+ await security.initialize();
263
+ return security;
264
+ }
265
+
266
+ export {
267
+ EdgeNodeSecurity,
268
+ createEdgeNodeSecurity
269
+ };
@@ -0,0 +1,269 @@
1
+ // src/security/edge-node.ts
2
+ import sodium from "libsodium-wrappers-sumo";
3
+ var EdgeNodeSecurity = class {
4
+ sodiumReady;
5
+ backendSecret;
6
+ keyPair;
7
+ constructor(options = {}) {
8
+ this.sodiumReady = sodium.ready;
9
+ this.backendSecret = options.backendSecret;
10
+ this.keyPair = options.keyPair;
11
+ }
12
+ /**
13
+ * Initialize libsodium
14
+ */
15
+ async initialize() {
16
+ await this.sodiumReady;
17
+ }
18
+ // ============================================================================
19
+ // JWT Token Management
20
+ // ============================================================================
21
+ /**
22
+ * Generate a JWT token for an edge node
23
+ * Note: In production, this is done by the backend. This is for testing.
24
+ */
25
+ generateJWT(edgeNodeId, subscriberId, expiresInDays = 30) {
26
+ if (!this.backendSecret) {
27
+ throw new Error("Backend secret not configured");
28
+ }
29
+ const now = Math.floor(Date.now() / 1e3);
30
+ const exp = now + expiresInDays * 24 * 60 * 60;
31
+ const jti = this.generateTokenId();
32
+ const payload = {
33
+ sub: edgeNodeId,
34
+ iss: "rainfall-backend",
35
+ iat: now,
36
+ exp,
37
+ jti,
38
+ scope: ["edge:heartbeat", "edge:claim", "edge:submit", "edge:queue"]
39
+ };
40
+ const header = { alg: "HS256", typ: "JWT" };
41
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
42
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
43
+ const signature = this.hmacSha256(
44
+ `${encodedHeader}.${encodedPayload}`,
45
+ this.backendSecret
46
+ );
47
+ const encodedSignature = this.base64UrlEncode(signature);
48
+ return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
49
+ }
50
+ /**
51
+ * Validate a JWT token
52
+ */
53
+ validateJWT(token) {
54
+ const parts = token.split(".");
55
+ if (parts.length !== 3) {
56
+ throw new Error("Invalid JWT format");
57
+ }
58
+ const [encodedHeader, encodedPayload, encodedSignature] = parts;
59
+ if (this.backendSecret) {
60
+ const expectedSignature = this.hmacSha256(
61
+ `${encodedHeader}.${encodedPayload}`,
62
+ this.backendSecret
63
+ );
64
+ const expectedEncoded = this.base64UrlEncode(expectedSignature);
65
+ if (!this.timingSafeEqual(encodedSignature, expectedEncoded)) {
66
+ throw new Error("Invalid JWT signature");
67
+ }
68
+ }
69
+ const payload = JSON.parse(this.base64UrlDecode(encodedPayload));
70
+ const now = Math.floor(Date.now() / 1e3);
71
+ if (payload.exp < now) {
72
+ throw new Error("JWT token expired");
73
+ }
74
+ if (payload.iss !== "rainfall-backend") {
75
+ throw new Error("Invalid JWT issuer");
76
+ }
77
+ return {
78
+ edgeNodeId: payload.sub,
79
+ subscriberId: payload.sub,
80
+ // Same as edge node ID for now
81
+ scopes: payload.scope,
82
+ expiresAt: payload.exp
83
+ };
84
+ }
85
+ /**
86
+ * Extract bearer token from Authorization header
87
+ */
88
+ extractBearerToken(authHeader) {
89
+ if (!authHeader) return null;
90
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
91
+ return match ? match[1] : null;
92
+ }
93
+ // ============================================================================
94
+ // ACL Enforcement
95
+ // ============================================================================
96
+ /**
97
+ * Check if an edge node is allowed to perform an action on a job
98
+ * Rule: Edge nodes can only access jobs for their own subscriber
99
+ */
100
+ checkACL(check) {
101
+ if (check.subscriberId !== check.jobSubscriberId) {
102
+ return {
103
+ allowed: false,
104
+ reason: `Edge node ${check.edgeNodeId} cannot access jobs from subscriber ${check.jobSubscriberId}`
105
+ };
106
+ }
107
+ const allowedActions = ["heartbeat", "claim", "submit", "queue"];
108
+ if (!allowedActions.includes(check.action)) {
109
+ return {
110
+ allowed: false,
111
+ reason: `Unknown action: ${check.action}`
112
+ };
113
+ }
114
+ return { allowed: true };
115
+ }
116
+ /**
117
+ * Middleware-style ACL check for job operations
118
+ */
119
+ requireSameSubscriber(edgeNodeSubscriberId, jobSubscriberId, operation) {
120
+ const result = this.checkACL({
121
+ edgeNodeId: edgeNodeSubscriberId,
122
+ subscriberId: edgeNodeSubscriberId,
123
+ jobSubscriberId,
124
+ action: operation
125
+ });
126
+ if (!result.allowed) {
127
+ throw new Error(result.reason);
128
+ }
129
+ }
130
+ // ============================================================================
131
+ // Encryption (Libsodium)
132
+ // ============================================================================
133
+ /**
134
+ * Generate a new Ed25519 key pair for an edge node
135
+ */
136
+ async generateKeyPair() {
137
+ await this.sodiumReady;
138
+ const keyPair = sodium.crypto_box_keypair();
139
+ return {
140
+ publicKey: this.bytesToBase64(keyPair.publicKey),
141
+ privateKey: this.bytesToBase64(keyPair.privateKey)
142
+ };
143
+ }
144
+ /**
145
+ * Encrypt job parameters for a target edge node using its public key
146
+ */
147
+ async encryptForEdgeNode(plaintext, targetPublicKeyBase64) {
148
+ await this.sodiumReady;
149
+ if (!this.keyPair) {
150
+ throw new Error("Local key pair not configured");
151
+ }
152
+ const targetPublicKey = this.base64ToBytes(targetPublicKeyBase64);
153
+ const ephemeralKeyPair = sodium.crypto_box_keypair();
154
+ const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
155
+ const message = new TextEncoder().encode(plaintext);
156
+ const ciphertext = sodium.crypto_box_easy(
157
+ message,
158
+ nonce,
159
+ targetPublicKey,
160
+ ephemeralKeyPair.privateKey
161
+ );
162
+ return {
163
+ ciphertext: this.bytesToBase64(ciphertext),
164
+ nonce: this.bytesToBase64(nonce),
165
+ ephemeralPublicKey: this.bytesToBase64(ephemeralKeyPair.publicKey)
166
+ };
167
+ }
168
+ /**
169
+ * Decrypt job parameters received from the backend
170
+ */
171
+ async decryptFromBackend(encrypted) {
172
+ await this.sodiumReady;
173
+ if (!this.keyPair) {
174
+ throw new Error("Local key pair not configured");
175
+ }
176
+ const privateKey = this.base64ToBytes(this.keyPair.privateKey);
177
+ const ephemeralPublicKey = this.base64ToBytes(encrypted.ephemeralPublicKey);
178
+ const nonce = this.base64ToBytes(encrypted.nonce);
179
+ const ciphertext = this.base64ToBytes(encrypted.ciphertext);
180
+ const decrypted = sodium.crypto_box_open_easy(
181
+ ciphertext,
182
+ nonce,
183
+ ephemeralPublicKey,
184
+ privateKey
185
+ );
186
+ if (!decrypted) {
187
+ throw new Error("Decryption failed - invalid ciphertext or keys");
188
+ }
189
+ return new TextDecoder().decode(decrypted);
190
+ }
191
+ /**
192
+ * Encrypt job parameters for local storage (using secretbox)
193
+ */
194
+ async encryptLocal(plaintext, key) {
195
+ await this.sodiumReady;
196
+ const keyBytes = this.deriveKey(key);
197
+ const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
198
+ const message = new TextEncoder().encode(plaintext);
199
+ const ciphertext = sodium.crypto_secretbox_easy(message, nonce, keyBytes);
200
+ return {
201
+ ciphertext: this.bytesToBase64(ciphertext),
202
+ nonce: this.bytesToBase64(nonce)
203
+ };
204
+ }
205
+ /**
206
+ * Decrypt locally stored job parameters
207
+ */
208
+ async decryptLocal(encrypted, key) {
209
+ await this.sodiumReady;
210
+ const keyBytes = this.deriveKey(key);
211
+ const nonce = this.base64ToBytes(encrypted.nonce);
212
+ const ciphertext = this.base64ToBytes(encrypted.ciphertext);
213
+ const decrypted = sodium.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
214
+ if (!decrypted) {
215
+ throw new Error("Local decryption failed");
216
+ }
217
+ return new TextDecoder().decode(decrypted);
218
+ }
219
+ // ============================================================================
220
+ // Utility Methods
221
+ // ============================================================================
222
+ generateTokenId() {
223
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
224
+ }
225
+ base64UrlEncode(str) {
226
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
227
+ }
228
+ base64UrlDecode(str) {
229
+ const padding = "=".repeat((4 - str.length % 4) % 4);
230
+ const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + padding;
231
+ return atob(base64);
232
+ }
233
+ hmacSha256(message, secret) {
234
+ const key = new TextEncoder().encode(secret);
235
+ const msg = new TextEncoder().encode(message);
236
+ const hash = sodium.crypto_auth(msg, key);
237
+ return this.bytesToBase64(hash);
238
+ }
239
+ timingSafeEqual(a, b) {
240
+ if (a.length !== b.length) return false;
241
+ let result = 0;
242
+ for (let i = 0; i < a.length; i++) {
243
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
244
+ }
245
+ return result === 0;
246
+ }
247
+ bytesToBase64(bytes) {
248
+ const binString = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
249
+ return btoa(binString);
250
+ }
251
+ base64ToBytes(base64) {
252
+ const binString = atob(base64);
253
+ return Uint8Array.from(binString, (m) => m.charCodeAt(0));
254
+ }
255
+ deriveKey(password) {
256
+ const passwordBytes = new TextEncoder().encode(password);
257
+ return sodium.crypto_generichash(32, passwordBytes, null);
258
+ }
259
+ };
260
+ async function createEdgeNodeSecurity(options = {}) {
261
+ const security = new EdgeNodeSecurity(options);
262
+ await security.initialize();
263
+ return security;
264
+ }
265
+
266
+ export {
267
+ EdgeNodeSecurity,
268
+ createEdgeNodeSecurity
269
+ };
package/dist/cli/index.js CHANGED
@@ -3278,13 +3278,13 @@ var import_child_process = require("child_process");
3278
3278
 
3279
3279
  // src/security/edge-node.ts
3280
3280
  init_cjs_shims();
3281
- var sodium = __toESM(require("libsodium-wrappers"));
3281
+ var import_libsodium_wrappers_sumo = __toESM(require("libsodium-wrappers-sumo"));
3282
3282
  var EdgeNodeSecurity = class {
3283
3283
  sodiumReady;
3284
3284
  backendSecret;
3285
3285
  keyPair;
3286
3286
  constructor(options = {}) {
3287
- this.sodiumReady = sodium.ready;
3287
+ this.sodiumReady = import_libsodium_wrappers_sumo.default.ready;
3288
3288
  this.backendSecret = options.backendSecret;
3289
3289
  this.keyPair = options.keyPair;
3290
3290
  }
@@ -3414,7 +3414,7 @@ var EdgeNodeSecurity = class {
3414
3414
  */
3415
3415
  async generateKeyPair() {
3416
3416
  await this.sodiumReady;
3417
- const keyPair = sodium.crypto_box_keypair();
3417
+ const keyPair = import_libsodium_wrappers_sumo.default.crypto_box_keypair();
3418
3418
  return {
3419
3419
  publicKey: this.bytesToBase64(keyPair.publicKey),
3420
3420
  privateKey: this.bytesToBase64(keyPair.privateKey)
@@ -3429,10 +3429,10 @@ var EdgeNodeSecurity = class {
3429
3429
  throw new Error("Local key pair not configured");
3430
3430
  }
3431
3431
  const targetPublicKey = this.base64ToBytes(targetPublicKeyBase64);
3432
- const ephemeralKeyPair = sodium.crypto_box_keypair();
3433
- const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
3432
+ const ephemeralKeyPair = import_libsodium_wrappers_sumo.default.crypto_box_keypair();
3433
+ const nonce = import_libsodium_wrappers_sumo.default.randombytes_buf(import_libsodium_wrappers_sumo.default.crypto_box_NONCEBYTES);
3434
3434
  const message = new TextEncoder().encode(plaintext);
3435
- const ciphertext = sodium.crypto_box_easy(
3435
+ const ciphertext = import_libsodium_wrappers_sumo.default.crypto_box_easy(
3436
3436
  message,
3437
3437
  nonce,
3438
3438
  targetPublicKey,
@@ -3456,7 +3456,7 @@ var EdgeNodeSecurity = class {
3456
3456
  const ephemeralPublicKey = this.base64ToBytes(encrypted.ephemeralPublicKey);
3457
3457
  const nonce = this.base64ToBytes(encrypted.nonce);
3458
3458
  const ciphertext = this.base64ToBytes(encrypted.ciphertext);
3459
- const decrypted = sodium.crypto_box_open_easy(
3459
+ const decrypted = import_libsodium_wrappers_sumo.default.crypto_box_open_easy(
3460
3460
  ciphertext,
3461
3461
  nonce,
3462
3462
  ephemeralPublicKey,
@@ -3473,9 +3473,9 @@ var EdgeNodeSecurity = class {
3473
3473
  async encryptLocal(plaintext, key) {
3474
3474
  await this.sodiumReady;
3475
3475
  const keyBytes = this.deriveKey(key);
3476
- const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
3476
+ const nonce = import_libsodium_wrappers_sumo.default.randombytes_buf(import_libsodium_wrappers_sumo.default.crypto_secretbox_NONCEBYTES);
3477
3477
  const message = new TextEncoder().encode(plaintext);
3478
- const ciphertext = sodium.crypto_secretbox_easy(message, nonce, keyBytes);
3478
+ const ciphertext = import_libsodium_wrappers_sumo.default.crypto_secretbox_easy(message, nonce, keyBytes);
3479
3479
  return {
3480
3480
  ciphertext: this.bytesToBase64(ciphertext),
3481
3481
  nonce: this.bytesToBase64(nonce)
@@ -3489,7 +3489,7 @@ var EdgeNodeSecurity = class {
3489
3489
  const keyBytes = this.deriveKey(key);
3490
3490
  const nonce = this.base64ToBytes(encrypted.nonce);
3491
3491
  const ciphertext = this.base64ToBytes(encrypted.ciphertext);
3492
- const decrypted = sodium.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
3492
+ const decrypted = import_libsodium_wrappers_sumo.default.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
3493
3493
  if (!decrypted) {
3494
3494
  throw new Error("Local decryption failed");
3495
3495
  }
@@ -3512,7 +3512,7 @@ var EdgeNodeSecurity = class {
3512
3512
  hmacSha256(message, secret) {
3513
3513
  const key = new TextEncoder().encode(secret);
3514
3514
  const msg = new TextEncoder().encode(message);
3515
- const hash = sodium.crypto_auth(msg, key);
3515
+ const hash = import_libsodium_wrappers_sumo.default.crypto_auth(msg, key);
3516
3516
  return this.bytesToBase64(hash);
3517
3517
  }
3518
3518
  timingSafeEqual(a, b) {
@@ -3533,7 +3533,7 @@ var EdgeNodeSecurity = class {
3533
3533
  }
3534
3534
  deriveKey(password) {
3535
3535
  const passwordBytes = new TextEncoder().encode(password);
3536
- return sodium.crypto_generichash(32, passwordBytes, null);
3536
+ return import_libsodium_wrappers_sumo.default.crypto_generichash(32, passwordBytes, null);
3537
3537
  }
3538
3538
  };
3539
3539
  async function createEdgeNodeSecurity(options = {}) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createEdgeNodeSecurity
4
- } from "../chunk-XEQ6U3JQ.mjs";
4
+ } from "../chunk-NCQVOLS4.mjs";
5
5
  import {
6
6
  Rainfall
7
7
  } from "../chunk-VDPKDC3R.mjs";
package/dist/index.js CHANGED
@@ -1565,13 +1565,13 @@ function createCronWorkflow(name, cron, workflow) {
1565
1565
  }
1566
1566
 
1567
1567
  // src/security/edge-node.ts
1568
- var sodium = __toESM(require("libsodium-wrappers"));
1568
+ var import_libsodium_wrappers_sumo = __toESM(require("libsodium-wrappers-sumo"));
1569
1569
  var EdgeNodeSecurity = class {
1570
1570
  sodiumReady;
1571
1571
  backendSecret;
1572
1572
  keyPair;
1573
1573
  constructor(options = {}) {
1574
- this.sodiumReady = sodium.ready;
1574
+ this.sodiumReady = import_libsodium_wrappers_sumo.default.ready;
1575
1575
  this.backendSecret = options.backendSecret;
1576
1576
  this.keyPair = options.keyPair;
1577
1577
  }
@@ -1701,7 +1701,7 @@ var EdgeNodeSecurity = class {
1701
1701
  */
1702
1702
  async generateKeyPair() {
1703
1703
  await this.sodiumReady;
1704
- const keyPair = sodium.crypto_box_keypair();
1704
+ const keyPair = import_libsodium_wrappers_sumo.default.crypto_box_keypair();
1705
1705
  return {
1706
1706
  publicKey: this.bytesToBase64(keyPair.publicKey),
1707
1707
  privateKey: this.bytesToBase64(keyPair.privateKey)
@@ -1716,10 +1716,10 @@ var EdgeNodeSecurity = class {
1716
1716
  throw new Error("Local key pair not configured");
1717
1717
  }
1718
1718
  const targetPublicKey = this.base64ToBytes(targetPublicKeyBase64);
1719
- const ephemeralKeyPair = sodium.crypto_box_keypair();
1720
- const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
1719
+ const ephemeralKeyPair = import_libsodium_wrappers_sumo.default.crypto_box_keypair();
1720
+ const nonce = import_libsodium_wrappers_sumo.default.randombytes_buf(import_libsodium_wrappers_sumo.default.crypto_box_NONCEBYTES);
1721
1721
  const message = new TextEncoder().encode(plaintext);
1722
- const ciphertext = sodium.crypto_box_easy(
1722
+ const ciphertext = import_libsodium_wrappers_sumo.default.crypto_box_easy(
1723
1723
  message,
1724
1724
  nonce,
1725
1725
  targetPublicKey,
@@ -1743,7 +1743,7 @@ var EdgeNodeSecurity = class {
1743
1743
  const ephemeralPublicKey = this.base64ToBytes(encrypted.ephemeralPublicKey);
1744
1744
  const nonce = this.base64ToBytes(encrypted.nonce);
1745
1745
  const ciphertext = this.base64ToBytes(encrypted.ciphertext);
1746
- const decrypted = sodium.crypto_box_open_easy(
1746
+ const decrypted = import_libsodium_wrappers_sumo.default.crypto_box_open_easy(
1747
1747
  ciphertext,
1748
1748
  nonce,
1749
1749
  ephemeralPublicKey,
@@ -1760,9 +1760,9 @@ var EdgeNodeSecurity = class {
1760
1760
  async encryptLocal(plaintext, key) {
1761
1761
  await this.sodiumReady;
1762
1762
  const keyBytes = this.deriveKey(key);
1763
- const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
1763
+ const nonce = import_libsodium_wrappers_sumo.default.randombytes_buf(import_libsodium_wrappers_sumo.default.crypto_secretbox_NONCEBYTES);
1764
1764
  const message = new TextEncoder().encode(plaintext);
1765
- const ciphertext = sodium.crypto_secretbox_easy(message, nonce, keyBytes);
1765
+ const ciphertext = import_libsodium_wrappers_sumo.default.crypto_secretbox_easy(message, nonce, keyBytes);
1766
1766
  return {
1767
1767
  ciphertext: this.bytesToBase64(ciphertext),
1768
1768
  nonce: this.bytesToBase64(nonce)
@@ -1776,7 +1776,7 @@ var EdgeNodeSecurity = class {
1776
1776
  const keyBytes = this.deriveKey(key);
1777
1777
  const nonce = this.base64ToBytes(encrypted.nonce);
1778
1778
  const ciphertext = this.base64ToBytes(encrypted.ciphertext);
1779
- const decrypted = sodium.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
1779
+ const decrypted = import_libsodium_wrappers_sumo.default.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
1780
1780
  if (!decrypted) {
1781
1781
  throw new Error("Local decryption failed");
1782
1782
  }
@@ -1799,7 +1799,7 @@ var EdgeNodeSecurity = class {
1799
1799
  hmacSha256(message, secret) {
1800
1800
  const key = new TextEncoder().encode(secret);
1801
1801
  const msg = new TextEncoder().encode(message);
1802
- const hash = sodium.crypto_auth(msg, key);
1802
+ const hash = import_libsodium_wrappers_sumo.default.crypto_auth(msg, key);
1803
1803
  return this.bytesToBase64(hash);
1804
1804
  }
1805
1805
  timingSafeEqual(a, b) {
@@ -1820,7 +1820,7 @@ var EdgeNodeSecurity = class {
1820
1820
  }
1821
1821
  deriveKey(password) {
1822
1822
  const passwordBytes = new TextEncoder().encode(password);
1823
- return sodium.crypto_generichash(32, passwordBytes, null);
1823
+ return import_libsodium_wrappers_sumo.default.crypto_generichash(32, passwordBytes, null);
1824
1824
  }
1825
1825
  };
1826
1826
  async function createEdgeNodeSecurity(options = {}) {
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  EdgeNodeSecurity,
3
3
  createEdgeNodeSecurity
4
- } from "./chunk-XEQ6U3JQ.mjs";
4
+ } from "./chunk-NCQVOLS4.mjs";
5
5
  import {
6
6
  RainfallDaemonContext,
7
7
  RainfallListenerRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainfall-devkit/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Official SDK for Rainfall API - 200+ tools for building AI-powered applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -75,7 +75,7 @@
75
75
  "dependencies": {
76
76
  "@modelcontextprotocol/sdk": "^1.27.1",
77
77
  "express": "^4",
78
- "libsodium-wrappers": "^0.8.2",
78
+ "libsodium-wrappers-sumo": "^0.8.2",
79
79
  "ws": "^8"
80
80
  },
81
81
  "engines": {