@push.rocks/smartmta 8.0.1 → 8.1.1

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 (43) hide show
  1. package/changelog.md +51 -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/index.d.ts +6 -0
  6. package/dist_ts/index.js +7 -1
  7. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +43 -3
  8. package/dist_ts/mail/delivery/classes.delivery.queue.js +310 -10
  9. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +13 -2
  10. package/dist_ts/mail/delivery/classes.delivery.system.js +106 -36
  11. package/dist_ts/mail/interfaces.storage.d.ts +34 -0
  12. package/dist_ts/mail/interfaces.storage.js +89 -3
  13. package/dist_ts/mail/routing/classes.domain.registry.js +5 -1
  14. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +15 -0
  15. package/dist_ts/mail/routing/classes.email.action.executor.js +38 -13
  16. package/dist_ts/mail/routing/classes.email.router.d.ts +2 -0
  17. package/dist_ts/mail/routing/classes.email.router.js +28 -21
  18. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +156 -4
  19. package/dist_ts/mail/routing/classes.unified.email.server.js +1001 -61
  20. package/dist_ts/mail/routing/functions.routing.validation.d.ts +8 -0
  21. package/dist_ts/mail/routing/functions.routing.validation.js +18 -0
  22. package/dist_ts/mail/routing/interfaces.d.ts +11 -1
  23. package/dist_ts/plugins.d.ts +2 -1
  24. package/dist_ts/plugins.js +3 -2
  25. package/dist_ts/security/classes.rustsecuritybridge.d.ts +32 -1
  26. package/dist_ts/security/classes.rustsecuritybridge.js +13 -1
  27. package/dist_ts/security/index.d.ts +1 -1
  28. package/dist_ts/security/index.js +1 -1
  29. package/package.json +1 -1
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts/index.ts +6 -0
  32. package/ts/mail/delivery/classes.delivery.queue.ts +429 -7
  33. package/ts/mail/delivery/classes.delivery.system.ts +137 -41
  34. package/ts/mail/interfaces.storage.ts +148 -2
  35. package/ts/mail/routing/classes.domain.registry.ts +4 -0
  36. package/ts/mail/routing/classes.email.action.executor.ts +97 -13
  37. package/ts/mail/routing/classes.email.router.ts +33 -20
  38. package/ts/mail/routing/classes.unified.email.server.ts +1367 -74
  39. package/ts/mail/routing/functions.routing.validation.ts +36 -0
  40. package/ts/mail/routing/interfaces.ts +13 -1
  41. package/ts/plugins.ts +2 -0
  42. package/ts/security/classes.rustsecuritybridge.ts +46 -1
  43. package/ts/security/index.ts +2 -0
@@ -1,7 +1,8 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { DKIMCreator } from '../security/classes.dkimcreator.js';
3
3
  import type { IStorageManager } from '../interfaces.storage.js';
4
- import type { IEmailRoute, IEmailDomainConfig } from './interfaces.js';
4
+ import type { IInboundEmailSecurityResult } from '../../security/classes.rustsecuritybridge.js';
5
+ import type { IEmailRoute, IEmailAction, IEmailDomainConfig } from './interfaces.js';
5
6
  import { Email } from '../core/classes.email.js';
6
7
  import { DomainRegistry } from './classes.domain.registry.js';
7
8
  import { BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
@@ -16,6 +17,25 @@ interface DcRouter {
16
17
  dnsServer?: any;
17
18
  options?: any;
18
19
  }
20
+ export type TRecipientRouteSource = 'explicit-route' | 'domain-policy' | 'no-route';
21
+ export type TRecipientRejectionReason = 'explicit-route' | 'domain-policy' | 'no-route' | 'domain-not-hosted' | 'relay-denied' | 'recipient-hook' | 'policy-error';
22
+ export interface IResolvedRecipientRoute {
23
+ recipient: string;
24
+ localDomain: string | null;
25
+ route: IEmailRoute | null;
26
+ source: TRecipientRouteSource;
27
+ }
28
+ export interface IRecipientRejectedEvent {
29
+ timestamp: string;
30
+ sender: string;
31
+ recipient: string;
32
+ localDomain: string | null;
33
+ remoteAddress: string;
34
+ smtpCode: number;
35
+ smtpMessage: string;
36
+ routeName: string | null;
37
+ reason: TRecipientRejectionReason;
38
+ }
19
39
  /**
20
40
  * Extended SMTP session interface with route information
21
41
  */
@@ -24,6 +44,10 @@ export interface IExtendedSmtpSession extends ISmtpSession {
24
44
  * Matched route for this session
25
45
  */
26
46
  matchedRoute?: IEmailRoute;
47
+ /** Per-envelope-recipient routes resolved before DATA acceptance. */
48
+ resolvedRecipientRoutes?: IResolvedRecipientRoute[];
49
+ /** Results computed by the Rust inbound pipeline before the message event is emitted. */
50
+ securityResults?: IInboundEmailSecurityResult | null;
27
51
  }
28
52
  export interface ISmtpDecision {
29
53
  accepted: boolean;
@@ -46,7 +70,78 @@ export interface IMessageAcceptanceContext {
46
70
  email: Email;
47
71
  rawMessage: Buffer;
48
72
  session: IExtendedSmtpSession;
73
+ matchedRoute?: IEmailRoute;
74
+ /** Full-envelope route resolution. */
75
+ resolvedRecipientRoutes: IResolvedRecipientRoute[];
76
+ abortSignal?: AbortSignal;
77
+ }
78
+ /**
79
+ * Terminal durable acceptance context for an envelope containing at least one `store` route.
80
+ * The consumer owns persistence and all recipient fan-out before this hook resolves.
81
+ */
82
+ export interface IAcceptEnvelopeContext {
83
+ email: Email;
84
+ /** Exact bytes received during SMTP DATA, before parsing or transformation. */
85
+ rawMessage: Buffer;
86
+ session: IExtendedSmtpSession;
87
+ envelope: {
88
+ mailFrom: string;
89
+ rcptTo: string[];
90
+ };
91
+ /** Complete resolved plan for every SMTP envelope recipient. */
92
+ resolvedRecipientRoutes: IResolvedRecipientRoute[];
49
93
  abortSignal?: AbortSignal;
94
+ /** Immutable attachment count derived from the parsed message. */
95
+ readonly attachmentCount: number;
96
+ /** Rust-computed inbound verdicts, available before durable acceptance. */
97
+ securityResults: IInboundEmailSecurityResult | null;
98
+ }
99
+ /**
100
+ * JSON-safe, immutable snapshot of one accepted envelope recipient's exact
101
+ * routing decision. The snapshot is safe to persist and replay without
102
+ * consulting the live route table.
103
+ */
104
+ export interface IAcceptedEnvelopeRecipientPlan {
105
+ readonly recipient: string;
106
+ readonly localDomain: string | null;
107
+ readonly routeName: string;
108
+ readonly source: TRecipientRouteSource;
109
+ readonly action: IEmailAction;
110
+ readonly idempotencyKey: string;
111
+ }
112
+ /** Serializable SMTP metadata needed to recreate an isolated action session. */
113
+ export interface IAcceptedEnvelopeDispatchMetadata {
114
+ readonly mailFrom: string;
115
+ readonly session?: {
116
+ readonly id?: string;
117
+ readonly remoteAddress?: string;
118
+ readonly clientHostname?: string;
119
+ readonly secure?: boolean;
120
+ readonly authenticated?: boolean;
121
+ readonly authenticatedUser?: string | null;
122
+ readonly user?: Readonly<{
123
+ username: string;
124
+ [key: string]: unknown;
125
+ }>;
126
+ };
127
+ }
128
+ export type TAcceptedEnvelopeRecipientDispatchStatus = 'stored' | 'queued' | 'rejected' | 'failed';
129
+ export interface IAcceptedEnvelopeRecipientDispatchResult {
130
+ recipient: string;
131
+ localDomain: string | null;
132
+ routeName: string;
133
+ source: TRecipientRouteSource;
134
+ actionType: IEmailAction['type'];
135
+ idempotencyKey: string;
136
+ status: TAcceptedEnvelopeRecipientDispatchStatus;
137
+ replayed: boolean;
138
+ queueId?: string;
139
+ smtpCode?: number;
140
+ message?: string;
141
+ }
142
+ export interface IAcceptedEnvelopeDispatchResult {
143
+ complete: boolean;
144
+ results: IAcceptedEnvelopeRecipientDispatchResult[];
50
145
  }
51
146
  export interface IMessageAcceptanceDecision extends ISmtpDecision {
52
147
  /** Continue SmartMTA's built-in route processing after the consumer accepted responsibility. */
@@ -69,6 +164,16 @@ export interface IOutboundConnectionProxy {
69
164
  ehloHostname?: string;
70
165
  close?: () => Promise<void> | void;
71
166
  }
167
+ export interface IOutboundTopologyIdentity {
168
+ hostnames?: string[];
169
+ ipAddresses?: string[];
170
+ }
171
+ export interface ISelfMxIdentityMatch {
172
+ isSelf: boolean;
173
+ source: 'explicit' | 'dns-derived' | 'dynamic' | null;
174
+ indeterminate: boolean;
175
+ evidence: string[];
176
+ }
72
177
  /**
73
178
  * Options for the unified email server
74
179
  */
@@ -123,10 +228,14 @@ export interface IUnifiedEmailServerOptions {
123
228
  };
124
229
  };
125
230
  hooks?: {
126
- /** Async recipient policy hook, called before each RCPT TO is accepted. */
231
+ /** Async recipient policy hook that may further restrict an accepted built-in decision. */
127
232
  onRcptTo?: (context: IRcptToPolicyContext) => Promise<ISmtpDecision>;
128
- /** Async message acceptance hook, called after DATA before the final SMTP response. */
233
+ /** Async message acceptance hook for envelopes with no resolved `store` action. */
129
234
  onMessageData?: (context: IMessageAcceptanceContext) => Promise<IMessageAcceptanceDecision>;
235
+ /** Required terminal durable owner for any envelope containing a resolved `store` action. */
236
+ onAcceptEnvelope?: (context: IAcceptEnvelopeContext) => Promise<void>;
237
+ /** Observer-safe callback for SMTP recipient rejections. */
238
+ onRecipientRejected?: (event: IRecipientRejectedEvent) => Promise<void> | void;
130
239
  };
131
240
  defaults?: {
132
241
  dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
@@ -141,6 +250,10 @@ export interface IUnifiedEmailServerOptions {
141
250
  socketTimeout?: number;
142
251
  retryAttempts?: number;
143
252
  defaultFrom?: string;
253
+ /** Static public listener identities that must never be selected as outbound MX targets. */
254
+ selfListenerIdentities?: IOutboundTopologyIdentity;
255
+ /** Current proven mail-edge identities. Connection proxy targets are deliberately not identities. */
256
+ topologyIdentityProvider?: () => Promise<IOutboundTopologyIdentity | null | undefined>;
144
257
  connectionProxyProvider?: (context: IOutboundConnectionProxyContext) => Promise<IOutboundConnectionProxy | null | undefined>;
145
258
  };
146
259
  queue: IQueueOptions;
@@ -214,9 +327,18 @@ export declare class UnifiedEmailServer extends EventEmitter {
214
327
  private bridgeEmailReceivedHandler?;
215
328
  private bridgeAuthRequestHandler?;
216
329
  private bridgeScramCredentialRequestHandler?;
330
+ private acceptedEnvelopeDispatchCleanupTimer?;
331
+ private acceptedEnvelopeDispatchCleanupPromise?;
332
+ private acceptedEnvelopeDispatchCleanupCursor?;
217
333
  constructor(dcRouter: DcRouter, options: IUnifiedEmailServerOptions);
334
+ onRecipientRejected(listener: (event: IRecipientRejectedEvent) => Promise<void> | void): this;
335
+ offRecipientRejected(listener: (event: IRecipientRejectedEvent) => Promise<void> | void): this;
218
336
  private getAdvertisedHostname;
219
337
  private getOutboundHostname;
338
+ private normalizeTopologyIdentity;
339
+ private getExplicitTopologyIdentity;
340
+ private withTopologyTimeout;
341
+ private resolveTopologyHostname;
220
342
  /**
221
343
  * Whether a hostname refers to this server's own SMTP identity.
222
344
  * Used by MTA delivery to refuse self-addressed relaying: a domain whose
@@ -224,6 +346,8 @@ export declare class UnifiedEmailServer extends EventEmitter {
224
346
  * full outbound/inbound pipeline forever.
225
347
  */
226
348
  isSelfHostname(hostname: string): boolean;
349
+ /** Resolve a candidate MX against explicit and current proven listener topology. */
350
+ resolveSelfMxIdentity(hostnameArg: string): Promise<ISelfMxIdentityMatch>;
227
351
  /**
228
352
  * Send an outbound email via the Rust SMTP client.
229
353
  * Uses connection pooling in the Rust binary for efficiency.
@@ -257,13 +381,41 @@ export declare class UnifiedEmailServer extends EventEmitter {
257
381
  stop(): Promise<void>;
258
382
  private handleRustRcptToRequest;
259
383
  private evaluateDefaultRcptToPolicy;
260
- private enforceAcceptedRcptToRelayPolicy;
384
+ private resolveRecipientPolicy;
385
+ private getRejectionReasonForResolution;
386
+ private notifyRecipientRejected;
261
387
  private buildPolicySession;
262
388
  private getAddressDomain;
263
389
  private getRecipientValidationTimeoutMs;
264
390
  private isRecipientValidationEnabled;
265
391
  private getMessageAcceptanceTimeoutMs;
266
392
  private runHookWithTimeout;
393
+ /** Build a route-evaluation view containing only the current SMTP envelope recipient. */
394
+ private cloneEmailForRoutingRecipient;
395
+ private scopeSessionForRoutingRecipient;
396
+ private buildDataPolicyContext;
397
+ private resolveDataRecipientRoutes;
398
+ private acceptDurableEnvelope;
399
+ private cloneJsonSafe;
400
+ private stableJson;
401
+ private validateAcceptedEnvelopeAction;
402
+ private normalizeAcceptedEnvelopePlan;
403
+ /**
404
+ * Snapshot the already-resolved acceptance plan for durable consumer storage.
405
+ * No live route lookup is performed when the returned plan is dispatched.
406
+ */
407
+ createAcceptedEnvelopeDispatchPlan(context: IAcceptEnvelopeContext, idempotencyKeys: readonly string[]): readonly IAcceptedEnvelopeRecipientPlan[];
408
+ private acceptedEnvelopeDispatchStorageKey;
409
+ private startAcceptedEnvelopeDispatchCleanup;
410
+ private cleanupExpiredAcceptedEnvelopeDispatchResults;
411
+ private readAcceptedEnvelopeDispatchResult;
412
+ private writeAcceptedEnvelopeDispatchResult;
413
+ /**
414
+ * Dispatch a durably accepted raw envelope using only its persisted exact
415
+ * action snapshots. Each recipient receives an isolated Email and session.
416
+ * Queue-backed actions require managed queue storage and are replay-safe.
417
+ */
418
+ dispatchAcceptedEnvelope(rawMessage: Uint8Array, planArg: readonly IAcceptedEnvelopeRecipientPlan[], metadataArg: IAcceptedEnvelopeDispatchMetadata): Promise<IAcceptedEnvelopeDispatchResult>;
267
419
  /**
268
420
  * Handle an emailReceived event from the Rust SMTP server.
269
421
  */