@anomira/node-sdk 0.2.0 → 0.2.3

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.d.cts CHANGED
@@ -78,6 +78,43 @@ interface AnomiraConfig {
78
78
  * Default: reads X-Forwarded-For → X-Real-IP → socket.remoteAddress
79
79
  */
80
80
  getIp?: (req: unknown) => string;
81
+ /**
82
+ * Invisible security — automatic blocking from Anomira's community
83
+ * threat intelligence network.
84
+ *
85
+ * When enabled (the default), the SDK periodically fetches high-confidence
86
+ * attacker IPs from the Anomira network — IPs that have been seen attacking
87
+ * multiple customers — and blocks them automatically, without any manual
88
+ * decision required.
89
+ *
90
+ * This is the "invisible" part: your API is protected from known bad actors
91
+ * the moment they appear in the network, before they even reach your handlers.
92
+ *
93
+ * Set `enabled: false` to disable auto-blocking and use threat data for
94
+ * alerting only (the data is still fetched and logged).
95
+ */
96
+ autoBlock?: {
97
+ /**
98
+ * Whether to automatically block IPs from the community threat network.
99
+ * @default true
100
+ */
101
+ enabled?: boolean;
102
+ /**
103
+ * Minimum community confidence score (0-100) to trigger an auto-block.
104
+ * Only IPs above this threshold are blocked automatically.
105
+ *
106
+ * @default 85
107
+ *
108
+ * Guidance:
109
+ * 85 (default) — confirmed malicious across multiple customers, very low false-positive risk
110
+ * 70 — broader coverage, slightly higher false-positive risk
111
+ * 95 — maximum precision, only the most confirmed threats
112
+ *
113
+ * Do not set below 60 — scores below that reflect limited data and carry
114
+ * meaningful false-positive risk.
115
+ */
116
+ communityThreshold?: number;
117
+ };
81
118
  }
82
119
  interface SdkEvent {
83
120
  name: string;
@@ -150,22 +187,41 @@ interface FirewallRule {
150
187
  */
151
188
  declare class AnomiraClient {
152
189
  #private;
153
- readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect">> & {
190
+ readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect" | "autoBlock">> & {
154
191
  getUserId: NonNullable<AnomiraConfig["getUserId"]>;
155
192
  getIp: NonNullable<AnomiraConfig["getIp"]>;
156
193
  detect: Required<NonNullable<AnomiraConfig["detect"]>>;
157
194
  captureConsole: boolean;
158
195
  service: string;
196
+ autoBlock: Required<NonNullable<AnomiraConfig["autoBlock"]>>;
159
197
  };
160
198
  private readonly buffer;
161
199
  private readonly logBuffer;
162
200
  private logFlushTimer;
163
201
  private blocklistTimer;
164
202
  private firewallTimer;
203
+ private communityThreatTimer;
165
204
  /** True when credentials are missing — all operations become no-ops. */
166
205
  private disabled;
167
- /** In-process cache of blocked IPs — refreshed every 60 s from the ingest server. */
206
+ /** In-process cache of manually blocked IPs — refreshed every 60 s. */
168
207
  private blockedIpCache;
208
+ /** In-process cache of community threat IPs with their confidence scores.
209
+ * Populated from Anomira's federated threat network — IPs confirmed malicious
210
+ * across multiple customers. Refreshed every 60 s. */
211
+ private communityThreatCache;
212
+ /**
213
+ * Set of honeypot paths to intercept. When a request matches, the SDK returns
214
+ * a fake response instead of forwarding to the customer's route handlers.
215
+ * Synced from ingest every 60 s. Keys are lowercase path strings.
216
+ */
217
+ private honeypotPaths;
218
+ /**
219
+ * Set of active canary token strings embedded in previously-served honeypot
220
+ * responses. When any of these appear in a request's Authorization header or
221
+ * body, a http.canary.triggered event is fired.
222
+ * Synced from ingest every 60 s (max 100 tokens, sliding 7-day window).
223
+ */
224
+ private canaryTokenCache;
169
225
  /** In-process cache of firewall rules with pre-compiled regex — refreshed every 60 s. */
170
226
  private compiledRules;
171
227
  private readonly _origLog;
@@ -184,8 +240,27 @@ declare class AnomiraClient {
184
240
  * });
185
241
  * ```
186
242
  */
187
- /** Returns true if the IP is in the current blocked list. Synchronous — no network call. */
243
+ /**
244
+ * Returns true if the IP should be blocked. Synchronous — no network call.
245
+ *
246
+ * Checks two independent sources:
247
+ * 1. Manual blocklist — IPs explicitly blocked by the customer or via playbooks.
248
+ * 2. Community threats — IPs from Anomira's federated network whose confidence
249
+ * score meets the autoBlock.communityThreshold (default 85).
250
+ * Only active when autoBlock.enabled is true (the default).
251
+ *
252
+ * If a customer sets autoBlock.enabled = false, only the manual blocklist is checked.
253
+ */
188
254
  isBlocked(ip: string): boolean;
255
+ /**
256
+ * Returns the block reason for an IP — useful for logging or custom responses.
257
+ * Returns null if the IP is not blocked.
258
+ */
259
+ blockReason(ip: string): {
260
+ source: "manual" | "community";
261
+ score?: number;
262
+ topAttack?: string;
263
+ } | null;
189
264
  /**
190
265
  * Fire-and-forget: report a blocked-IP attempt to the ingest server so the
191
266
  * dashboard can show that the block is actively working.
@@ -206,7 +281,18 @@ declare class AnomiraClient {
206
281
  rule: FirewallRule;
207
282
  } | null;
208
283
  track(eventName: string, data: {
209
- ip: string;
284
+ /**
285
+ * Client IP address. Optional — when omitted or empty the SDK
286
+ * automatically reads it from the active request context set by the
287
+ * Express/Fastify middleware. This means developers calling track()
288
+ * inside a request handler get the correct IP for free, with no extra
289
+ * configuration required.
290
+ *
291
+ * Only pass this explicitly when calling track() outside a request
292
+ * context (e.g. from a background job or a webhook handler that has
293
+ * the IP available separately).
294
+ */
295
+ ip?: string;
210
296
  userId?: string;
211
297
  meta?: Record<string, unknown>;
212
298
  }): void;
@@ -223,7 +309,7 @@ declare class AnomiraClient {
223
309
  * ```
224
310
  */
225
311
  trackLogin(data: {
226
- ip: string;
312
+ ip?: string;
227
313
  userId: string;
228
314
  meta?: Record<string, unknown>;
229
315
  }): Promise<void>;
@@ -242,7 +328,7 @@ declare class AnomiraClient {
242
328
  * ```
243
329
  */
244
330
  trackPhoneAuth(data: {
245
- ip: string;
331
+ ip?: string;
246
332
  userId: string;
247
333
  phone: string;
248
334
  meta?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -78,6 +78,43 @@ interface AnomiraConfig {
78
78
  * Default: reads X-Forwarded-For → X-Real-IP → socket.remoteAddress
79
79
  */
80
80
  getIp?: (req: unknown) => string;
81
+ /**
82
+ * Invisible security — automatic blocking from Anomira's community
83
+ * threat intelligence network.
84
+ *
85
+ * When enabled (the default), the SDK periodically fetches high-confidence
86
+ * attacker IPs from the Anomira network — IPs that have been seen attacking
87
+ * multiple customers — and blocks them automatically, without any manual
88
+ * decision required.
89
+ *
90
+ * This is the "invisible" part: your API is protected from known bad actors
91
+ * the moment they appear in the network, before they even reach your handlers.
92
+ *
93
+ * Set `enabled: false` to disable auto-blocking and use threat data for
94
+ * alerting only (the data is still fetched and logged).
95
+ */
96
+ autoBlock?: {
97
+ /**
98
+ * Whether to automatically block IPs from the community threat network.
99
+ * @default true
100
+ */
101
+ enabled?: boolean;
102
+ /**
103
+ * Minimum community confidence score (0-100) to trigger an auto-block.
104
+ * Only IPs above this threshold are blocked automatically.
105
+ *
106
+ * @default 85
107
+ *
108
+ * Guidance:
109
+ * 85 (default) — confirmed malicious across multiple customers, very low false-positive risk
110
+ * 70 — broader coverage, slightly higher false-positive risk
111
+ * 95 — maximum precision, only the most confirmed threats
112
+ *
113
+ * Do not set below 60 — scores below that reflect limited data and carry
114
+ * meaningful false-positive risk.
115
+ */
116
+ communityThreshold?: number;
117
+ };
81
118
  }
82
119
  interface SdkEvent {
83
120
  name: string;
@@ -150,22 +187,41 @@ interface FirewallRule {
150
187
  */
151
188
  declare class AnomiraClient {
152
189
  #private;
153
- readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect">> & {
190
+ readonly config: Required<Omit<AnomiraConfig, "getUserId" | "getIp" | "detect" | "autoBlock">> & {
154
191
  getUserId: NonNullable<AnomiraConfig["getUserId"]>;
155
192
  getIp: NonNullable<AnomiraConfig["getIp"]>;
156
193
  detect: Required<NonNullable<AnomiraConfig["detect"]>>;
157
194
  captureConsole: boolean;
158
195
  service: string;
196
+ autoBlock: Required<NonNullable<AnomiraConfig["autoBlock"]>>;
159
197
  };
160
198
  private readonly buffer;
161
199
  private readonly logBuffer;
162
200
  private logFlushTimer;
163
201
  private blocklistTimer;
164
202
  private firewallTimer;
203
+ private communityThreatTimer;
165
204
  /** True when credentials are missing — all operations become no-ops. */
166
205
  private disabled;
167
- /** In-process cache of blocked IPs — refreshed every 60 s from the ingest server. */
206
+ /** In-process cache of manually blocked IPs — refreshed every 60 s. */
168
207
  private blockedIpCache;
208
+ /** In-process cache of community threat IPs with their confidence scores.
209
+ * Populated from Anomira's federated threat network — IPs confirmed malicious
210
+ * across multiple customers. Refreshed every 60 s. */
211
+ private communityThreatCache;
212
+ /**
213
+ * Set of honeypot paths to intercept. When a request matches, the SDK returns
214
+ * a fake response instead of forwarding to the customer's route handlers.
215
+ * Synced from ingest every 60 s. Keys are lowercase path strings.
216
+ */
217
+ private honeypotPaths;
218
+ /**
219
+ * Set of active canary token strings embedded in previously-served honeypot
220
+ * responses. When any of these appear in a request's Authorization header or
221
+ * body, a http.canary.triggered event is fired.
222
+ * Synced from ingest every 60 s (max 100 tokens, sliding 7-day window).
223
+ */
224
+ private canaryTokenCache;
169
225
  /** In-process cache of firewall rules with pre-compiled regex — refreshed every 60 s. */
170
226
  private compiledRules;
171
227
  private readonly _origLog;
@@ -184,8 +240,27 @@ declare class AnomiraClient {
184
240
  * });
185
241
  * ```
186
242
  */
187
- /** Returns true if the IP is in the current blocked list. Synchronous — no network call. */
243
+ /**
244
+ * Returns true if the IP should be blocked. Synchronous — no network call.
245
+ *
246
+ * Checks two independent sources:
247
+ * 1. Manual blocklist — IPs explicitly blocked by the customer or via playbooks.
248
+ * 2. Community threats — IPs from Anomira's federated network whose confidence
249
+ * score meets the autoBlock.communityThreshold (default 85).
250
+ * Only active when autoBlock.enabled is true (the default).
251
+ *
252
+ * If a customer sets autoBlock.enabled = false, only the manual blocklist is checked.
253
+ */
188
254
  isBlocked(ip: string): boolean;
255
+ /**
256
+ * Returns the block reason for an IP — useful for logging or custom responses.
257
+ * Returns null if the IP is not blocked.
258
+ */
259
+ blockReason(ip: string): {
260
+ source: "manual" | "community";
261
+ score?: number;
262
+ topAttack?: string;
263
+ } | null;
189
264
  /**
190
265
  * Fire-and-forget: report a blocked-IP attempt to the ingest server so the
191
266
  * dashboard can show that the block is actively working.
@@ -206,7 +281,18 @@ declare class AnomiraClient {
206
281
  rule: FirewallRule;
207
282
  } | null;
208
283
  track(eventName: string, data: {
209
- ip: string;
284
+ /**
285
+ * Client IP address. Optional — when omitted or empty the SDK
286
+ * automatically reads it from the active request context set by the
287
+ * Express/Fastify middleware. This means developers calling track()
288
+ * inside a request handler get the correct IP for free, with no extra
289
+ * configuration required.
290
+ *
291
+ * Only pass this explicitly when calling track() outside a request
292
+ * context (e.g. from a background job or a webhook handler that has
293
+ * the IP available separately).
294
+ */
295
+ ip?: string;
210
296
  userId?: string;
211
297
  meta?: Record<string, unknown>;
212
298
  }): void;
@@ -223,7 +309,7 @@ declare class AnomiraClient {
223
309
  * ```
224
310
  */
225
311
  trackLogin(data: {
226
- ip: string;
312
+ ip?: string;
227
313
  userId: string;
228
314
  meta?: Record<string, unknown>;
229
315
  }): Promise<void>;
@@ -242,7 +328,7 @@ declare class AnomiraClient {
242
328
  * ```
243
329
  */
244
330
  trackPhoneAuth(data: {
245
- ip: string;
331
+ ip?: string;
246
332
  userId: string;
247
333
  phone: string;
248
334
  meta?: Record<string, unknown>;