@bumail/queue 0.13.0 → 0.14.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.
- package/README.md +44 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +442 -26
- package/dist/index.js.map +21 -13
- package/dist/queue/deliver.d.ts +2 -0
- package/dist/queue/deliver.d.ts.map +1 -1
- package/dist/queue/events.d.ts +3 -0
- package/dist/queue/events.d.ts.map +1 -1
- package/dist/queue/fallback-session.d.ts.map +1 -1
- package/dist/queue/fallback.d.ts.map +1 -1
- package/dist/queue/options.d.ts +10 -0
- package/dist/queue/options.d.ts.map +1 -1
- package/dist/queue/outcome.d.ts.map +1 -1
- package/dist/queue/session.d.ts +4 -0
- package/dist/queue/session.d.ts.map +1 -1
- package/dist/queue/settings/index.d.ts +3 -0
- package/dist/queue/settings/index.d.ts.map +1 -1
- package/dist/queue/settle.d.ts.map +1 -1
- package/dist/queue/worker.d.ts.map +1 -1
- package/dist/sts/attempt.d.ts +15 -0
- package/dist/sts/attempt.d.ts.map +1 -0
- package/dist/sts/bound.d.ts +15 -0
- package/dist/sts/bound.d.ts.map +1 -0
- package/dist/sts/cache.d.ts +27 -0
- package/dist/sts/cache.d.ts.map +1 -0
- package/dist/sts/discover.d.ts +52 -0
- package/dist/sts/discover.d.ts.map +1 -0
- package/dist/sts/options.d.ts +77 -0
- package/dist/sts/options.d.ts.map +1 -0
- package/dist/sts/refusal.d.ts +10 -0
- package/dist/sts/refusal.d.ts.map +1 -0
- package/dist/sts/run.d.ts +55 -0
- package/dist/sts/run.d.ts.map +1 -0
- package/dist/sts/settings.d.ts +5 -0
- package/dist/sts/settings.d.ts.map +1 -0
- package/dist/sts/tally.d.ts +20 -0
- package/dist/sts/tally.d.ts.map +1 -0
- package/docs/guide.md +63 -1
- package/docs/roadmap.md +6 -4
- package/docs/troubleshooting.md +49 -0
- package/package.json +7 -5
package/dist/queue/deliver.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { Lapse } from './lease';
|
|
|
4
4
|
import type { KeyedLimiter } from './limiter';
|
|
5
5
|
import { type NotifyContext } from './notify';
|
|
6
6
|
export interface DeliveryContext extends NotifyContext {
|
|
7
|
+
/** A group was left for later (the queue stopped before its policy was known): no attempt is counted for it. */
|
|
8
|
+
readonly halted?: () => void;
|
|
7
9
|
readonly domains: KeyedLimiter;
|
|
8
10
|
readonly events: Events;
|
|
9
11
|
/** The worker is stopping: a group not started yet is left for later. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/queue/deliver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAIpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAU,KAAK,aAAa,EAAY,MAAM,UAAU,CAAC;AAGhE,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAClE;AAiGD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAChC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/queue/deliver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAIpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAU,KAAK,aAAa,EAAY,MAAM,UAAU,CAAC;AAGhE,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,gHAAgH;IAChH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAClE;AAiGD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAChC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,CA8Cf"}
|
package/dist/queue/events.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Diagnostic } from '../contract/types';
|
|
2
|
+
import type { TlsEvent } from '../sts/options';
|
|
2
3
|
/** One recipient's outcome. */
|
|
3
4
|
export interface RecipientEvent {
|
|
4
5
|
/** The item's id. */
|
|
@@ -68,6 +69,8 @@ export interface QueueEvents {
|
|
|
68
69
|
deferred: DeferredEvent;
|
|
69
70
|
failed: RecipientEvent;
|
|
70
71
|
dsn: DsnEvent;
|
|
72
|
+
/** One try at an MX address under `mtaSts`, or a policy that could not be fetched. */
|
|
73
|
+
tls: TlsEvent;
|
|
71
74
|
error: QueueErrorEvent;
|
|
72
75
|
}
|
|
73
76
|
export type QueueListener<E extends keyof QueueEvents> = (event: QueueEvents[E]) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/queue/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/queue/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC9B,qBAAqB;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,GAAG,WAAW,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACpD,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,0CAA0C;AAC1C,MAAM,WAAW,QAAQ;IACxB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IAChD,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,gGAAgG;AAChG,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;IACvB,GAAG,EAAE,QAAQ,CAAC;IACd,sFAAsF;IACtF,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,MAAM,WAAW,IAAI,CACxD,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KACjB,IAAI,CAAC;AAEV,iFAAiF;AACjF,qBAAa,MAAM;;IAGlB,EAAE,CAAC,CAAC,SAAS,MAAM,WAAW,EAC7B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,MAAM,IAAI;IAYb,IAAI,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;CAS1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback-session.d.ts","sourceRoot":"","sources":["../../src/queue/fallback-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,KAAK,OAAO,EAAqB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAuBvE;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,KAAK,EACf,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GACrB,OAAO,CAAC,eAAe,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"fallback-session.d.ts","sourceRoot":"","sources":["../../src/queue/fallback-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,KAAK,OAAO,EAAqB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAuBvE;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,KAAK,EACf,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GACrB,OAAO,CAAC,eAAe,EAAE,CAAC,CA0C5B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC7B,IAAI,EAAE,SAAS,EACf,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,KAAK,GAAG,SAAS,GACzB,MAAM,EAAE,CAQV;AAED,8DAA8D;AAC9D,wBAAsB,WAAW,CAChC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,EAAE,KAAK,EACf,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GACrB,OAAO,CAAC,eAAe,EAAE,CAAC,CAsB5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/queue/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAUpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CACxB,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,KAAK,CAAC,EAAE,OAAO,GACb,OAAO,
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../src/queue/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAUpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CACxB,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,KAAK,CAAC,EAAE,OAAO,GACb,OAAO,CAeT"}
|
package/dist/queue/options.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MxResolver, SendMailAuth, SendMailOptions, SendMailResult, SendMailTimeouts, TlsMode } from '@bumail/smtp/client';
|
|
2
2
|
import type { QueueStore } from '../contract/queue-store';
|
|
3
|
+
import type { MtaStsOptions } from '../sts/options';
|
|
3
4
|
/** What delivers one message to one destination: `sendMail` of `@bumail/smtp/client`, or a fake in a spec. */
|
|
4
5
|
export type Sender = (message: Uint8Array, options: SendMailOptions) => Promise<SendMailResult>;
|
|
5
6
|
/** What the queue reads the time from, in milliseconds since the epoch: `Date.now` but in a spec. */
|
|
@@ -102,6 +103,15 @@ export interface QueueOptions {
|
|
|
102
103
|
readonly mxTls?: TlsMode;
|
|
103
104
|
/** Each step's timeout, in seconds, as `sendMail` takes them. */
|
|
104
105
|
readonly timeouts?: SendMailTimeouts;
|
|
106
|
+
/**
|
|
107
|
+
* MTA-STS (RFC 8461) on delivery by MX; off unless given. A domain with
|
|
108
|
+
* an `enforce` policy is sent to over verified TLS, to the MX hosts its
|
|
109
|
+
* policy names, and a failure defers as `4.7.5` and bounces as `5.7.5`
|
|
110
|
+
* only at `retry.giveUpAfter`. `testing` only reports. Smarthost routes
|
|
111
|
+
* and the `fallback` never use it. Your own `send` gets `mxNames`, `tls`
|
|
112
|
+
* and `onAttempt` in its options and has to pass them to `sendMail`.
|
|
113
|
+
*/
|
|
114
|
+
readonly mtaSts?: MtaStsOptions;
|
|
105
115
|
/** Seconds one session may take, DNS included. Default 1800. */
|
|
106
116
|
readonly deadline?: number;
|
|
107
117
|
readonly retry?: RetrySchedule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/queue/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,OAAO,EACP,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/queue/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/queue/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,EACL,cAAc,EACd,SAAS,EAET,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGrE;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,SAGC,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,OACQ,CAAC;AAKvD,8CAA8C;AAC9C,wBAAgB,YAAY,CAC3B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACX,UAAU,CAWZ;
|
|
1
|
+
{"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/queue/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,EACL,cAAc,EACd,SAAS,EAET,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGrE;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,SAGC,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,OACQ,CAAC;AAKvD,8CAA8C;AAC9C,wBAAgB,YAAY,CAC3B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACX,UAAU,CAWZ;AA4CD,sFAAsF;AACtF,wBAAgB,UAAU,CACzB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,GAAG,EAAE,MAAM,GACT,eAAe,EAAE,CA+BnB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACX,eAAe,EAAE,CAuBnB"}
|
package/dist/queue/session.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export type Used = Pick<RecipientEvent, 'route' | 'fallback' | 'mxReply' | 'reus
|
|
|
8
8
|
export interface Attempt {
|
|
9
9
|
readonly updates: RecipientUpdate[];
|
|
10
10
|
readonly error?: unknown;
|
|
11
|
+
/** An `enforce` policy refused the session: no fallback smarthost. */
|
|
12
|
+
readonly refused?: true;
|
|
13
|
+
/** An `enforce` policy governed the session: its failure never falls back to the smarthost. */
|
|
14
|
+
readonly enforced?: true;
|
|
11
15
|
/** The session went over a connection that had carried a message before. */
|
|
12
16
|
readonly reused?: boolean;
|
|
13
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/queue/session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/queue/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AA0CvC,wEAAwE;AACxE,MAAM,MAAM,IAAI,GAAG,IAAI,CACtB,cAAc,EACd,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAC3C,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC;IACxB,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,kFAAkF;AAClF,eAAO,MAAM,QAAQ,GAAI,GAAG,OAAO,KAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,CACN,CAAC;AAyCpD,kHAAkH;AAClH,wBAAsB,OAAO,CAC5B,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,KAAK,EACZ,QAAQ,CAAC,EAAE,KAAK,GACd,OAAO,CAAC,OAAO,CAAC,CAgDlB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { StsSettings } from '../../sts/discover';
|
|
1
2
|
import type { QueueOptions, Route } from '../options';
|
|
2
3
|
import { type DsnSettings } from './dsn';
|
|
3
4
|
import { type LimitSettings, type RetrySettings } from './limits';
|
|
@@ -22,6 +23,8 @@ export interface Settings {
|
|
|
22
23
|
/** Closes the queue's own connection pool, if it has one. */
|
|
23
24
|
readonly close: SenderSettings['close'];
|
|
24
25
|
readonly random: () => number;
|
|
26
|
+
/** MTA-STS on the MX route; absent unless `mtaSts` is given. */
|
|
27
|
+
readonly sts: StsSettings | undefined;
|
|
25
28
|
}
|
|
26
29
|
/** The options, checked, with their defaults; `INVALID` names the first wrong one. */
|
|
27
30
|
export declare function settingsOf(options: QueueOptions): Settings;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,OAAO,CAAC;AAChD,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,UAAU,CAAC;AAGzD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAE1D,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,OAAO,CAAC;AAChD,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,KAAK,cAAc,EAAY,MAAM,UAAU,CAAC;AAGzD,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAE1D,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,SAAS,CAAC;CACtC;AAKD,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,CAqD1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settle.d.ts","sourceRoot":"","sources":["../../src/queue/settle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"settle.d.ts","sourceRoot":"","sources":["../../src/queue/settle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,MAAM,mBAAmB,CAAC;AAI3B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,2EAA2E;AAC3E,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B;AA4BD;;;;;GAKG;AACH,wBAAgB,MAAM,CACrB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAmDT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/queue/worker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,MAAM;;gBAaN,GAAG,EAAE,aAAa;IAsD9B;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAgCnC,4FAA4F;IAC5F,IAAI,IAAI,IAAI;IAKZ,KAAK,IAAI,IAAI;IAqCb;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/queue/worker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,MAAM;;gBAaN,GAAG,EAAE,aAAa;IAsD9B;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAgCnC,4FAA4F;IAC5F,IAAI,IAAI,IAAI;IAKZ,KAAK,IAAI,IAAI;IAqCb;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAmB3B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type MxAttempt } from '@bumail/smtp/client';
|
|
2
|
+
import type { ActivePolicy } from './discover';
|
|
3
|
+
import type { TlsEvent, TlsPolicyReport } from './options';
|
|
4
|
+
/** How a `tls` event names the policy a session ran under. */
|
|
5
|
+
export declare const reportOf: (policy: ActivePolicy | undefined) => TlsPolicyReport;
|
|
6
|
+
/**
|
|
7
|
+
* The `tls` event of one try at an MX address, or `undefined` when the try
|
|
8
|
+
* says nothing of TLS (the connection failed before any). Under a policy
|
|
9
|
+
* the try counts only when the MX name matches it, the certificate
|
|
10
|
+
* checked out and TLS gave no `tlsResult`; in `testing` mode that is what
|
|
11
|
+
* would have failed an `enforce` delivery. With no policy, any `tlsResult`
|
|
12
|
+
* fails it and a try that reached the server is fine.
|
|
13
|
+
*/
|
|
14
|
+
export declare function tlsEventOf(domain: string, policy: ActivePolicy | undefined, attempt: MxAttempt, at: number): TlsEvent | undefined;
|
|
15
|
+
//# sourceMappingURL=attempt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt.d.ts","sourceRoot":"","sources":["../../src/sts/attempt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE3D,8DAA8D;AAC9D,eAAO,MAAM,QAAQ,GAAI,QAAQ,YAAY,GAAG,SAAS,KAAG,eAS7B,CAAC;AAEhC;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,OAAO,EAAE,SAAS,EAClB,EAAE,EAAE,MAAM,GACR,QAAQ,GAAG,SAAS,CAyBtB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** What a bounded wait came to. */
|
|
2
|
+
export type Bounded<T> = {
|
|
3
|
+
readonly done: true;
|
|
4
|
+
readonly value: T;
|
|
5
|
+
} | {
|
|
6
|
+
readonly done: false;
|
|
7
|
+
readonly why: 'aborted' | 'timeout';
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Waits for `work` (a promise or a value), but no longer than `timeoutMs`
|
|
11
|
+
* and not past `signal`: a lookup or a cache that never answers must not
|
|
12
|
+
* hold a delivery, or `stop()`. A rejection of `work` rejects.
|
|
13
|
+
*/
|
|
14
|
+
export declare function bounded<T>(work: T | PromiseLike<T>, signal: AbortSignal, timeoutMs: number | undefined): Promise<Bounded<T>>;
|
|
15
|
+
//# sourceMappingURL=bound.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bound.d.ts","sourceRoot":"","sources":["../../src/sts/bound.ts"],"names":[],"mappings":"AAEA,mCAAmC;AACnC,MAAM,MAAM,OAAO,CAAC,CAAC,IAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC1C;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjE;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC9B,IAAI,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,EACxB,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC3B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAqBrB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { StsPolicy } from '@bumail/mta-sts';
|
|
2
|
+
/** A policy kept for a domain (RFC 8461 §5.1), with what decides whether it is still good. */
|
|
3
|
+
export interface PolicyCacheEntry {
|
|
4
|
+
/** The `id` of the `_mta-sts` record the policy was fetched under. */
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly policy: StsPolicy;
|
|
7
|
+
/** The policy file as served, for a TLS report's `policy-string`. */
|
|
8
|
+
readonly text: string;
|
|
9
|
+
/** Milliseconds since the epoch, on the queue's clock. */
|
|
10
|
+
readonly fetchedAt: number;
|
|
11
|
+
/** `fetchedAt` plus the policy's `max_age`; from then on the entry is not used. */
|
|
12
|
+
readonly expiresAt: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where the queue keeps the policies it fetched, by lowercase domain.
|
|
16
|
+
* Answers may be synchronous or promises. A cache that throws is read as
|
|
17
|
+
* empty and its writes are dropped: it never fails a delivery.
|
|
18
|
+
*/
|
|
19
|
+
export interface PolicyCache {
|
|
20
|
+
get(domain: string): PolicyCacheEntry | undefined | Promise<PolicyCacheEntry | undefined>;
|
|
21
|
+
set(domain: string, entry: PolicyCacheEntry): void | Promise<void>;
|
|
22
|
+
/** Optional: drops a domain's entry. */
|
|
23
|
+
delete?(domain: string): void | Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/** A cache in memory, lost on restart: the default for a library user, and for specs. */
|
|
26
|
+
export declare function createMemoryPolicyCache(): PolicyCache;
|
|
27
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/sts/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAChC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,GAAG,CACF,MAAM,EAAE,MAAM,GACZ,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;IACxE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,wCAAwC;IACxC,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED,yFAAyF;AACzF,wBAAgB,uBAAuB,IAAI,WAAW,CAWrD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type FetchPolicyError } from '@bumail/mta-sts';
|
|
2
|
+
import type { PolicyCache } from './cache';
|
|
3
|
+
import type { MtaStsOptions, StsResolver } from './options';
|
|
4
|
+
/** `MtaStsOptions`, checked, with its defaults. */
|
|
5
|
+
export interface StsSettings {
|
|
6
|
+
readonly cache: PolicyCache;
|
|
7
|
+
readonly resolver: StsResolver;
|
|
8
|
+
readonly options: MtaStsOptions;
|
|
9
|
+
/** Fetches under way, by domain, so that sessions to one domain share one. */
|
|
10
|
+
readonly inflight: Map<string, Promise<Discovery>>;
|
|
11
|
+
/** Policy fetches that failed, by domain and record `id`, kept for `FAILED_FETCH_MS`: no session waits the whole fetch again (a `fresh` look fetches anyway). */
|
|
12
|
+
readonly failures: Map<string, FailedFetch>;
|
|
13
|
+
/** Aborted by `stop()`: a policy fetch under way gives up. */
|
|
14
|
+
signal(): AbortSignal;
|
|
15
|
+
abort(): void;
|
|
16
|
+
/** A fresh signal, once a `stop()` is over. */
|
|
17
|
+
reset(): void;
|
|
18
|
+
}
|
|
19
|
+
/** How long a failed policy fetch is remembered, in milliseconds. */
|
|
20
|
+
export declare const FAILED_FETCH_MS: number;
|
|
21
|
+
/** A policy fetch that failed, and until when it is not tried again. */
|
|
22
|
+
export interface FailedFetch {
|
|
23
|
+
readonly error: FetchPolicyError;
|
|
24
|
+
readonly until: number;
|
|
25
|
+
}
|
|
26
|
+
/** A policy in force, in mode `enforce` or `testing`. */
|
|
27
|
+
export interface ActivePolicy {
|
|
28
|
+
readonly domain: string;
|
|
29
|
+
readonly mode: 'enforce' | 'testing';
|
|
30
|
+
readonly mx: readonly string[];
|
|
31
|
+
readonly text: string;
|
|
32
|
+
}
|
|
33
|
+
/** What discovery found for a domain. */
|
|
34
|
+
export interface Discovery {
|
|
35
|
+
readonly policy?: ActivePolicy;
|
|
36
|
+
/** The policy could not be fetched (a cached one may still apply). */
|
|
37
|
+
readonly fetchError?: FetchPolicyError;
|
|
38
|
+
/** The queue is stopping: nothing is known, and nothing should be sent. */
|
|
39
|
+
readonly aborted?: true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The policy in force for a domain (RFC 8461 §5.1). The `_mta-sts` record
|
|
43
|
+
* is read first. With its `id` equal to the cached policy's, and the cache
|
|
44
|
+
* unexpired, the cache is used; with another `id`, or after expiry (or with
|
|
45
|
+
* `fresh`), the policy is fetched and cached. A policy that cannot be
|
|
46
|
+
* fetched, or a record that cannot be read, leaves the cached one in force
|
|
47
|
+
* while it has not expired (downgrade protection), and else no policy. The
|
|
48
|
+
* lookups and the cache are bounded by `fetchTimeoutMs` and by `stop()`.
|
|
49
|
+
* Sessions to one domain at once share one discovery.
|
|
50
|
+
*/
|
|
51
|
+
export declare function discover(sts: StsSettings, domain: string, now: () => number, fresh: boolean): Promise<Discovery>;
|
|
52
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/sts/discover.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EAKrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,SAAS,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE5D,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACnD,iKAAiK;IACjK,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5C,8DAA8D;IAC9D,MAAM,IAAI,WAAW,CAAC;IACtB,KAAK,IAAI,IAAI,CAAC;IACd,+CAA+C;IAC/C,KAAK,IAAI,IAAI,CAAC;CACd;AAED,qEAAqE;AACrE,eAAO,MAAM,eAAe,QAAa,CAAC;AAE1C,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,yCAAyC;AACzC,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IACvC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC;CACxB;AAgKD;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,MAAM,EACjB,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,SAAS,CAAC,CASpB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { PolicyFetch, TlsResultType } from '@bumail/mta-sts';
|
|
2
|
+
import type { QueueItem } from '../contract/types';
|
|
3
|
+
import type { PolicyCache } from './cache';
|
|
4
|
+
/** What the policy lookup asks DNS: `@bumail/dns`'s `Resolver` fits, as does any object of this shape. */
|
|
5
|
+
export interface StsResolver {
|
|
6
|
+
/** The TXT records of a name: strings, or records with a `text`. */
|
|
7
|
+
txt(name: string): Promise<readonly (string | {
|
|
8
|
+
readonly text: string;
|
|
9
|
+
})[]>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* MTA-STS (RFC 8461) on delivery by MX. Off unless `mtaSts` is given. The
|
|
13
|
+
* smarthost routes never use it.
|
|
14
|
+
*/
|
|
15
|
+
export interface MtaStsOptions {
|
|
16
|
+
/** Where fetched policies are kept. Default a cache in memory (`createMemoryPolicyCache()`). */
|
|
17
|
+
readonly cache?: PolicyCache;
|
|
18
|
+
/** The `fetch` that gets `https://mta-sts.<domain>/.well-known/mta-sts.txt`. Default `globalThis.fetch`. */
|
|
19
|
+
readonly fetch?: PolicyFetch;
|
|
20
|
+
/** Looks up `_mta-sts.<domain>`. Default the queue's `resolver` when it has `txt`; else `createQueue` refuses. */
|
|
21
|
+
readonly resolver?: StsResolver;
|
|
22
|
+
/** The whole policy fetch, in milliseconds. Default 60 000 (`@bumail/mta-sts`'s). */
|
|
23
|
+
readonly fetchTimeoutMs?: number;
|
|
24
|
+
/** The most `max_age` kept, in seconds. Default a year, RFC 8461's limit. */
|
|
25
|
+
readonly maxAgeCap?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Exempts a session from MTA-STS: `true` for an item and recipient
|
|
28
|
+
* domain means no discovery, no enforcement and no `tls` event for it.
|
|
29
|
+
* For mail that must go despite a TLS failure and must not be counted
|
|
30
|
+
* (a mailed TLS-RPT report, RFC 8460 §5.3), and for domains delivered
|
|
31
|
+
* in-process. A predicate that throws exempts nothing. Default: none.
|
|
32
|
+
*/
|
|
33
|
+
readonly skip?: (item: QueueItem, domain: string) => boolean;
|
|
34
|
+
}
|
|
35
|
+
/** What a `tls` event says of the policy a session ran under. */
|
|
36
|
+
export type TlsPolicyReport = {
|
|
37
|
+
readonly type: 'sts';
|
|
38
|
+
/** The domain the policy is of. */
|
|
39
|
+
readonly domain: string;
|
|
40
|
+
/** `enforce` or `testing`; a policy in mode `none` is `no-policy-found`. */
|
|
41
|
+
readonly mode: 'enforce' | 'testing';
|
|
42
|
+
readonly mx: readonly string[];
|
|
43
|
+
/** The policy file as served. */
|
|
44
|
+
readonly text: string;
|
|
45
|
+
} | {
|
|
46
|
+
readonly type: 'no-policy-found';
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* One try at one address of an MX host, or a policy that could not be had,
|
|
50
|
+
* as an RFC 8460 report counts them. Only on the `mx` route, when `mtaSts`
|
|
51
|
+
* is set.
|
|
52
|
+
*/
|
|
53
|
+
export interface TlsEvent {
|
|
54
|
+
/**
|
|
55
|
+
* `try`: one try at an MX address, or an MX host a policy refused without
|
|
56
|
+
* a try; `policy`: a policy that could not be fetched.
|
|
57
|
+
*/
|
|
58
|
+
readonly kind: 'try' | 'policy';
|
|
59
|
+
/** When it happened, in milliseconds since the epoch, on the queue's clock. */
|
|
60
|
+
readonly at: number;
|
|
61
|
+
/** The recipient domain. */
|
|
62
|
+
readonly domain: string;
|
|
63
|
+
readonly policy: TlsPolicyReport;
|
|
64
|
+
/** The MX host tried; absent for a policy fetch failure. */
|
|
65
|
+
readonly mxHost?: string;
|
|
66
|
+
/** The address tried; absent for a policy fetch failure and for an MX host a policy refused without a try. */
|
|
67
|
+
readonly address?: string;
|
|
68
|
+
/** The local address the connection was made from, when the sender says (`MxAttempt.localAddress`). */
|
|
69
|
+
readonly sendingAddress?: string;
|
|
70
|
+
/** On a try that delivered: whether it went over a connection the pool had used before. */
|
|
71
|
+
readonly reused?: boolean;
|
|
72
|
+
/** TLS held up (and, under a policy, the MX name and the certificate matched). */
|
|
73
|
+
readonly ok: boolean;
|
|
74
|
+
/** Why not: an RFC 8460 result type. Absent when `ok`. */
|
|
75
|
+
readonly tlsResult?: TlsResultType;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/sts/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,0GAA0G;AAC1G,MAAM,WAAW,WAAW;IAC3B,oEAAoE;IACpE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC,CAAC;CAC5E;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,4GAA4G;IAC5G,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,kHAAkH;IAClH,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,qFAAqF;IACrF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CAC7D;AAED,iEAAiE;AACjE,MAAM,MAAM,eAAe,GACxB;IACA,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,mCAAmC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACrB,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAExC;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACxB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8GAA8G;IAC9G,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;CACnC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Diagnostic, RecipientUpdate } from '../contract/types';
|
|
2
|
+
/** Whether a diagnostic is the queue's own refusal under an `enforce` policy (`4.7.5`), not a remote server's. */
|
|
3
|
+
export declare const isPolicyRefusal: (reply: Diagnostic | undefined) => boolean;
|
|
4
|
+
/**
|
|
5
|
+
* The updates of a session an `enforce` policy refused: deferred as `4.7.5`,
|
|
6
|
+
* or failed as `5.7.5` when `final`. The remote reply's code stays when its
|
|
7
|
+
* class matches the status.
|
|
8
|
+
*/
|
|
9
|
+
export declare function refusalUpdates(domain: string, updates: readonly RecipientUpdate[], final: boolean, why: string, maxText: number): RecipientUpdate[];
|
|
10
|
+
//# sourceMappingURL=refusal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refusal.d.ts","sourceRoot":"","sources":["../../src/sts/refusal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMrE,kHAAkH;AAClH,eAAO,MAAM,eAAe,GAAI,OAAO,UAAU,GAAG,SAAS,KAAG,OACtB,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GACb,eAAe,EAAE,CAmBnB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { MxDestination, SendMailResult, TlsMode } from '@bumail/smtp/client';
|
|
2
|
+
import type { RecipientUpdate } from '../contract/types';
|
|
3
|
+
import { type StsSettings } from './discover';
|
|
4
|
+
import type { TlsEvent } from './options';
|
|
5
|
+
/** What the session adds to the options of a delivery by MX. */
|
|
6
|
+
export interface StsSendOptions {
|
|
7
|
+
readonly mxNames?: readonly string[];
|
|
8
|
+
readonly tls?: TlsMode;
|
|
9
|
+
readonly onAttempt: NonNullable<MxDestination['onAttempt']>;
|
|
10
|
+
readonly onSkip?: NonNullable<MxDestination['onSkip']>;
|
|
11
|
+
}
|
|
12
|
+
/** MTA-STS as one session lives it. */
|
|
13
|
+
export interface StsRun {
|
|
14
|
+
readonly options: StsSendOptions;
|
|
15
|
+
/** An `enforce` policy governs the session: a failure of it never moves the mail to the fallback smarthost. */
|
|
16
|
+
readonly enforcing: boolean;
|
|
17
|
+
/** The send delivered: tells the tries, the delivering one with `result.reused`. */
|
|
18
|
+
delivered(result: SendMailResult): void;
|
|
19
|
+
/**
|
|
20
|
+
* The send threw: tells the tries; and under an `enforce` policy, when
|
|
21
|
+
* the error (or any try of the session) is a TLS or MX-name failure, the
|
|
22
|
+
* updates become `4.7.5` deferrals, or `5.7.5` failures once the time of
|
|
23
|
+
* giving up has come. `refused` says they are the policy's.
|
|
24
|
+
*/
|
|
25
|
+
failed(error: unknown, updates: RecipientUpdate[]): {
|
|
26
|
+
readonly updates: RecipientUpdate[];
|
|
27
|
+
readonly refused: boolean;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** What a session starts from. */
|
|
31
|
+
export interface StsInput {
|
|
32
|
+
readonly sts: StsSettings;
|
|
33
|
+
readonly domain: string;
|
|
34
|
+
readonly now: () => number;
|
|
35
|
+
/** The policy is fetched again whatever the cache holds: the attempt may run past the time of giving up. */
|
|
36
|
+
readonly refresh: boolean;
|
|
37
|
+
/** When the recipients give up, in milliseconds since the epoch. */
|
|
38
|
+
readonly giveUpAt: number;
|
|
39
|
+
readonly emit: (event: TlsEvent) => void;
|
|
40
|
+
/** The longest diagnostic text. */
|
|
41
|
+
readonly maxText: number;
|
|
42
|
+
}
|
|
43
|
+
/** The queue is stopping and the policy is unknown: nothing should be sent. */
|
|
44
|
+
export interface StsHalt {
|
|
45
|
+
readonly halt: true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Finds the policy for the domain, tells a failure to get it, and answers
|
|
49
|
+
* what to add to the send: `enforce` requires TLS and the policy's MX
|
|
50
|
+
* names, `testing` and none change nothing. Tries are told as `tls` events
|
|
51
|
+
* once the send ends. Whatever the lookup throws is "no policy" and an
|
|
52
|
+
* `sts-policy-fetch-error`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function prepare(input: StsInput): Promise<StsRun | StsHalt>;
|
|
55
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/sts/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,aAAa,EACb,cAAc,EACd,OAAO,EACP,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EAIN,KAAK,WAAW,EAChB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAI1C,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;CACvD;AAED,uCAAuC;AACvC,MAAM,WAAW,MAAM;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,+GAA+G;IAC/G,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oFAAoF;IACpF,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IACxC;;;;;OAKG;IACH,MAAM,CACL,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,eAAe,EAAE,GACxB;QAAE,QAAQ,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CACtE;AAED,kCAAkC;AAClC,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,4GAA4G;IAC5G,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACzC,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAiExE"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { StsSettings } from './discover';
|
|
2
|
+
import type { MtaStsOptions } from './options';
|
|
3
|
+
/** `mtaSts` checked, with its defaults; `undefined` when it is not given. `resolver` is the queue's own, used when it has `txt`. */
|
|
4
|
+
export declare function stsOf(options: MtaStsOptions | undefined, resolver: unknown): StsSettings | undefined;
|
|
5
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/sts/settings.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,WAAW,CAAC;AAK5D,oIAAoI;AACpI,wBAAgB,KAAK,CACpB,OAAO,EAAE,aAAa,GAAG,SAAS,EAClC,QAAQ,EAAE,OAAO,GACf,WAAW,GAAG,SAAS,CAwDzB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MxAttempt } from '@bumail/smtp/client';
|
|
2
|
+
import type { ActivePolicy } from './discover';
|
|
3
|
+
import type { TlsEvent } from './options';
|
|
4
|
+
/** What a session's tries came to, kept until the send ends. */
|
|
5
|
+
export interface Tally {
|
|
6
|
+
readonly tries: {
|
|
7
|
+
event: TlsEvent;
|
|
8
|
+
delivered: boolean;
|
|
9
|
+
}[];
|
|
10
|
+
/** MX hosts a policy refused without a try. */
|
|
11
|
+
readonly skipped: string[];
|
|
12
|
+
/** Some try had a `tlsResult`. */
|
|
13
|
+
tlsFailure: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const newTally: () => Tally;
|
|
16
|
+
/** Notes a try: its `tls` event, and whether it shows a TLS failure. */
|
|
17
|
+
export declare function record(tally: Tally, domain: string, policy: ActivePolicy | undefined, attempt: MxAttempt, at: number): void;
|
|
18
|
+
/** Tells the skipped hosts, then the tries, the delivering one with `reused`. */
|
|
19
|
+
export declare function tell(tally: Tally, emit: (event: TlsEvent) => void, domain: string, policy: ActivePolicy | undefined, now: () => number, reused: boolean | undefined): void;
|
|
20
|
+
//# sourceMappingURL=tally.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tally.d.ts","sourceRoot":"","sources":["../../src/sts/tally.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,gEAAgE;AAChE,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC1D,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,kCAAkC;IAClC,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,QAAQ,QAAO,KAI1B,CAAC;AAEH,wEAAwE;AACxE,wBAAgB,MAAM,CACrB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,OAAO,EAAE,SAAS,EAClB,EAAE,EAAE,MAAM,GACR,IAAI,CAIN;AAED,iFAAiF;AACjF,wBAAgB,IAAI,CACnB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,EAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,MAAM,EACjB,MAAM,EAAE,OAAO,GAAG,SAAS,GACzB,IAAI,CAeN"}
|
package/docs/guide.md
CHANGED
|
@@ -7,6 +7,7 @@ workers share it.
|
|
|
7
7
|
- [The smallest queue](#the-smallest-queue)
|
|
8
8
|
- [Enqueuing](#enqueuing)
|
|
9
9
|
- [Delivery and routing](#delivery-and-routing)
|
|
10
|
+
- [MTA-STS](#mta-sts)
|
|
10
11
|
- [Retries](#retries)
|
|
11
12
|
- [Delivery status notifications](#delivery-status-notifications)
|
|
12
13
|
- [Several workers, leases and stopping](#several-workers-leases-and-stopping)
|
|
@@ -164,7 +165,7 @@ outcome of the first session. A plain `5xx` (`5.1.x` unknown mailbox,
|
|
|
164
165
|
the MX (an `SmtpError` coded `TLS_*`, a refused STARTTLS `454 4.7.0` under
|
|
165
166
|
`mxTls: 'required'` included) never use it. A greylisting (a `4.7.x` whose
|
|
166
167
|
text says grey/greylist) is the MX's to retry, and does not either. Only
|
|
167
|
-
domains whose route is `'mx'` can fall back.
|
|
168
|
+
domains whose route is `'mx'` can fall back. Under an `enforce` MTA-STS policy (`mtaSts`) nothing falls back, whatever failed: when every MX refuses the connection (4.4.1, 4.4.2) the recipient is deferred for the next attempt, since the smarthost would send the mail without the policy's TLS and name checks. In `testing` mode, or with no policy, it falls back as above. That holds for a reply too: under `enforce` no failure falls back, even a `5.7.x` reputation refusal from an MX that met the policy.
|
|
168
169
|
|
|
169
170
|
A connection lost or a timeout *after* the final dot of `DATA` does not
|
|
170
171
|
fall back either, though it reads as `4.4.2`: the MX may already have taken
|
|
@@ -349,6 +350,65 @@ A store keeps an item from being claimed before its message can be read
|
|
|
349
350
|
at a claim is always damage, never an add still under way. Every store
|
|
350
351
|
here adds both in one step.
|
|
351
352
|
|
|
353
|
+
## MTA-STS
|
|
354
|
+
|
|
355
|
+
`mtaSts` turns on MTA-STS (RFC 8461) for the recipient domains that go by MX. It needs `@bumail/mta-sts` (a peer) and a resolver with `txt`: `mtaSts.resolver`, or the queue's own `resolver` when it has `txt` (`@bumail/dns`'s does).
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
const REPORT_ENVID = 'my-report-7f3a'; // what you enqueue your own reports with: `{ from, to, envid: REPORT_ENVID }`
|
|
359
|
+
const hosted = new Set(['example.net']); // domains you deliver in-process
|
|
360
|
+
|
|
361
|
+
createQueue({
|
|
362
|
+
store,
|
|
363
|
+
hostname: 'mail.example.net',
|
|
364
|
+
resolver: nodeResolver(),
|
|
365
|
+
mtaSts: {
|
|
366
|
+
cache: createMemoryPolicyCache(), // the default; a PolicyCache of yours survives restarts
|
|
367
|
+
fetch: globalThis.fetch, // the default
|
|
368
|
+
fetchTimeoutMs: 60_000, // the whole policy fetch
|
|
369
|
+
maxAgeCap: 31_557_600, // seconds
|
|
370
|
+
skip: (item, domain) => item.envid === REPORT_ENVID || hosted.has(domain), // exempt from MTA-STS
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Finding the policy.** The queue reads the TXT record `_mta-sts.<domain>`. A record whose `id` is the cached policy's, with the cache not expired, uses the cache. A new `id`, or an expired cache, fetches the policy and caches it until its `max_age` (on the queue's `clock`). No record and nothing cached means no policy. A fetch that fails (or a record that cannot be read) leaves a cached policy in force while it has not expired, as RFC 8461 §5.1 asks, and else no policy: the message is sent as without MTA-STS, and a `tls` event with `tlsResult: 'sts-policy-fetch-error'` (or `sts-policy-invalid`, `sts-webpki-invalid`) is told. A policy in mode `none` is no policy. The whole discovery (the cache, the TXT lookup and the fetch together) is bounded by one `fetchTimeoutMs`, not each step by its own (a lookup that times out is no record, a cache that times out, given at most a quarter of it, is empty). A policy fetch that fails is remembered for 5 minutes, per domain and record `id`, in the queue's memory: the sessions in between wait no longer for it, and each still tells the `sts-policy-fetch-error`. A new `id`, or a restart, fetches again. `stop()` aborts a lookup or fetch under way; the group is then left for later like one not started: nothing is sent, no attempt is counted, and the item stays pending. A cache entry that is malformed is ignored, and whatever else breaks the lookup is "no policy" and an `sts-policy-fetch-error` event, never a failed delivery.
|
|
376
|
+
|
|
377
|
+
**Modes.**
|
|
378
|
+
|
|
379
|
+
| mode | the session | on a failure |
|
|
380
|
+
| --- | --- | --- |
|
|
381
|
+
| `enforce` | `tls: 'required'` (over `mxTls`), `mxNames` = the policy's `mx` | an error with a `tlsResult` (an MX outside the policy: `MX_NOT_ALLOWED`; a certificate or STARTTLS failure) defers as `4.7.5`; never the `fallback` smarthost |
|
|
382
|
+
| `testing` | as without a policy: your `mxTls`, every MX host | none; each try is told on `tls`, with what `enforce` would have refused (a name outside the policy, an unchecked certificate, no TLS) |
|
|
383
|
+
| `none`, or no policy | as without MTA-STS | none |
|
|
384
|
+
|
|
385
|
+
**Exemptions.** `skip(item, domain)` returns `true` for a session that must not be under MTA-STS: no discovery, no enforcement and no `tls` event. Use it for mail that must go despite a TLS failure and must not be counted (a mailed TLS-RPT report, RFC 8460 §5.3) and for domains you deliver in-process; mark the first with the `envid` of `enqueue`, which the item keeps. A predicate that throws exempts nothing.
|
|
386
|
+
|
|
387
|
+
**Bouncing.** An `enforce` failure defers with the enhanced status `4.7.5` and the usual back-off. An attempt that may run past `retry.giveUpAfter` (it starts within `deadline` plus `fetchTimeoutMs` of it, which bounds the discovery) fetches the policy again, whatever the cache holds; whether the recipient fails as `5.7.5` or defers as `4.7.5` is decided when the failure happens, by the clock. A session in which any try broke the policy is a refusal even if its last error did not (a refused MX, then a connection failure). With that fresh policy: a policy that is gone (or in mode `none`) delivers the message normally; one still in `enforce` that fails bounces the recipient as `5.7.5` (a `failed` event and a DSN) instead of `4.4.7`. A cached policy that has not expired stays in force even when the TXT record has been removed; a domain that leaves MTA-STS publishes mode `none` first.
|
|
388
|
+
|
|
389
|
+
**The `tls` event** has one payload per try at an MX address, and one for a policy that could not be fetched (no `mxHost`):
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
queue.on('tls', (event) => {
|
|
393
|
+
event.kind; // 'try' | 'policy' (a policy that could not be fetched)
|
|
394
|
+
event.at; // ms since the epoch, on the queue's clock
|
|
395
|
+
event.domain; // the recipient domain
|
|
396
|
+
event.policy; // { type: 'sts', domain, mode: 'enforce' | 'testing', mx, text } | { type: 'no-policy-found' }
|
|
397
|
+
event.mxHost; // absent for a fetch failure
|
|
398
|
+
event.address; // absent for a fetch failure and for an MX host the policy refused without a try
|
|
399
|
+
event.sendingAddress; // the local address of the connection, when the sender says
|
|
400
|
+
event.reused; // on the try that delivered: a connection the pool had used before
|
|
401
|
+
event.ok; // TLS held up; under a policy the MX name and the certificate matched
|
|
402
|
+
event.tlsResult; // RFC 8460 result type, absent when ok
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
A try that failed before any TLS (the connection itself) is not told. In `enforce`, an MX host the policy skipped (or all of them, when none matches) is told once, without `address`, with `validation-failure`. Tries are told once the send ends.
|
|
407
|
+
|
|
408
|
+
**Your own `send`.** The queue builds the MX options and gives them to `send`; with `mtaSts` they hold `mxNames`, `tls`, `onAttempt` and `onSkip` on top. A `send` that hands them to `sendMail` (or `createSmtpPool().sendMail`) is under the policy; one that drops them is not, and the queue cannot tell.
|
|
409
|
+
|
|
410
|
+
**Not covered.** DANE, TLS reports (they are built from `tls` events by `@bumail/mta-sts`'s `tlsReport`), and the smarthost routes.
|
|
411
|
+
|
|
352
412
|
## Retries
|
|
353
413
|
|
|
354
414
|
```ts
|
|
@@ -576,6 +636,7 @@ off(); // stops listening
|
|
|
576
636
|
| `delivered` | `{ id, from, recipient, reply?, attempts, route?, fallback?, mxReply? }`: `route` is `'mx'` or `'smarthost'`, `fallback` is `true` after the [fallback](#falling-back-to-a-smarthost), `mxReply` the MX's reply that led to it |
|
|
577
637
|
| `deferred` | the same, and `nextAttemptAt`, and `mayHaveReachedMx: true` when the MX may hold a copy, `mayHaveReachedFallback: true` when the fallback smarthost may ([why](#falling-back-to-a-smarthost)) |
|
|
578
638
|
| `failed` | the same as `delivered`, and `mayHaveReachedMx` and `mayHaveReachedFallback` as for `deferred` |
|
|
639
|
+
| `tls` | `{ domain, policy, mxHost?, address?, ok, tlsResult? }`: one per try at an MX address under `mtaSts`, or per policy fetch failure; see [MTA-STS](#mta-sts) |
|
|
579
640
|
| `dsn` | `{ kind: 'delayed' \| 'failed' \| 'relayed', id, of, to, recipients }`: `id` is the DSN's own item, `of` the item it reports on |
|
|
580
641
|
| `error` | `{ error, id? }`: a store that failed (a lease renewal included), a DSN that could not be enqueued, a lease lost (`LEASE_LOST`), a message the store lost (`MESSAGE_UNREADABLE`), a route `sendMail` refused (`INVALID_OPTION`) |
|
|
581
642
|
|
|
@@ -1172,6 +1233,7 @@ connection or remove a file. It may be async.
|
|
|
1172
1233
|
| `hostname` | `string` | — | EHLO, Reporting-MTA, the DSN's From |
|
|
1173
1234
|
| `route` | `'mx' \| Smarthost` | `'mx'` | the default route |
|
|
1174
1235
|
| `routes` | `Record<string, Route>` | — | a route per recipient domain |
|
|
1236
|
+
| `mtaSts` | `{ cache?, fetch?, resolver?, fetchTimeoutMs?, maxAgeCap?, skip? }` | off | MTA-STS on the MX route; see [MTA-STS](#mta-sts) |
|
|
1175
1237
|
| `fallback` | `Smarthost` | — | where a recipient goes at once when MX fails by connection, timeout or a reputation refusal; see [Falling back](#falling-back-to-a-smarthost) |
|
|
1176
1238
|
| `resolver` | `MxResolver` | — | needed by `'mx'` |
|
|
1177
1239
|
| `mxPort` | `number`, 1–65535 | 25 | the port of MX hosts |
|