@push.rocks/smartmta 6.5.2 → 8.0.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 (84) hide show
  1. package/changelog.md +40 -0
  2. package/dist_rust/mailer-bin_linux_amd64 +0 -0
  3. package/dist_rust/mailer-bin_linux_arm64 +0 -0
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/functions.errors.d.ts +3 -0
  6. package/dist_ts/functions.errors.js +8 -0
  7. package/dist_ts/index.d.ts +3 -0
  8. package/dist_ts/index.js +4 -1
  9. package/dist_ts/mail/core/classes.bouncemanager.d.ts +16 -12
  10. package/dist_ts/mail/core/classes.bouncemanager.js +146 -129
  11. package/dist_ts/mail/core/classes.email.js +15 -13
  12. package/dist_ts/mail/core/classes.emailvalidator.d.ts +3 -3
  13. package/dist_ts/mail/core/classes.emailvalidator.js +7 -5
  14. package/dist_ts/mail/core/classes.templatemanager.d.ts +10 -6
  15. package/dist_ts/mail/core/classes.templatemanager.js +35 -51
  16. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +92 -22
  17. package/dist_ts/mail/delivery/classes.delivery.queue.js +738 -151
  18. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +13 -7
  19. package/dist_ts/mail/delivery/classes.delivery.system.js +458 -145
  20. package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +9 -8
  21. package/dist_ts/mail/delivery/functions.safe-observers.d.ts +10 -0
  22. package/dist_ts/mail/delivery/functions.safe-observers.js +37 -0
  23. package/dist_ts/mail/delivery/interfaces.d.ts +21 -0
  24. package/dist_ts/mail/delivery/interfaces.js +1 -1
  25. package/dist_ts/mail/interfaces.storage.d.ts +37 -6
  26. package/dist_ts/mail/interfaces.storage.js +33 -3
  27. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +10 -6
  28. package/dist_ts/mail/routing/classes.dkim.manager.js +47 -31
  29. package/dist_ts/mail/routing/classes.dns.manager.d.ts +7 -5
  30. package/dist_ts/mail/routing/classes.dns.manager.js +22 -11
  31. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +2 -1
  32. package/dist_ts/mail/routing/classes.email.action.executor.js +45 -16
  33. package/dist_ts/mail/routing/classes.email.router.d.ts +5 -2
  34. package/dist_ts/mail/routing/classes.email.router.js +17 -12
  35. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +12 -6
  36. package/dist_ts/mail/routing/classes.unified.email.server.js +69 -78
  37. package/dist_ts/mail/routing/interfaces.d.ts +0 -2
  38. package/dist_ts/mail/security/classes.dkimcreator.d.ts +22 -45
  39. package/dist_ts/mail/security/classes.dkimcreator.js +93 -296
  40. package/dist_ts/mail/security/classes.spfverifier.js +5 -3
  41. package/dist_ts/paths.d.ts +0 -12
  42. package/dist_ts/paths.js +3 -36
  43. package/dist_ts/plugins.d.ts +2 -5
  44. package/dist_ts/plugins.js +3 -6
  45. package/dist_ts/security/classes.contentscanner.js +14 -12
  46. package/dist_ts/security/classes.ipreputationchecker.d.ts +9 -6
  47. package/dist_ts/security/classes.ipreputationchecker.js +42 -93
  48. package/dist_ts/security/classes.rustsecuritybridge.d.ts +52 -4
  49. package/dist_ts/security/classes.rustsecuritybridge.js +201 -4
  50. package/dist_ts/security/classes.securitylogger.js +7 -5
  51. package/dist_ts/security/index.d.ts +1 -1
  52. package/dist_ts/security/index.js +2 -2
  53. package/package.json +8 -9
  54. package/readme.hints.md +4 -3
  55. package/readme.md +50 -18
  56. package/readme.plan.md +6 -0
  57. package/ts/00_commitinfo_data.ts +1 -1
  58. package/ts/functions.errors.ts +8 -0
  59. package/ts/index.ts +3 -0
  60. package/ts/mail/core/classes.bouncemanager.ts +188 -155
  61. package/ts/mail/core/classes.email.ts +20 -14
  62. package/ts/mail/core/classes.emailvalidator.ts +9 -7
  63. package/ts/mail/core/classes.templatemanager.ts +42 -57
  64. package/ts/mail/delivery/classes.delivery.queue.ts +996 -185
  65. package/ts/mail/delivery/classes.delivery.system.ts +583 -170
  66. package/ts/mail/delivery/classes.unified.rate.limiter.ts +9 -8
  67. package/ts/mail/delivery/functions.safe-observers.ts +45 -0
  68. package/ts/mail/delivery/interfaces.ts +27 -1
  69. package/ts/mail/interfaces.storage.ts +64 -10
  70. package/ts/mail/routing/classes.dkim.manager.ts +65 -40
  71. package/ts/mail/routing/classes.dns.manager.ts +39 -16
  72. package/ts/mail/routing/classes.email.action.executor.ts +64 -17
  73. package/ts/mail/routing/classes.email.router.ts +20 -13
  74. package/ts/mail/routing/classes.unified.email.server.ts +107 -86
  75. package/ts/mail/routing/interfaces.ts +0 -2
  76. package/ts/mail/security/classes.dkimcreator.ts +150 -355
  77. package/ts/mail/security/classes.spfverifier.ts +4 -2
  78. package/ts/paths.ts +2 -41
  79. package/ts/plugins.ts +1 -6
  80. package/ts/security/classes.contentscanner.ts +14 -12
  81. package/ts/security/classes.ipreputationchecker.ts +46 -99
  82. package/ts/security/classes.rustsecuritybridge.ts +272 -6
  83. package/ts/security/classes.securitylogger.ts +6 -4
  84. package/ts/security/index.ts +5 -1
package/readme.md CHANGED
@@ -210,10 +210,10 @@ const emailServer = new UnifiedEmailServer(dcRouterRef, {
210
210
  users: [{ username: 'outbound', password: 'secret' }],
211
211
  },
212
212
 
213
- // TLS certificates
213
+ // TLS material is provided in memory; SmartMTA does not read certificate files.
214
214
  tls: {
215
- certPath: '/etc/ssl/mail.crt',
216
- keyPath: '/etc/ssl/mail.key',
215
+ certPem: await certificateStore.getCertificatePem('mail.example.com'),
216
+ keyPem: await certificateStore.getPrivateKeyPem('mail.example.com'),
217
217
  },
218
218
 
219
219
  outbound: {
@@ -224,9 +224,11 @@ const emailServer = new UnifiedEmailServer(dcRouterRef, {
224
224
  connectionProxyProvider: async ({ host, port, ehloDomain }) => null,
225
225
  },
226
226
 
227
+ // Durable message bodies belong in SmartBucket (or another managed blob store).
228
+ // Use { storageMode: 'memory' } only for deliberately non-durable runtimes.
227
229
  queue: {
228
- storageType: 'disk',
229
- persistentPath: '/var/lib/smartmta/email-queue',
230
+ storageMode: 'managed',
231
+ storageManager: smartBucketBlobStorage,
230
232
  },
231
233
 
232
234
  maxMessageSize: 25 * 1024 * 1024, // 25 MB
@@ -239,7 +241,28 @@ await emailServer.start();
239
241
 
240
242
  > 🔒 **Note:** `start()` will throw if the Rust binary is not compiled. Run `pnpm build` first.
241
243
  >
244
+ > SmartMTA 8 requires Node.js 22 or newer.
245
+ >
242
246
  > `hostname` is the public SMTP identity for greetings and outbound delivery by default. It is not a bind address.
247
+ >
248
+ > `dcRouterRef.storageManager` implements `IStorageManager` for small textual state such as DKIM keys, routes, bounce metadata, and reputation cache; use SmartData for that adapter. Queue bodies and attachment blobs use `IBlobStorageManager`, normally backed by SmartBucket. SmartMTA has no local-filesystem persistence fallback.
249
+
250
+ ### Durable Delivery Checkpoints
251
+
252
+ Managed queues store complete queue items as SmartBucket blobs. Schema V2 adds per-recipient delivery states and a durable bounce outbox:
253
+
254
+ - A recipient becomes `delivered` only after the remote SMTP server's final `250` response.
255
+ - A permanent `5xx RCPT TO` response atomically stores both the recipient's terminal state and its pending bounce operation in one queue-item blob mutation.
256
+ - A temporary `4xx` response remains unresolved, so only that recipient is attempted against the next MX host or a later queue retry. Delivered and permanently rejected recipients are never resent.
257
+ - Bounce operation IDs are deterministic hashes of the queue item, normalized recipient, and operation kind. Pending work is drained immediately at delivery-system startup and periodically afterward, including work on terminal queue items.
258
+ - Observer and event-listener failures are logged and isolated after the durable state transition; they cannot reclassify delivery state or statistics.
259
+
260
+ V1 queue blobs remain readable. They hydrate with an empty bounce outbox and are upgraded to V2 on their next mutation; SmartMTA never invents retroactive bounce work. V2 blobs are intentionally not readable by V7, so rolling back after a V2 write is unsupported.
261
+
262
+ The managed-prefix ownership guard is process-local only. It coordinates queues only when they share the exact same `IBlobStorageManager` object instance and normalized storage prefix. Different storage-wrapper instances and different JavaScript processes are not coordinated; the embedder must enforce one delivery owner or provide an external distributed lease. This guard does not provide distributed ownership. Because queue writes are forward-only to schema V2, V7 rollback remains unsupported after the first V2 mutation.
263
+
264
+ Bounce delivery is at-least-once. `BounceManager` uses the operation ID as a durable processed marker and deduplicates concurrent/restarted calls. Its in-process completed-operation LRU is only a hot-duplicate optimization, bounded to 10,000 entries with a 24-hour TTL; cache misses always re-read the durable marker. A process crash after an external side effect but before the queue completion checkpoint can cause the operation to be presented again. Custom `bounceHandler.processSmtpFailure` implementations receive `options.operationId` and must deduplicate it in their own durable store; SmartMTA does not claim exactly-once execution for custom handlers.
265
+
243
266
 
244
267
  ### 🛂 Inbound Recipient Policy And Relay Control
245
268
 
@@ -378,7 +401,9 @@ DKIM key management is handled by `DKIMCreator`, which generates, stores, and ro
378
401
  ```typescript
379
402
  import { DKIMCreator } from '@push.rocks/smartmta';
380
403
 
381
- const dkimCreator = new DKIMCreator('/path/to/keys', storageManager);
404
+ // Small structured key state is supplied by the embedder through managed storage.
405
+ // Use a SmartData-backed IStorageManager; SmartMTA never writes keys to local files.
406
+ const dkimCreator = new DKIMCreator(storageManager);
382
407
 
383
408
  // Auto-generate keys if they don't exist
384
409
  await dkimCreator.handleDKIMKeysForDomain('example.com');
@@ -397,11 +422,12 @@ if (needsRotation) {
397
422
  ```
398
423
 
399
424
  When `UnifiedEmailServer.start()` is called:
400
- - DKIM keys are generated or loaded for every configured domain
401
- - Signing is applied to all outbound mail via the Rust security bridge
402
- - Key rotation is checked automatically based on your `rotationInterval` config
425
+ - In the default `automatic` mode, DKIM keys are generated or loaded for every configured domain and rotation follows `rotationInterval`.
426
+ - With `dkimKeyProvisioning: 'caller-managed'`, SmartMTA only loads the requested selector from managed storage; it never generates or rotates keys.
427
+ - A requested missing or invalid key fails closed before SMTP delivery. SmartMTA never falls back to unsigned delivery.
428
+ - Signing is applied exactly once, by the Rust SMTP client at final outbound transmission.
403
429
 
404
- Embedders can check `smartMtaCapabilities.selectorCorrectDkimSigning === true` as a compatibility probe. This guarantees selector-aligned DKIM key generation, lookup, and signing for both direct sends and queued delivery.
430
+ Embedders can check `smartMtaCapabilities.selectorCorrectDkimSigning`, `callerManagedDkimKeys`, `structuredSmtpTransactions`, and `typedDeliveryFailures` as compatibility probes.
405
431
 
406
432
  ### 🛡️ Email Authentication (SPF, DKIM, DMARC)
407
433
 
@@ -545,13 +571,16 @@ Automatic bounce detection (via Rust), classification, and suppression tracking:
545
571
  import { Core } from '@push.rocks/smartmta';
546
572
  const { BounceManager } = Core;
547
573
 
548
- const bounceManager = new BounceManager();
574
+ const bounceManager = new BounceManager({ storageManager: smartDataStorage });
549
575
 
550
576
  // Process an SMTP failure
551
577
  const bounce = await bounceManager.processSmtpFailure(
552
578
  'recipient@example.com',
553
579
  '550 5.1.1 User unknown',
554
- { originalEmailId: 'msg-123' }
580
+ {
581
+ operationId: 'queue-item-and-recipient-derived-operation-id',
582
+ originalEmailId: 'msg-123',
583
+ }
555
584
  );
556
585
  // -> { bounceType: 'invalid_recipient', bounceCategory: 'hard', ... }
557
586
 
@@ -597,7 +626,7 @@ const email = await templates.createEmail('welcome', {
597
626
 
598
627
  ### 🌍 DNS Management
599
628
 
600
- When `UnifiedEmailServer.start()` is called, it automatically ensures MX, SPF, DKIM, and DMARC records are in place for all configured domains:
629
+ DNS behavior is explicit per domain and DKIM ownership mode:
601
630
 
602
631
  ```typescript
603
632
  const emailServer = new UnifiedEmailServer(dcRouterRef, {
@@ -611,12 +640,15 @@ const emailServer = new UnifiedEmailServer(dcRouterRef, {
611
640
  // ... other config
612
641
  });
613
642
 
614
- // DNS records are set up automatically on start:
615
- // - MX records pointing to your mail server
616
- // - SPF TXT records authorizing your server IP
617
- // - DKIM TXT records with public keys from DKIMCreator
618
- // - DMARC TXT records with your policy
643
+ // In automatic key-provisioning mode, internal DNS domains register the
644
+ // configured MX/SPF/DMARC handlers and the generated selector-specific DKIM record.
645
+ // External DNS domains are validated and report the required public changes.
619
646
  await emailServer.start();
647
+
648
+ // With dkimKeyProvisioning: 'caller-managed', startup only validates the exact
649
+ // configured selector in managed storage and reads public DNS for validation.
650
+ // It performs no key generation/rotation, local DNS handler registration,
651
+ // DNS record write/delete, or provisioning fallback.
620
652
  ```
621
653
 
622
654
  ### 🪵 Logging
package/readme.plan.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Completed Phases
4
4
 
5
+ ### Phase 6: Durable Per-Recipient Delivery And Bounce Outbox
6
+ - Queue schema V2 stores explicit recipient dispositions and deterministic bounce operations in managed blob storage
7
+ - Final 250 acceptance, permanent 5xx terminal state, and unresolved 4xx retry semantics are checkpointed per recipient
8
+ - Built-in bounce processing is idempotent by operation ID; custom handlers receive the ID for at-least-once deduplication
9
+ - Queue and delivery observers are isolated from committed state, statistics, and downstream listeners
10
+
5
11
  ### Phase 3: Rust Primary Backend (DKIM/SPF/DMARC/IP Reputation)
6
12
  - Rust is the mandatory security backend — no TS fallbacks
7
13
  - All DKIM signing/verification, SPF, DMARC, IP reputation through Rust bridge
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartmta',
6
- version: '6.5.2',
6
+ version: '8.0.0',
7
7
  description: 'A high-performance, enterprise-grade Mail Transfer Agent (MTA) built from scratch in TypeScript with Rust acceleration.'
8
8
  }
@@ -0,0 +1,8 @@
1
+ /** Normalize values caught from promise and callback boundaries. */
2
+ export function getErrorMessage(error: unknown): string {
3
+ return error instanceof Error ? error.message : String(error);
4
+ }
5
+
6
+ export function getErrorStack(error: unknown): string | undefined {
7
+ return error instanceof Error ? error.stack : undefined;
8
+ }
package/ts/index.ts CHANGED
@@ -5,4 +5,7 @@ export { baseLogger, logger } from './logger.js';
5
5
 
6
6
  export const smartMtaCapabilities = Object.freeze({
7
7
  selectorCorrectDkimSigning: true,
8
+ callerManagedDkimKeys: true,
9
+ structuredSmtpTransactions: true,
10
+ typedDeliveryFailures: true,
8
11
  } as const);