@push.rocks/smartmta 9.4.0 → 9.5.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 (35) hide show
  1. package/changelog.md +23 -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 +1 -0
  6. package/dist_ts/index.js +2 -1
  7. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +71 -1
  8. package/dist_ts/mail/delivery/classes.delivery.queue.js +794 -179
  9. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +11 -0
  10. package/dist_ts/mail/delivery/classes.delivery.system.js +206 -80
  11. package/dist_ts/mail/delivery/functions.raw-email.d.ts +9 -0
  12. package/dist_ts/mail/delivery/functions.raw-email.js +85 -0
  13. package/dist_ts/mail/delivery/symbols.delivery.queue.d.ts +6 -0
  14. package/dist_ts/mail/delivery/symbols.delivery.queue.js +7 -0
  15. package/dist_ts/mail/index.d.ts +1 -0
  16. package/dist_ts/mail/index.js +1 -1
  17. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +40 -0
  18. package/dist_ts/mail/routing/classes.unified.email.server.js +222 -76
  19. package/dist_ts/mail/routing/symbols.unified.email.server.d.ts +2 -0
  20. package/dist_ts/mail/routing/symbols.unified.email.server.js +3 -0
  21. package/dist_ts/security/classes.rustsecuritybridge.d.ts +11 -0
  22. package/dist_ts/security/classes.rustsecuritybridge.js +1 -1
  23. package/package.json +2 -2
  24. package/readme.hints.md +8 -0
  25. package/readme.md +36 -3
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/index.ts +1 -0
  28. package/ts/mail/delivery/classes.delivery.queue.ts +969 -209
  29. package/ts/mail/delivery/classes.delivery.system.ts +254 -101
  30. package/ts/mail/delivery/functions.raw-email.ts +111 -0
  31. package/ts/mail/delivery/symbols.delivery.queue.ts +6 -0
  32. package/ts/mail/index.ts +1 -0
  33. package/ts/mail/routing/classes.unified.email.server.ts +329 -85
  34. package/ts/mail/routing/symbols.unified.email.server.ts +6 -0
  35. package/ts/security/classes.rustsecuritybridge.ts +6 -0
package/changelog.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-07-31 - 9.5.0
4
+
5
+ ### Features
6
+
7
+ - add a durable raw outbound MTA queue (raw-outbound)
8
+ - Add managed-only `UnifiedEmailServer.enqueueRawEmail()` with canonical CRLF-terminated exact RFC822 bytes, a separate canonical ordered SMTP envelope, stable idempotency, a 32 MiB default message limit, and a weighted global raw-operation memory budget.
9
+ - Store raw items as atomic schema V3 queue blobs while retaining only sanitized metadata in memory; preserve structured schema V2 writes and V1/V2 hydration unchanged.
10
+ - Serialize raw status mutations and removal per item, verify payload length/digest/envelope on every lazy reread, refuse processing-item removal, and fail closed on missing or corrupt payloads.
11
+ - Isolate all public queue/status snapshots, keep queue-only operations behind internal symbols, and route internal readiness by item ID so observers cannot mutate delivery inputs.
12
+ - Reuse MX, self-MX, recipient checkpoint, retry, bounce, RemoteIngress proxy/source-evidence, timeout, TLS, pooling, capture, and cleanup behavior for raw delivery.
13
+ - Extend Rust raw SMTP delivery with mandatory caller-supplied DKIM material, canonical-byte enforcement, and actual-sent-byte capture without UTF-8 conversion; keep unsigned queue bytes immutable across attempts and capture memory reserved through verification.
14
+ - Snapshot raw caller buffers synchronously, reserve queue capacity before persistence, share the weighted memory budget with direct raw sends, cap fixed raw-operation overhead, use bounded startup hydration, close outbound admission before shutdown, drain admitted enqueue/queue-scan/delivery/authentication work before storage and Rust teardown, and recover interrupted V3 records without dropping their payload.
15
+ - Add `smartMtaCapabilities.durableRawOutboundQueue` and document the schema V3 downgrade gate and deployment-wide single-writer prerequisite.
16
+
17
+ ## 2026-07-27 - 9.4.1
18
+
19
+ ### Fixes
20
+
21
+ - dispatch newly queued delivery items immediately (delivery)
22
+ - Wake the normal queue eligibility scan after durable enqueue, resume, delivery completion, and rate-limit reset while preserving pause and retry timing.
23
+ - Serialize queue scan wakeups and bound shutdown when an active itemsReady observer does not settle.
24
+ - Use a fixed one-minute rate-limit window to avoid false throttling on freshly started delivery processing.
25
+
3
26
  ## 2026-07-27 - 9.4.0
4
27
 
5
28
  ### Features
Binary file
Binary file
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartmta',
6
- version: '9.4.0',
6
+ version: '9.5.0',
7
7
  description: 'A high-performance, enterprise-grade Mail Transfer Agent (MTA) built from scratch in TypeScript with Rust acceleration.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlIQUF5SDtDQUN2SSxDQUFBIn0=
@@ -15,4 +15,5 @@ export declare const smartMtaCapabilities: Readonly<{
15
15
  readonly exactAcceptedEnvelopeDispatch: true;
16
16
  readonly terminalLocalDeliverActions: true;
17
17
  readonly smtpAuthAccountsAndScopes: true;
18
+ readonly durableRawOutboundQueue: true;
18
19
  }>;
package/dist_ts/index.js CHANGED
@@ -15,5 +15,6 @@ export const smartMtaCapabilities = Object.freeze({
15
15
  exactAcceptedEnvelopeDispatch: true,
16
16
  terminalLocalDeliverActions: true,
17
17
  smtpAuthAccountsAndScopes: true,
18
+ durableRawOutboundQueue: true,
18
19
  });
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsaUJBQWlCLENBQUM7QUFDaEMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVqRCxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2hELDBCQUEwQixFQUFFLElBQUk7SUFDaEMscUJBQXFCLEVBQUUsSUFBSTtJQUMzQiwwQkFBMEIsRUFBRSxJQUFJO0lBQ2hDLHFCQUFxQixFQUFFLElBQUk7SUFDM0Isb0JBQW9CLEVBQUUsSUFBSTtJQUMxQixpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLG1CQUFtQixFQUFFLElBQUk7SUFDekIsbUJBQW1CLEVBQUUsSUFBSTtJQUN6Qiw0QkFBNEIsRUFBRSxJQUFJO0lBQ2xDLDZCQUE2QixFQUFFLElBQUk7SUFDbkMsMkJBQTJCLEVBQUUsSUFBSTtJQUNqQyx5QkFBeUIsRUFBRSxJQUFJO0NBQ3ZCLENBQUMsQ0FBQyJ9
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsaUJBQWlCLENBQUM7QUFDaEMsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUVqRCxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ2hELDBCQUEwQixFQUFFLElBQUk7SUFDaEMscUJBQXFCLEVBQUUsSUFBSTtJQUMzQiwwQkFBMEIsRUFBRSxJQUFJO0lBQ2hDLHFCQUFxQixFQUFFLElBQUk7SUFDM0Isb0JBQW9CLEVBQUUsSUFBSTtJQUMxQixpQkFBaUIsRUFBRSxJQUFJO0lBQ3ZCLG1CQUFtQixFQUFFLElBQUk7SUFDekIsbUJBQW1CLEVBQUUsSUFBSTtJQUN6Qiw0QkFBNEIsRUFBRSxJQUFJO0lBQ2xDLDZCQUE2QixFQUFFLElBQUk7SUFDbkMsMkJBQTJCLEVBQUUsSUFBSTtJQUNqQyx5QkFBeUIsRUFBRSxJQUFJO0lBQy9CLHVCQUF1QixFQUFFLElBQUk7Q0FDckIsQ0FBQyxDQUFDIn0=
@@ -1,9 +1,11 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import type { IBlobStorageManager } from '../interfaces.storage.js';
2
+ import { type IBlobStorageManager } from '../interfaces.storage.js';
3
3
  import { type EmailProcessingMode } from './interfaces.js';
4
4
  import type { ISmtpTransactionAttempt } from './interfaces.js';
5
5
  import type { IEmailRoute } from '../routing/interfaces.js';
6
6
  import { Email } from '../core/classes.email.js';
7
+ import { type IRawSmtpEnvelope } from './functions.raw-email.js';
8
+ import { kQueueCloseAdmission, kQueueCreateItemSnapshot, kQueueEnqueueRaw, kQueueReserveRawAccess, kQueueSetReadyItemsHandler, kQueueWithRawMessage } from './symbols.delivery.queue.js';
7
9
  /**
8
10
  * Queue item status
9
11
  */
@@ -47,6 +49,14 @@ export interface IQueueItem {
47
49
  idempotencyFingerprint?: string;
48
50
  processingMode: EmailProcessingMode;
49
51
  processingResult: Email;
52
+ /** Present only for durable exact-byte outbound queue items. */
53
+ messageFormat?: 'raw-rfc822';
54
+ /** SHA-256 of the immutable unsigned RFC822 bytes. */
55
+ rawMessageSha256?: string;
56
+ /** Exact immutable unsigned RFC822 byte length. */
57
+ rawMessageLength?: number;
58
+ /** Canonical SMTP envelope, independent from message headers. */
59
+ smtpEnvelope?: IRawSmtpEnvelope;
50
60
  /** Materialized attachment count for queue/detail consumers. */
51
61
  attachmentCount?: number;
52
62
  route?: IEmailRoute;
@@ -64,6 +74,8 @@ export interface IQueueItem {
64
74
  smtpTransactions?: ISmtpTransactionAttempt[];
65
75
  }
66
76
  export declare const ACCEPTED_ENVELOPE_IDEMPOTENCY_RETENTION_MS: number;
77
+ export declare const DEFAULT_MAX_RAW_MESSAGE_BYTES: number;
78
+ export declare const DEFAULT_RAW_IN_FLIGHT_BYTE_BUDGET: number;
67
79
  export interface IQueueFailureOptions {
68
80
  /** Unknown failures default to retryable so temporary internal faults do not lose mail. */
69
81
  retryable?: boolean;
@@ -91,6 +103,10 @@ export interface IQueueOptions {
91
103
  checkInterval?: number;
92
104
  maxQueueSize?: number;
93
105
  maxPerDestination?: number;
106
+ /** Maximum exact RFC822 bytes accepted by enqueueRawEmail. Defaults to 32 MiB. */
107
+ maxRawMessageBytes?: number;
108
+ /** Conservative memory budget for active raw operations. Defaults to 192 MiB. */
109
+ rawInFlightByteBudget?: number;
94
110
  maxRetries?: number;
95
111
  baseRetryDelay?: number;
96
112
  maxRetryDelay?: number;
@@ -103,6 +119,11 @@ export interface IQueueEnqueueOptions {
103
119
  /** Fail closed unless the queue is backed by managed blob storage. */
104
120
  requireDurable?: boolean;
105
121
  }
122
+ interface IRawQueueEnqueueOptions {
123
+ rawMessage: Buffer;
124
+ envelope: IRawSmtpEnvelope;
125
+ idempotencyKey: string;
126
+ }
106
127
  export interface IQueueStats {
107
128
  queueSize: number;
108
129
  status: {
@@ -131,23 +152,46 @@ export declare class UnifiedDeliveryQueue extends EventEmitter {
131
152
  private queue;
132
153
  private checkTimer?;
133
154
  private cleanupTimer?;
155
+ private cleanupFlight?;
134
156
  private stats;
135
157
  private processing;
158
+ private processingScanPromise?;
159
+ private processingRescanRequested;
136
160
  private totalProcessed;
137
161
  private ownsManagedPrefix;
138
162
  private lifecycleState;
139
163
  private readonly idempotentEnqueueFlights;
140
164
  private idempotencyTombstoneCleanupCursor?;
165
+ private readonly itemMutationFlights;
166
+ private rawBytesInFlight;
167
+ private rawByteWaiters;
168
+ private rawReservationCount;
169
+ private rawReservationDrainWaiters;
170
+ private readyItemsHandler?;
171
+ private acceptingNewItems;
172
+ private acceptingMutations;
173
+ private acceptingRawReservations;
174
+ private readonly enqueueFlights;
175
+ private pendingNewItemCount;
176
+ private shutdownFlight?;
141
177
  /**
142
178
  * Create a new unified delivery queue
143
179
  * @param options Queue options
144
180
  */
145
181
  constructor(options: IQueueOptions);
146
182
  private assertMutable;
183
+ private assertAcceptingNewItems;
184
+ private trackEnqueueFlight;
147
185
  /** Whether queue entries are committed through caller-managed blob storage. */
148
186
  hasManagedStorage(): boolean;
149
187
  /** Whether managed storage can list a backend-bounded page of keys. */
150
188
  hasPaginatedManagedStorage(): boolean;
189
+ [kQueueCloseAdmission](): void;
190
+ [kQueueSetReadyItemsHandler](handlerArg?: (itemIds: string[]) => Promise<void>): void;
191
+ private cloneEmail;
192
+ [kQueueCreateItemSnapshot](itemArg: IQueueItem): IQueueItem;
193
+ private emitItemEventSafely;
194
+ private emitReadyItemsSafely;
151
195
  private idempotentQueueId;
152
196
  private idempotencyTombstonePrefix;
153
197
  private idempotencyTombstoneKey;
@@ -169,6 +213,7 @@ export declare class UnifiedDeliveryQueue extends EventEmitter {
169
213
  * Start queue processing
170
214
  */
171
215
  private startProcessing;
216
+ private runPeriodicCleanup;
172
217
  /**
173
218
  * Stop queue processing
174
219
  */
@@ -177,6 +222,12 @@ export declare class UnifiedDeliveryQueue extends EventEmitter {
177
222
  * Check for items that need to be processed
178
223
  */
179
224
  private processQueue;
225
+ /**
226
+ * Wake the normal eligibility scan without bypassing pause or retry timing.
227
+ * The interval remains the recovery path for retries and any missed wakeup.
228
+ */
229
+ wake(): void;
230
+ private runProcessingScans;
180
231
  /**
181
232
  * Add an item to the queue
182
233
  * @param processingResult Processing result to queue
@@ -184,6 +235,10 @@ export declare class UnifiedDeliveryQueue extends EventEmitter {
184
235
  * @param route Email route
185
236
  */
186
237
  enqueue(processingResult: Email, mode: EmailProcessingMode, route?: IEmailRoute, enqueueOptions?: IQueueEnqueueOptions): Promise<string>;
238
+ private enqueueAccepted;
239
+ [kQueueEnqueueRaw](optionsArg: IRawQueueEnqueueOptions): Promise<string>;
240
+ private enqueueRawAccepted;
241
+ private enqueueIdempotent;
187
242
  private enqueueNew;
188
243
  /**
189
244
  * Get an item from the queue
@@ -244,8 +299,21 @@ export declare class UnifiedDeliveryQueue extends EventEmitter {
244
299
  removeItem(id: string): Promise<boolean>;
245
300
  private storageKey;
246
301
  private persistItem;
302
+ private persistCurrentItem;
247
303
  private serializeItem;
304
+ private withItemMutex;
248
305
  private applyPersistedMutation;
306
+ private createRawIntegrityError;
307
+ private readVerifiedRawMessage;
308
+ private claimRawReservation;
309
+ private createRawByteRelease;
310
+ private reserveRawBytes;
311
+ private reserveRawBytesImmediately;
312
+ [kQueueReserveRawAccess](rawMessageLengthArg: number): () => void;
313
+ private releaseRawBytes;
314
+ private withRawByteReservation;
315
+ private waitForRawReservationsToDrain;
316
+ [kQueueWithRawMessage]<T>(itemIdArg: string, callbackArg: (rawMessage: Buffer) => Promise<T>): Promise<T>;
249
317
  private removeItemFromStorage;
250
318
  private deserializeItem;
251
319
  private loadFromStorage;
@@ -274,4 +342,6 @@ export declare class UnifiedDeliveryQueue extends EventEmitter {
274
342
  * Shutdown the queue
275
343
  */
276
344
  shutdown(): Promise<void>;
345
+ private shutdownOnce;
277
346
  }
347
+ export {};