@ik-firewall/core 2.4.0 → 2.4.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.
- package/dist/index.cjs +99 -60
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +99 -60
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -90,10 +90,10 @@ var Registry = class _Registry {
|
|
|
90
90
|
/**
|
|
91
91
|
* Loads instance configurations with Hook support
|
|
92
92
|
*/
|
|
93
|
-
load() {
|
|
93
|
+
async load() {
|
|
94
94
|
if (this.hooks?.loadState) {
|
|
95
95
|
try {
|
|
96
|
-
const hookData = this.hooks.loadState("ik_registry");
|
|
96
|
+
const hookData = await this.hooks.loadState("ik_registry");
|
|
97
97
|
if (hookData) return JSON.parse(hookData);
|
|
98
98
|
} catch (e) {
|
|
99
99
|
console.warn("[IK_REGISTRY] Registry load via hook failed, falling back...");
|
|
@@ -114,10 +114,10 @@ var Registry = class _Registry {
|
|
|
114
114
|
}
|
|
115
115
|
return {};
|
|
116
116
|
}
|
|
117
|
-
save(data) {
|
|
117
|
+
async save(data) {
|
|
118
118
|
const jsonStr = JSON.stringify(data, null, 2);
|
|
119
119
|
if (this.hooks?.persistState) {
|
|
120
|
-
this.hooks.persistState("ik_registry", jsonStr);
|
|
120
|
+
await this.hooks.persistState("ik_registry", jsonStr);
|
|
121
121
|
}
|
|
122
122
|
if (fs && this.registryPath) {
|
|
123
123
|
try {
|
|
@@ -132,10 +132,10 @@ var Registry = class _Registry {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
updateInstance(instanceId, config) {
|
|
136
|
-
const all = this.load();
|
|
135
|
+
async updateInstance(instanceId, config) {
|
|
136
|
+
const all = await this.load();
|
|
137
137
|
all[instanceId] = { ...all[instanceId] || {}, ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
138
|
-
this.save(all);
|
|
138
|
+
await this.save(all);
|
|
139
139
|
}
|
|
140
140
|
getUsageFilePath() {
|
|
141
141
|
return this.usagePath;
|
|
@@ -158,13 +158,25 @@ var Registry = class _Registry {
|
|
|
158
158
|
} catch (e) {
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
-
clearUsage() {
|
|
161
|
+
async clearUsage() {
|
|
162
162
|
if (!fs || !this.usagePath) return;
|
|
163
163
|
try {
|
|
164
164
|
if (fs.existsSync(this.usagePath)) fs.unlinkSync(this.usagePath);
|
|
165
165
|
} catch (e) {
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
async getLastFlushTime() {
|
|
169
|
+
if (this.hooks?.loadState) {
|
|
170
|
+
const val = await this.hooks.loadState("ik_last_flush");
|
|
171
|
+
if (val) return parseInt(val, 10);
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
async setLastFlushTime(time) {
|
|
176
|
+
if (this.hooks?.persistState) {
|
|
177
|
+
await this.hooks.persistState("ik_last_flush", time.toString());
|
|
178
|
+
}
|
|
179
|
+
}
|
|
168
180
|
};
|
|
169
181
|
|
|
170
182
|
// src/lib/hmac.ts
|
|
@@ -173,13 +185,22 @@ var IKHmac = class {
|
|
|
173
185
|
constructor(secret) {
|
|
174
186
|
this.secret = secret;
|
|
175
187
|
}
|
|
188
|
+
sortObject(obj) {
|
|
189
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
190
|
+
if (Array.isArray(obj)) return obj.map((i) => this.sortObject(i));
|
|
191
|
+
const sorted = {};
|
|
192
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
193
|
+
sorted[key] = this.sortObject(obj[key]);
|
|
194
|
+
});
|
|
195
|
+
return sorted;
|
|
196
|
+
}
|
|
176
197
|
/**
|
|
177
198
|
* Verifies that a response from the IK-Firewall server is authentic.
|
|
178
199
|
*/
|
|
179
200
|
async verify(data) {
|
|
180
201
|
if (!data || !data.signature) return false;
|
|
181
202
|
const { signature, ...rest } = data;
|
|
182
|
-
const payload = JSON.stringify(rest);
|
|
203
|
+
const payload = JSON.stringify(this.sortObject(rest));
|
|
183
204
|
try {
|
|
184
205
|
const crypto = typeof window === "undefined" ? (await import(
|
|
185
206
|
/* webpackIgnore: true */
|
|
@@ -210,7 +231,6 @@ var ConfigManager = class {
|
|
|
210
231
|
if (hooks) {
|
|
211
232
|
this.registry.setHooks(hooks);
|
|
212
233
|
}
|
|
213
|
-
this.loadFromRegistry();
|
|
214
234
|
}
|
|
215
235
|
getRegistry() {
|
|
216
236
|
return this.registry;
|
|
@@ -226,16 +246,16 @@ var ConfigManager = class {
|
|
|
226
246
|
if (state) {
|
|
227
247
|
const parsed = JSON.parse(state);
|
|
228
248
|
const registry = this.registry;
|
|
229
|
-
registry.save(parsed);
|
|
230
|
-
this.loadFromRegistry();
|
|
249
|
+
await registry.save(parsed);
|
|
250
|
+
await this.loadFromRegistry();
|
|
231
251
|
}
|
|
232
252
|
} catch (e) {
|
|
233
253
|
console.warn("[IK_CONFIG] Hydration from hooks failed:", e);
|
|
234
254
|
}
|
|
235
255
|
}
|
|
236
256
|
}
|
|
237
|
-
loadFromRegistry() {
|
|
238
|
-
const instanceConfigs = this.registry.load();
|
|
257
|
+
async loadFromRegistry() {
|
|
258
|
+
const instanceConfigs = await this.registry.load();
|
|
239
259
|
this.config.instanceConfigs = {
|
|
240
260
|
...this.config.instanceConfigs || {},
|
|
241
261
|
...instanceConfigs
|
|
@@ -270,14 +290,15 @@ var ConfigManager = class {
|
|
|
270
290
|
const response = await fetch(`${endpoint}?licenseKey=${instanceConfig?.licenseKey || ""}&instanceId=${instanceId}&version=2.3.0`);
|
|
271
291
|
if (response.ok) {
|
|
272
292
|
const data = await response.json();
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
293
|
+
const defaultSecret = "default-secret-change-in-production";
|
|
294
|
+
const hmacSecret = this.config.hmacSecret;
|
|
295
|
+
const verificationKey = hmacSecret && hmacSecret !== defaultSecret ? hmacSecret : defaultSecret;
|
|
296
|
+
const verifier = new IKHmac(verificationKey);
|
|
297
|
+
if (!await verifier.verify(data)) {
|
|
298
|
+
console.error("[IK_SYNC] \u{1F6A8} Signature verification failed! Remote config rejected.");
|
|
299
|
+
return;
|
|
279
300
|
}
|
|
280
|
-
this.setInstanceConfig(instanceId, {
|
|
301
|
+
await this.setInstanceConfig(instanceId, {
|
|
281
302
|
billingStatus: data.billingStatus,
|
|
282
303
|
isVerified: data.isVerified,
|
|
283
304
|
pricing: data.pricing,
|
|
@@ -289,7 +310,7 @@ var ConfigManager = class {
|
|
|
289
310
|
}
|
|
290
311
|
} catch (error) {
|
|
291
312
|
console.error(`[IK_CONFIG] Remote sync failed (${error.message}). Falling open (Fail-Open mode).`);
|
|
292
|
-
this.setInstanceConfig(instanceId, {
|
|
313
|
+
await this.setInstanceConfig(instanceId, {
|
|
293
314
|
isVerified: true,
|
|
294
315
|
lastSyncDate: now.toISOString()
|
|
295
316
|
});
|
|
@@ -321,13 +342,13 @@ var ConfigManager = class {
|
|
|
321
342
|
* Updates the live blocked status for an instance (Kill-Switch).
|
|
322
343
|
* This is used by UsageTracker when it receives a response from the central server.
|
|
323
344
|
*/
|
|
324
|
-
setBlockedStatus(instanceId, blocked) {
|
|
345
|
+
async setBlockedStatus(instanceId, blocked) {
|
|
325
346
|
if (blocked) {
|
|
326
347
|
this.blockedInstances.add(instanceId);
|
|
327
|
-
this.setInstanceConfig(instanceId, { billingStatus: "blocked" });
|
|
348
|
+
await this.setInstanceConfig(instanceId, { billingStatus: "blocked" });
|
|
328
349
|
} else {
|
|
329
350
|
this.blockedInstances.delete(instanceId);
|
|
330
|
-
this.setInstanceConfig(instanceId, { billingStatus: "active" });
|
|
351
|
+
await this.setInstanceConfig(instanceId, { billingStatus: "active" });
|
|
331
352
|
}
|
|
332
353
|
}
|
|
333
354
|
updateConfig(newConfig) {
|
|
@@ -361,14 +382,15 @@ var ConfigManager = class {
|
|
|
361
382
|
});
|
|
362
383
|
if (response.ok) {
|
|
363
384
|
const data = await response.json();
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
385
|
+
const defaultSecret = "default-secret-change-in-production";
|
|
386
|
+
const hmacSecret = this.config.hmacSecret;
|
|
387
|
+
const verificationKey = hmacSecret && hmacSecret !== defaultSecret ? hmacSecret : defaultSecret;
|
|
388
|
+
const verifier = new IKHmac(verificationKey);
|
|
389
|
+
if (!await verifier.verify(data)) {
|
|
390
|
+
console.error("[IK_REGISTRY] \u{1F6A8} Registration signature mismatch. Data rejected!");
|
|
391
|
+
return false;
|
|
370
392
|
}
|
|
371
|
-
this.setInstanceConfig(instanceId, {
|
|
393
|
+
await this.setInstanceConfig(instanceId, {
|
|
372
394
|
licenseKey: data.licenseKey,
|
|
373
395
|
billingStatus: data.status.toLowerCase(),
|
|
374
396
|
isVerified: true,
|
|
@@ -384,10 +406,10 @@ var ConfigManager = class {
|
|
|
384
406
|
return false;
|
|
385
407
|
}
|
|
386
408
|
}
|
|
387
|
-
ensureInstanceConfig(instanceId) {
|
|
388
|
-
const all = this.registry.load();
|
|
409
|
+
async ensureInstanceConfig(instanceId) {
|
|
410
|
+
const all = await this.registry.load();
|
|
389
411
|
if (!all[instanceId]) {
|
|
390
|
-
this.registry.updateInstance(instanceId, {
|
|
412
|
+
await this.registry.updateInstance(instanceId, {
|
|
391
413
|
balance: this.config.balance || 0.5,
|
|
392
414
|
aggressiveness: this.config.aggressiveness || 0.5,
|
|
393
415
|
isEnabled: true,
|
|
@@ -395,15 +417,15 @@ var ConfigManager = class {
|
|
|
395
417
|
ownerEmail: this.config.ownerEmail
|
|
396
418
|
// Inherit from global if not specific
|
|
397
419
|
});
|
|
398
|
-
this.loadFromRegistry();
|
|
420
|
+
await this.loadFromRegistry();
|
|
399
421
|
}
|
|
400
422
|
}
|
|
401
|
-
getInstanceConfigs() {
|
|
423
|
+
async getInstanceConfigs() {
|
|
402
424
|
return this.registry.load();
|
|
403
425
|
}
|
|
404
|
-
setInstanceConfig(instanceId, config) {
|
|
405
|
-
this.registry.updateInstance(instanceId, config);
|
|
406
|
-
this.loadFromRegistry();
|
|
426
|
+
async setInstanceConfig(instanceId, config) {
|
|
427
|
+
await this.registry.updateInstance(instanceId, config);
|
|
428
|
+
await this.loadFromRegistry();
|
|
407
429
|
}
|
|
408
430
|
};
|
|
409
431
|
|
|
@@ -1522,11 +1544,14 @@ var UsageTracker = class {
|
|
|
1522
1544
|
}
|
|
1523
1545
|
async checkFlushConditions(instanceId) {
|
|
1524
1546
|
const now = Date.now();
|
|
1547
|
+
if (this.lastFlushTime === 0) {
|
|
1548
|
+
const persisted = await this.config.getRegistry().getLastFlushTime();
|
|
1549
|
+
if (persisted) this.lastFlushTime = persisted;
|
|
1550
|
+
else this.lastFlushTime = now;
|
|
1551
|
+
}
|
|
1525
1552
|
const timeSinceFlush = now - this.lastFlushTime;
|
|
1526
1553
|
if (this.aggregationMap.size >= this.BATCH_SIZE_THRESHOLD || timeSinceFlush >= this.TIME_THRESHOLD_MS) {
|
|
1527
|
-
this.performAsyncFlush(instanceId)
|
|
1528
|
-
console.error("[IK_TRACKER] Background flush failed:", err);
|
|
1529
|
-
});
|
|
1554
|
+
await this.performAsyncFlush(instanceId);
|
|
1530
1555
|
}
|
|
1531
1556
|
}
|
|
1532
1557
|
async performAsyncFlush(instanceId) {
|
|
@@ -1535,7 +1560,8 @@ var UsageTracker = class {
|
|
|
1535
1560
|
const reports = Array.from(this.aggregationMap.values());
|
|
1536
1561
|
this.aggregationMap.clear();
|
|
1537
1562
|
this.lastFlushTime = Date.now();
|
|
1538
|
-
const
|
|
1563
|
+
const instanceConfigs = await this.config.getInstanceConfigs();
|
|
1564
|
+
const licenseKey = instanceConfigs[instanceId]?.licenseKey || this.config.get("licenseKey");
|
|
1539
1565
|
const payload = {
|
|
1540
1566
|
licenseKey: licenseKey || "TRIAL-KEY",
|
|
1541
1567
|
instanceId,
|
|
@@ -1546,12 +1572,13 @@ var UsageTracker = class {
|
|
|
1546
1572
|
await this.sendBatch(payload);
|
|
1547
1573
|
} catch (error) {
|
|
1548
1574
|
console.error("[IK_TRACKER] Batch send failed:", error.message);
|
|
1549
|
-
const isAuthError = error.message?.includes("401");
|
|
1550
|
-
if (
|
|
1575
|
+
const isAuthError = error.message?.includes("401") || error.message?.includes("403");
|
|
1576
|
+
if (isAuthError) {
|
|
1577
|
+
console.warn("[IK_TRACKER] \u{1F512} Unauthorized/Forbidden: Subscription invalid or blocked. Discarding batch.");
|
|
1578
|
+
this.config.setBlockedStatus(instanceId, true);
|
|
1579
|
+
} else {
|
|
1551
1580
|
console.log("[IK_TRACKER] Queuing for retry...");
|
|
1552
1581
|
this.handleFailure(payload);
|
|
1553
|
-
} else {
|
|
1554
|
-
console.warn("[IK_TRACKER] Unauthorized: Please check your IK_FIREWALL_SECRET and License Key. Discarding batch.");
|
|
1555
1582
|
}
|
|
1556
1583
|
} finally {
|
|
1557
1584
|
this.flushInProgress = false;
|
|
@@ -1564,14 +1591,16 @@ var UsageTracker = class {
|
|
|
1564
1591
|
if (process.env.IK_FIREWALL_DISABLED === "true" || process.env.NEXT_PUBLIC_IK_FIREWALL_DISABLED === "true") {
|
|
1565
1592
|
return;
|
|
1566
1593
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1594
|
+
const defaultSecret = "default-secret-change-in-production";
|
|
1595
|
+
const signingKey = hmacSecret && hmacSecret !== defaultSecret ? hmacSecret : payload.licenseKey || defaultSecret;
|
|
1596
|
+
try {
|
|
1597
|
+
const crypto = typeof window === "undefined" ? (await import("crypto")).default : null;
|
|
1598
|
+
if (crypto && crypto.createHmac) {
|
|
1599
|
+
const { signature: _sig, ...signablePayload } = payload;
|
|
1600
|
+
const sortedPayload = this.sortObject(signablePayload);
|
|
1601
|
+
payload.signature = crypto.createHmac("sha256", signingKey).update(JSON.stringify(sortedPayload)).digest("hex");
|
|
1574
1602
|
}
|
|
1603
|
+
} catch (e) {
|
|
1575
1604
|
}
|
|
1576
1605
|
const response = await fetch(endpoint, {
|
|
1577
1606
|
method: "POST",
|
|
@@ -1594,6 +1623,7 @@ var UsageTracker = class {
|
|
|
1594
1623
|
}
|
|
1595
1624
|
} catch (e) {
|
|
1596
1625
|
}
|
|
1626
|
+
await this.config.getRegistry().setLastFlushTime(this.lastFlushTime);
|
|
1597
1627
|
this.hooks?.onFlushSuccess?.(payload);
|
|
1598
1628
|
}
|
|
1599
1629
|
async handleFailure(payload) {
|
|
@@ -1609,6 +1639,15 @@ var UsageTracker = class {
|
|
|
1609
1639
|
await this.hooks.persistState(`ik_retry_${payload.timestamp}`, JSON.stringify(payload));
|
|
1610
1640
|
}
|
|
1611
1641
|
}
|
|
1642
|
+
sortObject(obj) {
|
|
1643
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
1644
|
+
if (Array.isArray(obj)) return obj.map((i) => this.sortObject(i));
|
|
1645
|
+
const sorted = {};
|
|
1646
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
1647
|
+
sorted[key] = this.sortObject(obj[key]);
|
|
1648
|
+
});
|
|
1649
|
+
return sorted;
|
|
1650
|
+
}
|
|
1612
1651
|
getBuffer() {
|
|
1613
1652
|
return Array.from(this.aggregationMap.values());
|
|
1614
1653
|
}
|
|
@@ -1807,11 +1846,11 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1807
1846
|
getSessionDNA() {
|
|
1808
1847
|
return this.sessionDNA;
|
|
1809
1848
|
}
|
|
1810
|
-
getInstanceConfigs() {
|
|
1849
|
+
async getInstanceConfigs() {
|
|
1811
1850
|
return this.configManager.getInstanceConfigs();
|
|
1812
1851
|
}
|
|
1813
|
-
setInstanceConfig(instanceId, config) {
|
|
1814
|
-
this.configManager.setInstanceConfig(instanceId, config);
|
|
1852
|
+
async setInstanceConfig(instanceId, config) {
|
|
1853
|
+
await this.configManager.setInstanceConfig(instanceId, config);
|
|
1815
1854
|
}
|
|
1816
1855
|
/**
|
|
1817
1856
|
* Discovers available providers by checking environment variables and local health.
|
|
@@ -1895,7 +1934,7 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1895
1934
|
this.hydrated = true;
|
|
1896
1935
|
}
|
|
1897
1936
|
if (instanceId) {
|
|
1898
|
-
this.configManager.ensureInstanceConfig(instanceId);
|
|
1937
|
+
await this.configManager.ensureInstanceConfig(instanceId);
|
|
1899
1938
|
}
|
|
1900
1939
|
const mergedConfig = this.configManager.getMergedConfig(instanceId);
|
|
1901
1940
|
if (instanceId && mergedConfig?.billingStatus === "blocked") {
|
|
@@ -2030,7 +2069,7 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
2030
2069
|
}
|
|
2031
2070
|
async analyzeAIAssisted(input, provider, personaName = "professional", instanceId, configOverride) {
|
|
2032
2071
|
if (instanceId) {
|
|
2033
|
-
this.configManager.ensureInstanceConfig(instanceId);
|
|
2072
|
+
await this.configManager.ensureInstanceConfig(instanceId);
|
|
2034
2073
|
}
|
|
2035
2074
|
const activeConfig = this.configManager.getMergedConfig(instanceId, configOverride);
|
|
2036
2075
|
const locale = activeConfig?.locale || "en";
|
package/dist/index.d.cts
CHANGED
|
@@ -340,8 +340,8 @@ declare class IKFirewallCore {
|
|
|
340
340
|
getConfig(): IKConfig;
|
|
341
341
|
getPlugins(): IKPluginInfo[];
|
|
342
342
|
getSessionDNA(): number;
|
|
343
|
-
getInstanceConfigs(): Record<string, Partial<IKConfig
|
|
344
|
-
setInstanceConfig(instanceId: string, config: Partial<IKConfig>): void
|
|
343
|
+
getInstanceConfigs(): Promise<Record<string, Partial<IKConfig>>>;
|
|
344
|
+
setInstanceConfig(instanceId: string, config: Partial<IKConfig>): Promise<void>;
|
|
345
345
|
/**
|
|
346
346
|
* Discovers available providers by checking environment variables and local health.
|
|
347
347
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -340,8 +340,8 @@ declare class IKFirewallCore {
|
|
|
340
340
|
getConfig(): IKConfig;
|
|
341
341
|
getPlugins(): IKPluginInfo[];
|
|
342
342
|
getSessionDNA(): number;
|
|
343
|
-
getInstanceConfigs(): Record<string, Partial<IKConfig
|
|
344
|
-
setInstanceConfig(instanceId: string, config: Partial<IKConfig>): void
|
|
343
|
+
getInstanceConfigs(): Promise<Record<string, Partial<IKConfig>>>;
|
|
344
|
+
setInstanceConfig(instanceId: string, config: Partial<IKConfig>): Promise<void>;
|
|
345
345
|
/**
|
|
346
346
|
* Discovers available providers by checking environment variables and local health.
|
|
347
347
|
*/
|
package/dist/index.js
CHANGED
|
@@ -48,10 +48,10 @@ var Registry = class _Registry {
|
|
|
48
48
|
/**
|
|
49
49
|
* Loads instance configurations with Hook support
|
|
50
50
|
*/
|
|
51
|
-
load() {
|
|
51
|
+
async load() {
|
|
52
52
|
if (this.hooks?.loadState) {
|
|
53
53
|
try {
|
|
54
|
-
const hookData = this.hooks.loadState("ik_registry");
|
|
54
|
+
const hookData = await this.hooks.loadState("ik_registry");
|
|
55
55
|
if (hookData) return JSON.parse(hookData);
|
|
56
56
|
} catch (e) {
|
|
57
57
|
console.warn("[IK_REGISTRY] Registry load via hook failed, falling back...");
|
|
@@ -72,10 +72,10 @@ var Registry = class _Registry {
|
|
|
72
72
|
}
|
|
73
73
|
return {};
|
|
74
74
|
}
|
|
75
|
-
save(data) {
|
|
75
|
+
async save(data) {
|
|
76
76
|
const jsonStr = JSON.stringify(data, null, 2);
|
|
77
77
|
if (this.hooks?.persistState) {
|
|
78
|
-
this.hooks.persistState("ik_registry", jsonStr);
|
|
78
|
+
await this.hooks.persistState("ik_registry", jsonStr);
|
|
79
79
|
}
|
|
80
80
|
if (fs && this.registryPath) {
|
|
81
81
|
try {
|
|
@@ -90,10 +90,10 @@ var Registry = class _Registry {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
updateInstance(instanceId, config) {
|
|
94
|
-
const all = this.load();
|
|
93
|
+
async updateInstance(instanceId, config) {
|
|
94
|
+
const all = await this.load();
|
|
95
95
|
all[instanceId] = { ...all[instanceId] || {}, ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
96
|
-
this.save(all);
|
|
96
|
+
await this.save(all);
|
|
97
97
|
}
|
|
98
98
|
getUsageFilePath() {
|
|
99
99
|
return this.usagePath;
|
|
@@ -116,13 +116,25 @@ var Registry = class _Registry {
|
|
|
116
116
|
} catch (e) {
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
clearUsage() {
|
|
119
|
+
async clearUsage() {
|
|
120
120
|
if (!fs || !this.usagePath) return;
|
|
121
121
|
try {
|
|
122
122
|
if (fs.existsSync(this.usagePath)) fs.unlinkSync(this.usagePath);
|
|
123
123
|
} catch (e) {
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
+
async getLastFlushTime() {
|
|
127
|
+
if (this.hooks?.loadState) {
|
|
128
|
+
const val = await this.hooks.loadState("ik_last_flush");
|
|
129
|
+
if (val) return parseInt(val, 10);
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
async setLastFlushTime(time) {
|
|
134
|
+
if (this.hooks?.persistState) {
|
|
135
|
+
await this.hooks.persistState("ik_last_flush", time.toString());
|
|
136
|
+
}
|
|
137
|
+
}
|
|
126
138
|
};
|
|
127
139
|
|
|
128
140
|
// src/lib/hmac.ts
|
|
@@ -131,13 +143,22 @@ var IKHmac = class {
|
|
|
131
143
|
constructor(secret) {
|
|
132
144
|
this.secret = secret;
|
|
133
145
|
}
|
|
146
|
+
sortObject(obj) {
|
|
147
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
148
|
+
if (Array.isArray(obj)) return obj.map((i) => this.sortObject(i));
|
|
149
|
+
const sorted = {};
|
|
150
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
151
|
+
sorted[key] = this.sortObject(obj[key]);
|
|
152
|
+
});
|
|
153
|
+
return sorted;
|
|
154
|
+
}
|
|
134
155
|
/**
|
|
135
156
|
* Verifies that a response from the IK-Firewall server is authentic.
|
|
136
157
|
*/
|
|
137
158
|
async verify(data) {
|
|
138
159
|
if (!data || !data.signature) return false;
|
|
139
160
|
const { signature, ...rest } = data;
|
|
140
|
-
const payload = JSON.stringify(rest);
|
|
161
|
+
const payload = JSON.stringify(this.sortObject(rest));
|
|
141
162
|
try {
|
|
142
163
|
const crypto = typeof window === "undefined" ? (await import(
|
|
143
164
|
/* webpackIgnore: true */
|
|
@@ -168,7 +189,6 @@ var ConfigManager = class {
|
|
|
168
189
|
if (hooks) {
|
|
169
190
|
this.registry.setHooks(hooks);
|
|
170
191
|
}
|
|
171
|
-
this.loadFromRegistry();
|
|
172
192
|
}
|
|
173
193
|
getRegistry() {
|
|
174
194
|
return this.registry;
|
|
@@ -184,16 +204,16 @@ var ConfigManager = class {
|
|
|
184
204
|
if (state) {
|
|
185
205
|
const parsed = JSON.parse(state);
|
|
186
206
|
const registry = this.registry;
|
|
187
|
-
registry.save(parsed);
|
|
188
|
-
this.loadFromRegistry();
|
|
207
|
+
await registry.save(parsed);
|
|
208
|
+
await this.loadFromRegistry();
|
|
189
209
|
}
|
|
190
210
|
} catch (e) {
|
|
191
211
|
console.warn("[IK_CONFIG] Hydration from hooks failed:", e);
|
|
192
212
|
}
|
|
193
213
|
}
|
|
194
214
|
}
|
|
195
|
-
loadFromRegistry() {
|
|
196
|
-
const instanceConfigs = this.registry.load();
|
|
215
|
+
async loadFromRegistry() {
|
|
216
|
+
const instanceConfigs = await this.registry.load();
|
|
197
217
|
this.config.instanceConfigs = {
|
|
198
218
|
...this.config.instanceConfigs || {},
|
|
199
219
|
...instanceConfigs
|
|
@@ -228,14 +248,15 @@ var ConfigManager = class {
|
|
|
228
248
|
const response = await fetch(`${endpoint}?licenseKey=${instanceConfig?.licenseKey || ""}&instanceId=${instanceId}&version=2.3.0`);
|
|
229
249
|
if (response.ok) {
|
|
230
250
|
const data = await response.json();
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
251
|
+
const defaultSecret = "default-secret-change-in-production";
|
|
252
|
+
const hmacSecret = this.config.hmacSecret;
|
|
253
|
+
const verificationKey = hmacSecret && hmacSecret !== defaultSecret ? hmacSecret : defaultSecret;
|
|
254
|
+
const verifier = new IKHmac(verificationKey);
|
|
255
|
+
if (!await verifier.verify(data)) {
|
|
256
|
+
console.error("[IK_SYNC] \u{1F6A8} Signature verification failed! Remote config rejected.");
|
|
257
|
+
return;
|
|
237
258
|
}
|
|
238
|
-
this.setInstanceConfig(instanceId, {
|
|
259
|
+
await this.setInstanceConfig(instanceId, {
|
|
239
260
|
billingStatus: data.billingStatus,
|
|
240
261
|
isVerified: data.isVerified,
|
|
241
262
|
pricing: data.pricing,
|
|
@@ -247,7 +268,7 @@ var ConfigManager = class {
|
|
|
247
268
|
}
|
|
248
269
|
} catch (error) {
|
|
249
270
|
console.error(`[IK_CONFIG] Remote sync failed (${error.message}). Falling open (Fail-Open mode).`);
|
|
250
|
-
this.setInstanceConfig(instanceId, {
|
|
271
|
+
await this.setInstanceConfig(instanceId, {
|
|
251
272
|
isVerified: true,
|
|
252
273
|
lastSyncDate: now.toISOString()
|
|
253
274
|
});
|
|
@@ -279,13 +300,13 @@ var ConfigManager = class {
|
|
|
279
300
|
* Updates the live blocked status for an instance (Kill-Switch).
|
|
280
301
|
* This is used by UsageTracker when it receives a response from the central server.
|
|
281
302
|
*/
|
|
282
|
-
setBlockedStatus(instanceId, blocked) {
|
|
303
|
+
async setBlockedStatus(instanceId, blocked) {
|
|
283
304
|
if (blocked) {
|
|
284
305
|
this.blockedInstances.add(instanceId);
|
|
285
|
-
this.setInstanceConfig(instanceId, { billingStatus: "blocked" });
|
|
306
|
+
await this.setInstanceConfig(instanceId, { billingStatus: "blocked" });
|
|
286
307
|
} else {
|
|
287
308
|
this.blockedInstances.delete(instanceId);
|
|
288
|
-
this.setInstanceConfig(instanceId, { billingStatus: "active" });
|
|
309
|
+
await this.setInstanceConfig(instanceId, { billingStatus: "active" });
|
|
289
310
|
}
|
|
290
311
|
}
|
|
291
312
|
updateConfig(newConfig) {
|
|
@@ -319,14 +340,15 @@ var ConfigManager = class {
|
|
|
319
340
|
});
|
|
320
341
|
if (response.ok) {
|
|
321
342
|
const data = await response.json();
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
343
|
+
const defaultSecret = "default-secret-change-in-production";
|
|
344
|
+
const hmacSecret = this.config.hmacSecret;
|
|
345
|
+
const verificationKey = hmacSecret && hmacSecret !== defaultSecret ? hmacSecret : defaultSecret;
|
|
346
|
+
const verifier = new IKHmac(verificationKey);
|
|
347
|
+
if (!await verifier.verify(data)) {
|
|
348
|
+
console.error("[IK_REGISTRY] \u{1F6A8} Registration signature mismatch. Data rejected!");
|
|
349
|
+
return false;
|
|
328
350
|
}
|
|
329
|
-
this.setInstanceConfig(instanceId, {
|
|
351
|
+
await this.setInstanceConfig(instanceId, {
|
|
330
352
|
licenseKey: data.licenseKey,
|
|
331
353
|
billingStatus: data.status.toLowerCase(),
|
|
332
354
|
isVerified: true,
|
|
@@ -342,10 +364,10 @@ var ConfigManager = class {
|
|
|
342
364
|
return false;
|
|
343
365
|
}
|
|
344
366
|
}
|
|
345
|
-
ensureInstanceConfig(instanceId) {
|
|
346
|
-
const all = this.registry.load();
|
|
367
|
+
async ensureInstanceConfig(instanceId) {
|
|
368
|
+
const all = await this.registry.load();
|
|
347
369
|
if (!all[instanceId]) {
|
|
348
|
-
this.registry.updateInstance(instanceId, {
|
|
370
|
+
await this.registry.updateInstance(instanceId, {
|
|
349
371
|
balance: this.config.balance || 0.5,
|
|
350
372
|
aggressiveness: this.config.aggressiveness || 0.5,
|
|
351
373
|
isEnabled: true,
|
|
@@ -353,15 +375,15 @@ var ConfigManager = class {
|
|
|
353
375
|
ownerEmail: this.config.ownerEmail
|
|
354
376
|
// Inherit from global if not specific
|
|
355
377
|
});
|
|
356
|
-
this.loadFromRegistry();
|
|
378
|
+
await this.loadFromRegistry();
|
|
357
379
|
}
|
|
358
380
|
}
|
|
359
|
-
getInstanceConfigs() {
|
|
381
|
+
async getInstanceConfigs() {
|
|
360
382
|
return this.registry.load();
|
|
361
383
|
}
|
|
362
|
-
setInstanceConfig(instanceId, config) {
|
|
363
|
-
this.registry.updateInstance(instanceId, config);
|
|
364
|
-
this.loadFromRegistry();
|
|
384
|
+
async setInstanceConfig(instanceId, config) {
|
|
385
|
+
await this.registry.updateInstance(instanceId, config);
|
|
386
|
+
await this.loadFromRegistry();
|
|
365
387
|
}
|
|
366
388
|
};
|
|
367
389
|
|
|
@@ -1480,11 +1502,14 @@ var UsageTracker = class {
|
|
|
1480
1502
|
}
|
|
1481
1503
|
async checkFlushConditions(instanceId) {
|
|
1482
1504
|
const now = Date.now();
|
|
1505
|
+
if (this.lastFlushTime === 0) {
|
|
1506
|
+
const persisted = await this.config.getRegistry().getLastFlushTime();
|
|
1507
|
+
if (persisted) this.lastFlushTime = persisted;
|
|
1508
|
+
else this.lastFlushTime = now;
|
|
1509
|
+
}
|
|
1483
1510
|
const timeSinceFlush = now - this.lastFlushTime;
|
|
1484
1511
|
if (this.aggregationMap.size >= this.BATCH_SIZE_THRESHOLD || timeSinceFlush >= this.TIME_THRESHOLD_MS) {
|
|
1485
|
-
this.performAsyncFlush(instanceId)
|
|
1486
|
-
console.error("[IK_TRACKER] Background flush failed:", err);
|
|
1487
|
-
});
|
|
1512
|
+
await this.performAsyncFlush(instanceId);
|
|
1488
1513
|
}
|
|
1489
1514
|
}
|
|
1490
1515
|
async performAsyncFlush(instanceId) {
|
|
@@ -1493,7 +1518,8 @@ var UsageTracker = class {
|
|
|
1493
1518
|
const reports = Array.from(this.aggregationMap.values());
|
|
1494
1519
|
this.aggregationMap.clear();
|
|
1495
1520
|
this.lastFlushTime = Date.now();
|
|
1496
|
-
const
|
|
1521
|
+
const instanceConfigs = await this.config.getInstanceConfigs();
|
|
1522
|
+
const licenseKey = instanceConfigs[instanceId]?.licenseKey || this.config.get("licenseKey");
|
|
1497
1523
|
const payload = {
|
|
1498
1524
|
licenseKey: licenseKey || "TRIAL-KEY",
|
|
1499
1525
|
instanceId,
|
|
@@ -1504,12 +1530,13 @@ var UsageTracker = class {
|
|
|
1504
1530
|
await this.sendBatch(payload);
|
|
1505
1531
|
} catch (error) {
|
|
1506
1532
|
console.error("[IK_TRACKER] Batch send failed:", error.message);
|
|
1507
|
-
const isAuthError = error.message?.includes("401");
|
|
1508
|
-
if (
|
|
1533
|
+
const isAuthError = error.message?.includes("401") || error.message?.includes("403");
|
|
1534
|
+
if (isAuthError) {
|
|
1535
|
+
console.warn("[IK_TRACKER] \u{1F512} Unauthorized/Forbidden: Subscription invalid or blocked. Discarding batch.");
|
|
1536
|
+
this.config.setBlockedStatus(instanceId, true);
|
|
1537
|
+
} else {
|
|
1509
1538
|
console.log("[IK_TRACKER] Queuing for retry...");
|
|
1510
1539
|
this.handleFailure(payload);
|
|
1511
|
-
} else {
|
|
1512
|
-
console.warn("[IK_TRACKER] Unauthorized: Please check your IK_FIREWALL_SECRET and License Key. Discarding batch.");
|
|
1513
1540
|
}
|
|
1514
1541
|
} finally {
|
|
1515
1542
|
this.flushInProgress = false;
|
|
@@ -1522,14 +1549,16 @@ var UsageTracker = class {
|
|
|
1522
1549
|
if (process.env.IK_FIREWALL_DISABLED === "true" || process.env.NEXT_PUBLIC_IK_FIREWALL_DISABLED === "true") {
|
|
1523
1550
|
return;
|
|
1524
1551
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1552
|
+
const defaultSecret = "default-secret-change-in-production";
|
|
1553
|
+
const signingKey = hmacSecret && hmacSecret !== defaultSecret ? hmacSecret : payload.licenseKey || defaultSecret;
|
|
1554
|
+
try {
|
|
1555
|
+
const crypto = typeof window === "undefined" ? (await import("crypto")).default : null;
|
|
1556
|
+
if (crypto && crypto.createHmac) {
|
|
1557
|
+
const { signature: _sig, ...signablePayload } = payload;
|
|
1558
|
+
const sortedPayload = this.sortObject(signablePayload);
|
|
1559
|
+
payload.signature = crypto.createHmac("sha256", signingKey).update(JSON.stringify(sortedPayload)).digest("hex");
|
|
1532
1560
|
}
|
|
1561
|
+
} catch (e) {
|
|
1533
1562
|
}
|
|
1534
1563
|
const response = await fetch(endpoint, {
|
|
1535
1564
|
method: "POST",
|
|
@@ -1552,6 +1581,7 @@ var UsageTracker = class {
|
|
|
1552
1581
|
}
|
|
1553
1582
|
} catch (e) {
|
|
1554
1583
|
}
|
|
1584
|
+
await this.config.getRegistry().setLastFlushTime(this.lastFlushTime);
|
|
1555
1585
|
this.hooks?.onFlushSuccess?.(payload);
|
|
1556
1586
|
}
|
|
1557
1587
|
async handleFailure(payload) {
|
|
@@ -1567,6 +1597,15 @@ var UsageTracker = class {
|
|
|
1567
1597
|
await this.hooks.persistState(`ik_retry_${payload.timestamp}`, JSON.stringify(payload));
|
|
1568
1598
|
}
|
|
1569
1599
|
}
|
|
1600
|
+
sortObject(obj) {
|
|
1601
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
1602
|
+
if (Array.isArray(obj)) return obj.map((i) => this.sortObject(i));
|
|
1603
|
+
const sorted = {};
|
|
1604
|
+
Object.keys(obj).sort().forEach((key) => {
|
|
1605
|
+
sorted[key] = this.sortObject(obj[key]);
|
|
1606
|
+
});
|
|
1607
|
+
return sorted;
|
|
1608
|
+
}
|
|
1570
1609
|
getBuffer() {
|
|
1571
1610
|
return Array.from(this.aggregationMap.values());
|
|
1572
1611
|
}
|
|
@@ -1765,11 +1804,11 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1765
1804
|
getSessionDNA() {
|
|
1766
1805
|
return this.sessionDNA;
|
|
1767
1806
|
}
|
|
1768
|
-
getInstanceConfigs() {
|
|
1807
|
+
async getInstanceConfigs() {
|
|
1769
1808
|
return this.configManager.getInstanceConfigs();
|
|
1770
1809
|
}
|
|
1771
|
-
setInstanceConfig(instanceId, config) {
|
|
1772
|
-
this.configManager.setInstanceConfig(instanceId, config);
|
|
1810
|
+
async setInstanceConfig(instanceId, config) {
|
|
1811
|
+
await this.configManager.setInstanceConfig(instanceId, config);
|
|
1773
1812
|
}
|
|
1774
1813
|
/**
|
|
1775
1814
|
* Discovers available providers by checking environment variables and local health.
|
|
@@ -1853,7 +1892,7 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1853
1892
|
this.hydrated = true;
|
|
1854
1893
|
}
|
|
1855
1894
|
if (instanceId) {
|
|
1856
|
-
this.configManager.ensureInstanceConfig(instanceId);
|
|
1895
|
+
await this.configManager.ensureInstanceConfig(instanceId);
|
|
1857
1896
|
}
|
|
1858
1897
|
const mergedConfig = this.configManager.getMergedConfig(instanceId);
|
|
1859
1898
|
if (instanceId && mergedConfig?.billingStatus === "blocked") {
|
|
@@ -1988,7 +2027,7 @@ var IKFirewallCore = class _IKFirewallCore {
|
|
|
1988
2027
|
}
|
|
1989
2028
|
async analyzeAIAssisted(input, provider, personaName = "professional", instanceId, configOverride) {
|
|
1990
2029
|
if (instanceId) {
|
|
1991
|
-
this.configManager.ensureInstanceConfig(instanceId);
|
|
2030
|
+
await this.configManager.ensureInstanceConfig(instanceId);
|
|
1992
2031
|
}
|
|
1993
2032
|
const activeConfig = this.configManager.getMergedConfig(instanceId, configOverride);
|
|
1994
2033
|
const locale = activeConfig?.locale || "en";
|