@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.
- package/changelog.md +40 -0
- package/dist_rust/mailer-bin_linux_amd64 +0 -0
- package/dist_rust/mailer-bin_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/functions.errors.d.ts +3 -0
- package/dist_ts/functions.errors.js +8 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -1
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +16 -12
- package/dist_ts/mail/core/classes.bouncemanager.js +146 -129
- package/dist_ts/mail/core/classes.email.js +15 -13
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +3 -3
- package/dist_ts/mail/core/classes.emailvalidator.js +7 -5
- package/dist_ts/mail/core/classes.templatemanager.d.ts +10 -6
- package/dist_ts/mail/core/classes.templatemanager.js +35 -51
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +92 -22
- package/dist_ts/mail/delivery/classes.delivery.queue.js +738 -151
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +13 -7
- package/dist_ts/mail/delivery/classes.delivery.system.js +458 -145
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +9 -8
- package/dist_ts/mail/delivery/functions.safe-observers.d.ts +10 -0
- package/dist_ts/mail/delivery/functions.safe-observers.js +37 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +21 -0
- package/dist_ts/mail/delivery/interfaces.js +1 -1
- package/dist_ts/mail/interfaces.storage.d.ts +37 -6
- package/dist_ts/mail/interfaces.storage.js +33 -3
- package/dist_ts/mail/routing/classes.dkim.manager.d.ts +10 -6
- package/dist_ts/mail/routing/classes.dkim.manager.js +47 -31
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +7 -5
- package/dist_ts/mail/routing/classes.dns.manager.js +22 -11
- package/dist_ts/mail/routing/classes.email.action.executor.d.ts +2 -1
- package/dist_ts/mail/routing/classes.email.action.executor.js +45 -16
- package/dist_ts/mail/routing/classes.email.router.d.ts +5 -2
- package/dist_ts/mail/routing/classes.email.router.js +17 -12
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +12 -6
- package/dist_ts/mail/routing/classes.unified.email.server.js +69 -78
- package/dist_ts/mail/routing/interfaces.d.ts +0 -2
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +22 -45
- package/dist_ts/mail/security/classes.dkimcreator.js +93 -296
- package/dist_ts/mail/security/classes.spfverifier.js +5 -3
- package/dist_ts/paths.d.ts +0 -12
- package/dist_ts/paths.js +3 -36
- package/dist_ts/plugins.d.ts +2 -5
- package/dist_ts/plugins.js +3 -6
- package/dist_ts/security/classes.contentscanner.js +14 -12
- package/dist_ts/security/classes.ipreputationchecker.d.ts +9 -6
- package/dist_ts/security/classes.ipreputationchecker.js +42 -93
- package/dist_ts/security/classes.rustsecuritybridge.d.ts +52 -4
- package/dist_ts/security/classes.rustsecuritybridge.js +201 -4
- package/dist_ts/security/classes.securitylogger.js +7 -5
- package/dist_ts/security/index.d.ts +1 -1
- package/dist_ts/security/index.js +2 -2
- package/package.json +8 -9
- package/readme.hints.md +4 -3
- package/readme.md +50 -18
- package/readme.plan.md +6 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/functions.errors.ts +8 -0
- package/ts/index.ts +3 -0
- package/ts/mail/core/classes.bouncemanager.ts +188 -155
- package/ts/mail/core/classes.email.ts +20 -14
- package/ts/mail/core/classes.emailvalidator.ts +9 -7
- package/ts/mail/core/classes.templatemanager.ts +42 -57
- package/ts/mail/delivery/classes.delivery.queue.ts +996 -185
- package/ts/mail/delivery/classes.delivery.system.ts +583 -170
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +9 -8
- package/ts/mail/delivery/functions.safe-observers.ts +45 -0
- package/ts/mail/delivery/interfaces.ts +27 -1
- package/ts/mail/interfaces.storage.ts +64 -10
- package/ts/mail/routing/classes.dkim.manager.ts +65 -40
- package/ts/mail/routing/classes.dns.manager.ts +39 -16
- package/ts/mail/routing/classes.email.action.executor.ts +64 -17
- package/ts/mail/routing/classes.email.router.ts +20 -13
- package/ts/mail/routing/classes.unified.email.server.ts +107 -86
- package/ts/mail/routing/interfaces.ts +0 -2
- package/ts/mail/security/classes.dkimcreator.ts +150 -355
- package/ts/mail/security/classes.spfverifier.ts +4 -2
- package/ts/paths.ts +2 -41
- package/ts/plugins.ts +1 -6
- package/ts/security/classes.contentscanner.ts +14 -12
- package/ts/security/classes.ipreputationchecker.ts +46 -99
- package/ts/security/classes.rustsecuritybridge.ts +272 -6
- package/ts/security/classes.securitylogger.ts +6 -4
- 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
|
|
213
|
+
// TLS material is provided in memory; SmartMTA does not read certificate files.
|
|
214
214
|
tls: {
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
402
|
-
-
|
|
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
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
-
//
|
|
615
|
-
//
|
|
616
|
-
//
|
|
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
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -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