@push.rocks/smartmta 5.1.3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/changelog.md +7 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/index.d.ts +3 -0
  5. package/dist_ts/index.js +4 -0
  6. package/dist_ts/logger.d.ts +17 -0
  7. package/dist_ts/logger.js +76 -0
  8. package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
  9. package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
  10. package/dist_ts/mail/core/classes.email.d.ts +291 -0
  11. package/dist_ts/mail/core/classes.email.js +802 -0
  12. package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
  13. package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
  14. package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
  15. package/dist_ts/mail/core/classes.templatemanager.js +240 -0
  16. package/dist_ts/mail/core/index.d.ts +4 -0
  17. package/dist_ts/mail/core/index.js +6 -0
  18. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
  19. package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
  20. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
  21. package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
  22. package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
  23. package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
  24. package/dist_ts/mail/delivery/index.d.ts +4 -0
  25. package/dist_ts/mail/delivery/index.js +6 -0
  26. package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
  27. package/dist_ts/mail/delivery/interfaces.js +17 -0
  28. package/dist_ts/mail/index.d.ts +7 -0
  29. package/dist_ts/mail/index.js +12 -0
  30. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
  31. package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
  32. package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
  33. package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
  34. package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
  35. package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
  36. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
  37. package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
  38. package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
  39. package/dist_ts/mail/routing/classes.email.router.js +494 -0
  40. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
  41. package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
  42. package/dist_ts/mail/routing/index.d.ts +7 -0
  43. package/dist_ts/mail/routing/index.js +9 -0
  44. package/dist_ts/mail/routing/interfaces.d.ts +187 -0
  45. package/dist_ts/mail/routing/interfaces.js +2 -0
  46. package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
  47. package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
  48. package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
  49. package/dist_ts/mail/security/classes.spfverifier.js +87 -0
  50. package/dist_ts/mail/security/index.d.ts +2 -0
  51. package/dist_ts/mail/security/index.js +4 -0
  52. package/dist_ts/paths.d.ts +14 -0
  53. package/dist_ts/paths.js +39 -0
  54. package/dist_ts/plugins.d.ts +24 -0
  55. package/dist_ts/plugins.js +28 -0
  56. package/dist_ts/security/classes.contentscanner.d.ts +130 -0
  57. package/dist_ts/security/classes.contentscanner.js +338 -0
  58. package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
  59. package/dist_ts/security/classes.ipreputationchecker.js +263 -0
  60. package/dist_ts/security/classes.rustsecuritybridge.d.ts +398 -0
  61. package/dist_ts/security/classes.rustsecuritybridge.js +484 -0
  62. package/dist_ts/security/classes.securitylogger.d.ts +140 -0
  63. package/dist_ts/security/classes.securitylogger.js +235 -0
  64. package/dist_ts/security/index.d.ts +4 -0
  65. package/dist_ts/security/index.js +5 -0
  66. package/package.json +6 -1
  67. package/ts/00_commitinfo_data.ts +8 -0
  68. package/ts/index.ts +3 -0
  69. package/ts/logger.ts +91 -0
  70. package/ts/mail/core/classes.bouncemanager.ts +731 -0
  71. package/ts/mail/core/classes.email.ts +942 -0
  72. package/ts/mail/core/classes.emailvalidator.ts +239 -0
  73. package/ts/mail/core/classes.templatemanager.ts +320 -0
  74. package/ts/mail/core/index.ts +5 -0
  75. package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
  76. package/ts/mail/delivery/classes.delivery.system.ts +816 -0
  77. package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
  78. package/ts/mail/delivery/index.ts +5 -0
  79. package/ts/mail/delivery/interfaces.ts +167 -0
  80. package/ts/mail/index.ts +17 -0
  81. package/ts/mail/routing/classes.dkim.manager.ts +157 -0
  82. package/ts/mail/routing/classes.dns.manager.ts +573 -0
  83. package/ts/mail/routing/classes.domain.registry.ts +139 -0
  84. package/ts/mail/routing/classes.email.action.executor.ts +175 -0
  85. package/ts/mail/routing/classes.email.router.ts +575 -0
  86. package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
  87. package/ts/mail/routing/index.ts +9 -0
  88. package/ts/mail/routing/interfaces.ts +202 -0
  89. package/ts/mail/security/classes.dkimcreator.ts +447 -0
  90. package/ts/mail/security/classes.spfverifier.ts +126 -0
  91. package/ts/mail/security/index.ts +3 -0
  92. package/ts/paths.ts +48 -0
  93. package/ts/plugins.ts +53 -0
  94. package/ts/security/classes.contentscanner.ts +400 -0
  95. package/ts/security/classes.ipreputationchecker.ts +315 -0
  96. package/ts/security/classes.rustsecuritybridge.ts +943 -0
  97. package/ts/security/classes.securitylogger.ts +299 -0
  98. package/ts/security/index.ts +40 -0
@@ -0,0 +1,484 @@
1
+ import * as plugins from '../plugins.js';
2
+ import * as paths from '../paths.js';
3
+ import { logger } from '../logger.js';
4
+ import { EventEmitter } from 'events';
5
+ // ---------------------------------------------------------------------------
6
+ // Bridge state machine
7
+ // ---------------------------------------------------------------------------
8
+ export var BridgeState;
9
+ (function (BridgeState) {
10
+ BridgeState["Idle"] = "idle";
11
+ BridgeState["Starting"] = "starting";
12
+ BridgeState["Running"] = "running";
13
+ BridgeState["Restarting"] = "restarting";
14
+ BridgeState["Failed"] = "failed";
15
+ BridgeState["Stopped"] = "stopped";
16
+ })(BridgeState || (BridgeState = {}));
17
+ const DEFAULT_RESILIENCE_CONFIG = {
18
+ maxRestartAttempts: 5,
19
+ healthCheckIntervalMs: 30_000,
20
+ restartBackoffBaseMs: 1_000,
21
+ restartBackoffMaxMs: 30_000,
22
+ healthCheckTimeoutMs: 5_000,
23
+ };
24
+ // ---------------------------------------------------------------------------
25
+ // RustSecurityBridge — singleton wrapper around smartrust.RustBridge
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Bridge between TypeScript and the Rust `mailer-bin` binary.
29
+ *
30
+ * Uses `@push.rocks/smartrust` for JSON-over-stdin/stdout IPC.
31
+ * Singleton — access via `RustSecurityBridge.getInstance()`.
32
+ *
33
+ * Features resilience via auto-restart with exponential backoff,
34
+ * periodic health checks, and a state machine that tracks the
35
+ * bridge lifecycle.
36
+ */
37
+ export class RustSecurityBridge extends EventEmitter {
38
+ static instance = null;
39
+ static _resilienceConfig = { ...DEFAULT_RESILIENCE_CONFIG };
40
+ bridge;
41
+ _running = false;
42
+ _state = BridgeState.Idle;
43
+ _restartAttempts = 0;
44
+ _restartTimer = null;
45
+ _healthCheckTimer = null;
46
+ _deliberateStop = false;
47
+ _smtpServerConfig = null;
48
+ constructor() {
49
+ super();
50
+ this.bridge = new plugins.smartrust.RustBridge({
51
+ binaryName: 'mailer-bin',
52
+ cliArgs: ['--management'],
53
+ requestTimeoutMs: 30_000,
54
+ readyTimeoutMs: 10_000,
55
+ localPaths: [
56
+ plugins.path.join(paths.packageDir, 'dist_rust', 'mailer-bin'),
57
+ plugins.path.join(paths.packageDir, 'rust', 'target', 'release', 'mailer-bin'),
58
+ plugins.path.join(paths.packageDir, 'rust', 'target', 'debug', 'mailer-bin'),
59
+ ],
60
+ searchSystemPath: false,
61
+ });
62
+ // Forward lifecycle events
63
+ this.bridge.on('ready', () => {
64
+ this._running = true;
65
+ logger.log('info', 'Rust security bridge is ready');
66
+ });
67
+ this.bridge.on('exit', (code, signal) => {
68
+ this._running = false;
69
+ logger.log('warn', `Rust security bridge exited (code=${code}, signal=${signal})`);
70
+ if (this._deliberateStop) {
71
+ this.setState(BridgeState.Stopped);
72
+ }
73
+ else if (this._state === BridgeState.Running) {
74
+ // Unexpected exit — attempt restart
75
+ this.attemptRestart();
76
+ }
77
+ });
78
+ this.bridge.on('stderr', (line) => {
79
+ logger.log('debug', `[rust-bridge] ${line}`);
80
+ });
81
+ }
82
+ // -----------------------------------------------------------------------
83
+ // Static configuration & singleton
84
+ // -----------------------------------------------------------------------
85
+ /** Get or create the singleton instance. */
86
+ static getInstance() {
87
+ if (!RustSecurityBridge.instance) {
88
+ RustSecurityBridge.instance = new RustSecurityBridge();
89
+ }
90
+ return RustSecurityBridge.instance;
91
+ }
92
+ /** Reset the singleton instance (for testing). */
93
+ static resetInstance() {
94
+ if (RustSecurityBridge.instance) {
95
+ RustSecurityBridge.instance.stopHealthCheck();
96
+ if (RustSecurityBridge.instance._restartTimer) {
97
+ clearTimeout(RustSecurityBridge.instance._restartTimer);
98
+ RustSecurityBridge.instance._restartTimer = null;
99
+ }
100
+ RustSecurityBridge.instance.removeAllListeners();
101
+ }
102
+ RustSecurityBridge.instance = null;
103
+ }
104
+ /** Configure resilience parameters. Can be called before or after getInstance(). */
105
+ static configure(config) {
106
+ RustSecurityBridge._resilienceConfig = {
107
+ ...RustSecurityBridge._resilienceConfig,
108
+ ...config,
109
+ };
110
+ }
111
+ // -----------------------------------------------------------------------
112
+ // State management
113
+ // -----------------------------------------------------------------------
114
+ /** Current bridge state. */
115
+ get state() {
116
+ return this._state;
117
+ }
118
+ /** Whether the Rust process is currently running and accepting commands. */
119
+ get running() {
120
+ return this._running;
121
+ }
122
+ setState(newState) {
123
+ const oldState = this._state;
124
+ if (oldState === newState)
125
+ return;
126
+ this._state = newState;
127
+ logger.log('info', `Rust bridge state: ${oldState} -> ${newState}`);
128
+ this.emit('stateChange', { oldState, newState });
129
+ }
130
+ /**
131
+ * Throws a descriptive error if the bridge is not in Running state.
132
+ * Called at the top of every command method.
133
+ */
134
+ ensureRunning() {
135
+ if (this._state === BridgeState.Running && this._running) {
136
+ return;
137
+ }
138
+ switch (this._state) {
139
+ case BridgeState.Idle:
140
+ throw new Error('Rust bridge has not been started yet. Call start() first.');
141
+ case BridgeState.Starting:
142
+ throw new Error('Rust bridge is still starting. Wait for start() to resolve.');
143
+ case BridgeState.Restarting:
144
+ throw new Error('Rust bridge is restarting after a crash. Commands will resume once it recovers.');
145
+ case BridgeState.Failed:
146
+ throw new Error('Rust bridge has failed after exhausting all restart attempts.');
147
+ case BridgeState.Stopped:
148
+ throw new Error('Rust bridge has been stopped. Call start() to restart it.');
149
+ default:
150
+ throw new Error(`Rust bridge is not running (state=${this._state}).`);
151
+ }
152
+ }
153
+ // -----------------------------------------------------------------------
154
+ // Lifecycle
155
+ // -----------------------------------------------------------------------
156
+ /**
157
+ * Spawn the Rust binary and wait for the ready signal.
158
+ * @returns `true` if the binary started successfully, `false` otherwise.
159
+ */
160
+ async start() {
161
+ if (this._running && this._state === BridgeState.Running) {
162
+ return true;
163
+ }
164
+ this._deliberateStop = false;
165
+ this._restartAttempts = 0;
166
+ this.setState(BridgeState.Starting);
167
+ try {
168
+ const ok = await this.bridge.spawn();
169
+ this._running = ok;
170
+ if (ok) {
171
+ this.setState(BridgeState.Running);
172
+ this.startHealthCheck();
173
+ logger.log('info', 'Rust security bridge started');
174
+ }
175
+ else {
176
+ this.setState(BridgeState.Failed);
177
+ logger.log('warn', 'Rust security bridge failed to start (binary not found or timeout)');
178
+ }
179
+ return ok;
180
+ }
181
+ catch (err) {
182
+ this.setState(BridgeState.Failed);
183
+ logger.log('error', `Failed to start Rust security bridge: ${err.message}`);
184
+ return false;
185
+ }
186
+ }
187
+ /** Kill the Rust process deliberately. */
188
+ async stop() {
189
+ this._deliberateStop = true;
190
+ // Cancel any pending restart
191
+ if (this._restartTimer) {
192
+ clearTimeout(this._restartTimer);
193
+ this._restartTimer = null;
194
+ }
195
+ this.stopHealthCheck();
196
+ this._smtpServerConfig = null;
197
+ if (!this._running) {
198
+ this.setState(BridgeState.Stopped);
199
+ return;
200
+ }
201
+ try {
202
+ this.bridge.kill();
203
+ this._running = false;
204
+ this.setState(BridgeState.Stopped);
205
+ logger.log('info', 'Rust security bridge stopped');
206
+ }
207
+ catch (err) {
208
+ logger.log('error', `Error stopping Rust security bridge: ${err.message}`);
209
+ }
210
+ }
211
+ // -----------------------------------------------------------------------
212
+ // Auto-restart with exponential backoff
213
+ // -----------------------------------------------------------------------
214
+ attemptRestart() {
215
+ const config = RustSecurityBridge._resilienceConfig;
216
+ this._restartAttempts++;
217
+ if (this._restartAttempts > config.maxRestartAttempts) {
218
+ logger.log('error', `Rust bridge exceeded max restart attempts (${config.maxRestartAttempts}). Giving up.`);
219
+ this.setState(BridgeState.Failed);
220
+ return;
221
+ }
222
+ this.setState(BridgeState.Restarting);
223
+ this.stopHealthCheck();
224
+ const delay = Math.min(config.restartBackoffBaseMs * Math.pow(2, this._restartAttempts - 1), config.restartBackoffMaxMs);
225
+ logger.log('info', `Rust bridge restart attempt ${this._restartAttempts}/${config.maxRestartAttempts} in ${delay}ms`);
226
+ this._restartTimer = setTimeout(async () => {
227
+ this._restartTimer = null;
228
+ // Guard: if stop() was called while we were waiting, don't restart
229
+ if (this._deliberateStop) {
230
+ this.setState(BridgeState.Stopped);
231
+ return;
232
+ }
233
+ try {
234
+ const ok = await this.bridge.spawn();
235
+ this._running = ok;
236
+ if (ok) {
237
+ logger.log('info', 'Rust bridge restarted successfully');
238
+ this._restartAttempts = 0;
239
+ this.setState(BridgeState.Running);
240
+ this.startHealthCheck();
241
+ await this.restoreAfterRestart();
242
+ }
243
+ else {
244
+ logger.log('warn', 'Rust bridge restart failed (spawn returned false)');
245
+ this.attemptRestart();
246
+ }
247
+ }
248
+ catch (err) {
249
+ logger.log('error', `Rust bridge restart failed: ${err.message}`);
250
+ this.attemptRestart();
251
+ }
252
+ }, delay);
253
+ }
254
+ /**
255
+ * Restore state after a successful restart:
256
+ * - Re-send startSmtpServer command if the SMTP server was running
257
+ */
258
+ async restoreAfterRestart() {
259
+ if (this._smtpServerConfig) {
260
+ try {
261
+ logger.log('info', 'Restoring SMTP server after bridge restart');
262
+ const result = await this.bridge.sendCommand('startSmtpServer', this._smtpServerConfig);
263
+ if (result?.started) {
264
+ logger.log('info', 'SMTP server restored after bridge restart');
265
+ }
266
+ else {
267
+ logger.log('warn', 'SMTP server failed to restore after bridge restart');
268
+ }
269
+ }
270
+ catch (err) {
271
+ logger.log('error', `Failed to restore SMTP server after restart: ${err.message}`);
272
+ }
273
+ }
274
+ }
275
+ // -----------------------------------------------------------------------
276
+ // Health check
277
+ // -----------------------------------------------------------------------
278
+ startHealthCheck() {
279
+ this.stopHealthCheck();
280
+ const config = RustSecurityBridge._resilienceConfig;
281
+ this._healthCheckTimer = setInterval(async () => {
282
+ if (this._state !== BridgeState.Running || !this._running) {
283
+ return;
284
+ }
285
+ try {
286
+ const pongPromise = this.bridge.sendCommand('ping', {});
287
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Health check timeout')), config.healthCheckTimeoutMs));
288
+ const res = await Promise.race([pongPromise, timeoutPromise]);
289
+ if (!res?.pong) {
290
+ throw new Error('Health check: unexpected ping response');
291
+ }
292
+ }
293
+ catch (err) {
294
+ logger.log('warn', `Rust bridge health check failed: ${err.message}. Killing process to trigger restart.`);
295
+ try {
296
+ this.bridge.kill();
297
+ }
298
+ catch {
299
+ // Already dead
300
+ }
301
+ // The exit handler will trigger attemptRestart()
302
+ }
303
+ }, config.healthCheckIntervalMs);
304
+ }
305
+ stopHealthCheck() {
306
+ if (this._healthCheckTimer) {
307
+ clearInterval(this._healthCheckTimer);
308
+ this._healthCheckTimer = null;
309
+ }
310
+ }
311
+ // -----------------------------------------------------------------------
312
+ // Commands — thin typed wrappers over sendCommand
313
+ // -----------------------------------------------------------------------
314
+ /** Ping the Rust process. */
315
+ async ping() {
316
+ this.ensureRunning();
317
+ const res = await this.bridge.sendCommand('ping', {});
318
+ return res?.pong === true;
319
+ }
320
+ /** Get version information for all Rust crates. */
321
+ async getVersion() {
322
+ this.ensureRunning();
323
+ return this.bridge.sendCommand('version', {});
324
+ }
325
+ /** Validate an email address. */
326
+ async validateEmail(email) {
327
+ this.ensureRunning();
328
+ return this.bridge.sendCommand('validateEmail', { email });
329
+ }
330
+ /** Detect bounce type from SMTP response / diagnostic code. */
331
+ async detectBounce(opts) {
332
+ this.ensureRunning();
333
+ return this.bridge.sendCommand('detectBounce', opts);
334
+ }
335
+ /** Scan email content for threats (phishing, spam, malware, etc.). */
336
+ async scanContent(opts) {
337
+ this.ensureRunning();
338
+ return this.bridge.sendCommand('scanContent', opts);
339
+ }
340
+ /** Check IP reputation via DNSBL. */
341
+ async checkIpReputation(ip) {
342
+ this.ensureRunning();
343
+ return this.bridge.sendCommand('checkIpReputation', { ip });
344
+ }
345
+ /** Verify DKIM signatures on a raw email message. */
346
+ async verifyDkim(rawMessage) {
347
+ this.ensureRunning();
348
+ return this.bridge.sendCommand('verifyDkim', { rawMessage });
349
+ }
350
+ /** Sign an email with DKIM (RSA or Ed25519). */
351
+ async signDkim(opts) {
352
+ this.ensureRunning();
353
+ return this.bridge.sendCommand('signDkim', opts);
354
+ }
355
+ /** Check SPF for a sender. */
356
+ async checkSpf(opts) {
357
+ this.ensureRunning();
358
+ return this.bridge.sendCommand('checkSpf', opts);
359
+ }
360
+ /**
361
+ * Compound email security verification: DKIM + SPF + DMARC in one IPC call.
362
+ *
363
+ * This is the preferred method for inbound email verification — it avoids
364
+ * 3 sequential round-trips and correctly passes raw mail-auth types internally.
365
+ */
366
+ async verifyEmail(opts) {
367
+ this.ensureRunning();
368
+ return this.bridge.sendCommand('verifyEmail', opts);
369
+ }
370
+ // -----------------------------------------------------------------------
371
+ // SMTP Client — outbound email delivery via Rust
372
+ // -----------------------------------------------------------------------
373
+ /** Send a structured email via the Rust SMTP client. */
374
+ async sendOutboundEmail(opts) {
375
+ this.ensureRunning();
376
+ return this.bridge.sendCommand('sendEmail', opts);
377
+ }
378
+ /** Send a pre-formatted raw email via the Rust SMTP client. */
379
+ async sendRawEmail(opts) {
380
+ this.ensureRunning();
381
+ return this.bridge.sendCommand('sendRawEmail', opts);
382
+ }
383
+ /** Verify connectivity to an SMTP server. */
384
+ async verifySmtpConnection(opts) {
385
+ this.ensureRunning();
386
+ return this.bridge.sendCommand('verifySmtpConnection', opts);
387
+ }
388
+ /** Close a specific connection pool (or all pools if no key is given). */
389
+ async closeSmtpPool(poolKey) {
390
+ this.ensureRunning();
391
+ await this.bridge.sendCommand('closeSmtpPool', poolKey ? { poolKey } : {});
392
+ }
393
+ /** Get status of all SMTP client connection pools. */
394
+ async getSmtpPoolStatus() {
395
+ this.ensureRunning();
396
+ return this.bridge.sendCommand('getSmtpPoolStatus', {});
397
+ }
398
+ // -----------------------------------------------------------------------
399
+ // SMTP Server lifecycle
400
+ // -----------------------------------------------------------------------
401
+ /**
402
+ * Start the Rust SMTP server.
403
+ * The server will listen on the configured ports and emit events for
404
+ * emailReceived and authRequest that must be handled by the caller.
405
+ */
406
+ async startSmtpServer(config) {
407
+ this.ensureRunning();
408
+ this._smtpServerConfig = config;
409
+ const result = await this.bridge.sendCommand('startSmtpServer', config);
410
+ return result?.started === true;
411
+ }
412
+ /** Stop the Rust SMTP server. */
413
+ async stopSmtpServer() {
414
+ this.ensureRunning();
415
+ this._smtpServerConfig = null;
416
+ await this.bridge.sendCommand('stopSmtpServer', {});
417
+ }
418
+ /**
419
+ * Send the result of email processing back to the Rust SMTP server.
420
+ * This resolves a pending correlation-ID callback, allowing the Rust
421
+ * server to send the SMTP response to the client.
422
+ */
423
+ async sendEmailProcessingResult(opts) {
424
+ this.ensureRunning();
425
+ await this.bridge.sendCommand('emailProcessingResult', opts);
426
+ }
427
+ /**
428
+ * Send the result of authentication validation back to the Rust SMTP server.
429
+ */
430
+ async sendAuthResult(opts) {
431
+ this.ensureRunning();
432
+ await this.bridge.sendCommand('authResult', opts);
433
+ }
434
+ /**
435
+ * Send SCRAM credentials back to the Rust SMTP server.
436
+ * Values (salt, storedKey, serverKey) must be base64-encoded.
437
+ */
438
+ async sendScramCredentialResult(opts) {
439
+ this.ensureRunning();
440
+ await this.bridge.sendCommand('scramCredentialResult', opts);
441
+ }
442
+ /** Update rate limit configuration at runtime. */
443
+ async configureRateLimits(config) {
444
+ this.ensureRunning();
445
+ await this.bridge.sendCommand('configureRateLimits', config);
446
+ }
447
+ // -----------------------------------------------------------------------
448
+ // Event registration — delegates to the underlying bridge EventEmitter
449
+ // -----------------------------------------------------------------------
450
+ /**
451
+ * Register a handler for emailReceived events from the Rust SMTP server.
452
+ * These events fire when a complete email has been received and needs processing.
453
+ */
454
+ onEmailReceived(handler) {
455
+ this.bridge.on('management:emailReceived', handler);
456
+ }
457
+ /**
458
+ * Register a handler for authRequest events from the Rust SMTP server.
459
+ * The handler must call sendAuthResult() with the correlationId.
460
+ */
461
+ onAuthRequest(handler) {
462
+ this.bridge.on('management:authRequest', handler);
463
+ }
464
+ /**
465
+ * Register a handler for scramCredentialRequest events from the Rust SMTP server.
466
+ * The handler must call sendScramCredentialResult() with the correlationId.
467
+ */
468
+ onScramCredentialRequest(handler) {
469
+ this.bridge.on('management:scramCredentialRequest', handler);
470
+ }
471
+ /** Remove an emailReceived event handler. */
472
+ offEmailReceived(handler) {
473
+ this.bridge.off('management:emailReceived', handler);
474
+ }
475
+ /** Remove an authRequest event handler. */
476
+ offAuthRequest(handler) {
477
+ this.bridge.off('management:authRequest', handler);
478
+ }
479
+ /** Remove a scramCredentialRequest event handler. */
480
+ offScramCredentialRequest(handler) {
481
+ this.bridge.off('management:scramCredentialRequest', handler);
482
+ }
483
+ }
484
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydXN0c2VjdXJpdHlicmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9zZWN1cml0eS9jbGFzc2VzLnJ1c3RzZWN1cml0eWJyaWRnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEtBQUssS0FBSyxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxRQUFRLENBQUM7QUE4VHRDLDhFQUE4RTtBQUM5RSx1QkFBdUI7QUFDdkIsOEVBQThFO0FBRTlFLE1BQU0sQ0FBTixJQUFZLFdBT1g7QUFQRCxXQUFZLFdBQVc7SUFDckIsNEJBQWEsQ0FBQTtJQUNiLG9DQUFxQixDQUFBO0lBQ3JCLGtDQUFtQixDQUFBO0lBQ25CLHdDQUF5QixDQUFBO0lBQ3pCLGdDQUFpQixDQUFBO0lBQ2pCLGtDQUFtQixDQUFBO0FBQ3JCLENBQUMsRUFQVyxXQUFXLEtBQVgsV0FBVyxRQU90QjtBQVVELE1BQU0seUJBQXlCLEdBQTRCO0lBQ3pELGtCQUFrQixFQUFFLENBQUM7SUFDckIscUJBQXFCLEVBQUUsTUFBTTtJQUM3QixvQkFBb0IsRUFBRSxLQUFLO0lBQzNCLG1CQUFtQixFQUFFLE1BQU07SUFDM0Isb0JBQW9CLEVBQUUsS0FBSztDQUM1QixDQUFDO0FBRUYsOEVBQThFO0FBQzlFLHFFQUFxRTtBQUNyRSw4RUFBOEU7QUFFOUU7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxPQUFPLGtCQUFtQixTQUFRLFlBQVk7SUFDMUMsTUFBTSxDQUFDLFFBQVEsR0FBOEIsSUFBSSxDQUFDO0lBQ2xELE1BQU0sQ0FBQyxpQkFBaUIsR0FBNEIsRUFBRSxHQUFHLHlCQUF5QixFQUFFLENBQUM7SUFFckYsTUFBTSxDQUFxRTtJQUMzRSxRQUFRLEdBQUcsS0FBSyxD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
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Log level for security events
3
+ */
4
+ export declare enum SecurityLogLevel {
5
+ INFO = "info",
6
+ WARN = "warn",
7
+ ERROR = "error",
8
+ CRITICAL = "critical"
9
+ }
10
+ /**
11
+ * Security event types for categorization
12
+ */
13
+ export declare enum SecurityEventType {
14
+ AUTHENTICATION = "authentication",
15
+ ACCESS_CONTROL = "access_control",
16
+ EMAIL_VALIDATION = "email_validation",
17
+ EMAIL_PROCESSING = "email_processing",
18
+ EMAIL_FORWARDING = "email_forwarding",
19
+ EMAIL_DELIVERY = "email_delivery",
20
+ DKIM = "dkim",
21
+ SPF = "spf",
22
+ DMARC = "dmarc",
23
+ RATE_LIMIT = "rate_limit",
24
+ RATE_LIMITING = "rate_limiting",
25
+ SPAM = "spam",
26
+ MALWARE = "malware",
27
+ CONNECTION = "connection",
28
+ DATA_EXPOSURE = "data_exposure",
29
+ CONFIGURATION = "configuration",
30
+ IP_REPUTATION = "ip_reputation",
31
+ REJECTED_CONNECTION = "rejected_connection"
32
+ }
33
+ /**
34
+ * Security event interface
35
+ */
36
+ export interface ISecurityEvent {
37
+ timestamp: number;
38
+ level: SecurityLogLevel;
39
+ type: SecurityEventType;
40
+ message: string;
41
+ details?: any;
42
+ ipAddress?: string;
43
+ userId?: string;
44
+ sessionId?: string;
45
+ emailId?: string;
46
+ domain?: string;
47
+ action?: string;
48
+ result?: string;
49
+ success?: boolean;
50
+ }
51
+ /**
52
+ * Security logger for enhanced security monitoring
53
+ */
54
+ export declare class SecurityLogger {
55
+ private static instance;
56
+ private securityEvents;
57
+ private maxEventHistory;
58
+ private enableNotifications;
59
+ private constructor();
60
+ /**
61
+ * Get singleton instance
62
+ */
63
+ static getInstance(options?: {
64
+ maxEventHistory?: number;
65
+ enableNotifications?: boolean;
66
+ }): SecurityLogger;
67
+ /**
68
+ * Log a security event
69
+ * @param event The security event to log
70
+ */
71
+ logEvent(event: Omit<ISecurityEvent, 'timestamp'>): void;
72
+ /**
73
+ * Get recent security events
74
+ * @param limit Maximum number of events to return
75
+ * @param filter Filter for specific event types
76
+ * @returns Recent security events
77
+ */
78
+ getRecentEvents(limit?: number, filter?: {
79
+ level?: SecurityLogLevel;
80
+ type?: SecurityEventType;
81
+ fromTimestamp?: number;
82
+ toTimestamp?: number;
83
+ }): ISecurityEvent[];
84
+ /**
85
+ * Get events by security level
86
+ * @param level The security level to filter by
87
+ * @param limit Maximum number of events to return
88
+ * @returns Security events matching the level
89
+ */
90
+ getEventsByLevel(level: SecurityLogLevel, limit?: number): ISecurityEvent[];
91
+ /**
92
+ * Get events by security type
93
+ * @param type The event type to filter by
94
+ * @param limit Maximum number of events to return
95
+ * @returns Security events matching the type
96
+ */
97
+ getEventsByType(type: SecurityEventType, limit?: number): ISecurityEvent[];
98
+ /**
99
+ * Get security events for a specific IP address
100
+ * @param ipAddress The IP address to filter by
101
+ * @param limit Maximum number of events to return
102
+ * @returns Security events for the IP address
103
+ */
104
+ getEventsByIP(ipAddress: string, limit?: number): ISecurityEvent[];
105
+ /**
106
+ * Get security events for a specific domain
107
+ * @param domain The domain to filter by
108
+ * @param limit Maximum number of events to return
109
+ * @returns Security events for the domain
110
+ */
111
+ getEventsByDomain(domain: string, limit?: number): ISecurityEvent[];
112
+ /**
113
+ * Send a notification for critical security events
114
+ * @param event The security event to notify about
115
+ * @private
116
+ */
117
+ private sendNotification;
118
+ /**
119
+ * Clear event history
120
+ */
121
+ clearEvents(): void;
122
+ /**
123
+ * Get statistical summary of security events
124
+ * @param timeWindow Optional time window in milliseconds
125
+ * @returns Summary of security events
126
+ */
127
+ getEventsSummary(timeWindow?: number): {
128
+ total: number;
129
+ byLevel: Record<SecurityLogLevel, number>;
130
+ byType: Record<SecurityEventType, number>;
131
+ topIPs: Array<{
132
+ ip: string;
133
+ count: number;
134
+ }>;
135
+ topDomains: Array<{
136
+ domain: string;
137
+ count: number;
138
+ }>;
139
+ };
140
+ }