@serve.zone/dcrouter 18.6.2 → 18.7.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 (41) hide show
  1. package/deno.json +1 -1
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/classes.dcrouter.js +4 -1
  4. package/dist_ts/dns/classes.dns-server-runtime.js +3 -3
  5. package/dist_ts/dns/manager.dns.d.ts +16 -0
  6. package/dist_ts/dns/manager.dns.js +193 -40
  7. package/dist_ts/dns/providers/cloudflare.provider.d.ts +5 -0
  8. package/dist_ts/dns/providers/cloudflare.provider.js +78 -23
  9. package/dist_ts/dns/providers/interfaces.d.ts +2 -0
  10. package/dist_ts/email/classes.email-domain.manager.d.ts +1 -0
  11. package/dist_ts/email/classes.email-domain.manager.js +36 -8
  12. package/dist_ts/email/classes.mail-dns-sync.d.ts +8 -0
  13. package/dist_ts/email/classes.mail-dns-sync.js +239 -55
  14. package/dist_ts/email/classes.mail-edge-eligibility.js +44 -17
  15. package/dist_ts/email/classes.mail-egress-coordinator.js +13 -3
  16. package/dist_ts/email/reconcile-mail-dns-singleton.d.ts +1 -0
  17. package/dist_ts/email/reconcile-mail-dns-singleton.js +6 -2
  18. package/dist_ts/opsserver/handlers/dns-provider.handler.js +3 -1
  19. package/dist_ts/opsserver/handlers/dns-record.handler.js +44 -35
  20. package/dist_ts/opsserver/handlers/domain.handler.js +13 -2
  21. package/dist_ts/remoteingress/classes.hub-lifecycle.js +2 -1
  22. package/dist_ts_interfaces/data/email-domain.d.ts +2 -0
  23. package/dist_ts_web/00_commitinfo_data.js +1 -1
  24. package/package.json +4 -4
  25. package/readme.md +7 -7
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/classes.dcrouter.ts +4 -0
  28. package/ts/dns/classes.dns-server-runtime.ts +2 -2
  29. package/ts/dns/manager.dns.ts +235 -35
  30. package/ts/dns/providers/cloudflare.provider.ts +93 -26
  31. package/ts/dns/providers/interfaces.ts +3 -0
  32. package/ts/email/classes.email-domain.manager.ts +38 -6
  33. package/ts/email/classes.mail-dns-sync.ts +246 -53
  34. package/ts/email/classes.mail-edge-eligibility.ts +53 -15
  35. package/ts/email/classes.mail-egress-coordinator.ts +18 -4
  36. package/ts/email/reconcile-mail-dns-singleton.ts +8 -1
  37. package/ts/opsserver/handlers/dns-provider.handler.ts +2 -0
  38. package/ts/opsserver/handlers/dns-record.handler.ts +58 -49
  39. package/ts/opsserver/handlers/domain.handler.ts +11 -1
  40. package/ts/remoteingress/classes.hub-lifecycle.ts +1 -0
  41. package/ts_web/00_commitinfo_data.ts +1 -1
@@ -150,6 +150,7 @@ export class RemoteIngressHubLifecycle {
150
150
  this.dcRouterRef.tunnelManager = undefined;
151
151
  }
152
152
  }
153
+ this.dcRouterRef.mailDnsSync.requestEdgeEligibilityCheck('RemoteIngress hub stopped');
153
154
  }
154
155
  async restartTunnelHubLocked() {
155
156
  const generation = ++this.generation;
@@ -239,4 +240,4 @@ export class RemoteIngressHubLifecycle {
239
240
  return tlsConfig;
240
241
  }
241
242
  }
242
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5odWItbGlmZWN5Y2xlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcmVtb3RlaW5ncmVzcy9jbGFzc2VzLmh1Yi1saWZlY3ljbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUMsT0FBTyxFQUFFLG9CQUFvQixFQUFxQyxNQUFNLG9DQUFvQyxDQUFDO0FBQzdHLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUk1RDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLHlCQUF5QjtJQUtoQjtJQUpaLGNBQWMsR0FBa0IsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2xELFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDakIsVUFBVSxHQUFHLENBQUMsQ0FBQztJQUV2QixZQUFvQixXQUFxQjtRQUFyQixnQkFBVyxHQUFYLFdBQVcsQ0FBVTtJQUFHLENBQUM7SUFFdEMsS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLG9CQUFvQixDQUFDO1FBQ25FLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1lBQzFCLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsb0JBQW9CLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDMUQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN6QixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSwrQ0FBK0MsQ0FBQyxDQUFDO1lBQ3BFLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsa0NBQWtDLENBQUMsQ0FBQztRQUN2RCxJ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
243
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5odWItbGlmZWN5Y2xlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcmVtb3RlaW5ncmVzcy9jbGFzc2VzLmh1Yi1saWZlY3ljbGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDOUMsT0FBTyxFQUFFLG9CQUFvQixFQUFxQyxNQUFNLG9DQUFvQyxDQUFDO0FBQzdHLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUk1RDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLHlCQUF5QjtJQUtoQjtJQUpaLGNBQWMsR0FBa0IsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2xELFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDakIsVUFBVSxHQUFHLENBQUMsQ0FBQztJQUV2QixZQUFvQixXQUFxQjtRQUFyQixnQkFBVyxHQUFYLFdBQVcsQ0FBVTtJQUFHLENBQUM7SUFFdEMsS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxvQkFBb0IsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLG9CQUFvQixDQUFDO1FBQ25FLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1lBQzFCLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsb0JBQW9CLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDMUQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN6QixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSwrQ0FBK0MsQ0FBQyxDQUFDO1lBQ3BFLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsa0NBQWtDLENBQUMsQ0FBQztRQUN2RCxJ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
@@ -86,6 +86,8 @@ export interface IEmailDomainReconciliation {
86
86
  lastSuccessAt?: string;
87
87
  retryAt?: string;
88
88
  deletingAt?: string;
89
+ /** Provider record ids whose absence must be confirmed before a deletion tombstone can finalize. */
90
+ deletingProviderRecordIds?: string[];
89
91
  }
90
92
  /**
91
93
  * An email domain managed by dcrouter.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '18.6.2',
6
+ version: '18.7.1',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViLzAwX2NvbW1pdGluZm9fZGF0YS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4QixJQUFJLEVBQUUsc0JBQXNCO0lBQzVCLE9BQU8sRUFBRSxRQUFRO0lBQ2pCLFdBQVcsRUFBRSwwRUFBMEU7Q0FDeEYsQ0FBQSJ9
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "18.6.2",
4
+ "version": "18.7.1",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -18,8 +18,8 @@
18
18
  "@git.zone/tsbuild": "^4.4.2",
19
19
  "@git.zone/tsbundle": "^2.11.3",
20
20
  "@git.zone/tsdeno": "^1.7.0",
21
- "@git.zone/tsdocker": "^3.1.0",
22
- "@git.zone/tsrun": "^2.0.5",
21
+ "@git.zone/tsdocker": "^3.1.1",
22
+ "@git.zone/tsrun": "^2.0.6",
23
23
  "@git.zone/tstest": "^3.6.7",
24
24
  "@git.zone/tswatch": "^3.3.5",
25
25
  "@types/node": "26.1.1",
@@ -30,7 +30,7 @@
30
30
  "@api.global/typedrequest-interfaces": "^3.0.19",
31
31
  "@api.global/typedserver": "^8.6.4",
32
32
  "@api.global/typedsocket": "^5.0.0",
33
- "@apiclient.xyz/cloudflare": "^7.1.0",
33
+ "@apiclient.xyz/cloudflare": "^8.0.0",
34
34
  "@design.estate/dees-catalog": "^3.99.0",
35
35
  "@design.estate/dees-element": "^2.4.0",
36
36
  "@idp.global/sdk": "^14.3.0",
package/readme.md CHANGED
@@ -121,7 +121,7 @@ Bootstrap behavior:
121
121
  | `baseDir` | Root directory for dcrouter runtime data. Defaults to `~/.serve.zone/dcrouter`. |
122
122
  | `coreTrafficConfig` | Main CoreTraffic route configuration for HTTP/HTTPS/TCP/SNI traffic. `smartProxyConfig` remains accepted as a legacy alias. |
123
123
  | `emailConfig` | UnifiedEmailServer configuration: hostname, ports, domains, and mail routes. |
124
- | `emailOutboundMode` | Outbound SMTP mode. Defaults to `direct`; `remoteIngress` routes outbound SMTP through a RemoteIngress egress edge. |
124
+ | `emailOutboundMode` | Outbound SMTP mode. Defaults to `remoteIngress`; explicit `direct` mode lets unmanaged/static senders dial from the hub. |
125
125
  | `emailPortConfig` | External-to-internal email port mapping and received-email storage path. |
126
126
  | `tls` | Legacy/static TLS and ACME contact settings used to seed certificate config. |
127
127
  | `dnsNsDomains` | Nameserver hostnames used for generated NS records and DoH routes. A zone becomes authoritative by having its public NS records observed naming one of these. |
@@ -495,7 +495,7 @@ from the destination server.
495
495
 
496
496
  ### RemoteIngress SMTP Egress
497
497
 
498
- The email settings API and Ops UI expose `outboundMode`. Keep it at `direct` to let SmartMTA dial destination MX hosts from the dcrouter hub. Set it to `remoteIngress` to make SmartMTA request a one-shot RemoteIngress TCP egress proxy for each outbound SMTP connection while keeping the logical MX host and port for SMTP/TLS identity.
498
+ The email settings API and Ops UI expose `outboundMode`. Keep it at `direct` to let unmanaged/static senders dial destination MX hosts from the dcrouter hub; managed sender domains are always rejected in direct mode. Set it to `remoteIngress` to make SmartMTA request a one-shot RemoteIngress TCP egress proxy for each outbound SMTP connection while keeping the logical MX host and port for SMTP/TLS identity. Direct mode is explicit operator policy, never an automatic fallback.
499
499
 
500
500
  RemoteIngress edge create/update APIs accept an `egress` policy. Egress is default-disabled and is propagated to the hub only when `egress.enabled === true`.
501
501
 
@@ -510,17 +510,17 @@ Supported egress policy fields:
510
510
  | `deniedCidrs` | CIDRs denied after edge-side DNS resolution. |
511
511
  | `maxConcurrentStreams` | Optional concurrent outbound SMTP stream cap for the edge. |
512
512
 
513
- RemoteIngress status responses include `capabilities`, `egressEnabled`, and source-bound egress identity. A mail edge is eligible only when it is enabled, carries the `mail` tag, permits destination port `25`, is connected with a fresh heartbeat, uses native QUIC without TCP fallback, advertises both `egressTcpV1` and `egressIdentityV1`, and reports a fresh non-stale `localSocketBind` identity. That identity must match the edge's declared `publicIp` or `publicIpV6`, the edge must have a `mailHostname`, its PTR must resolve exactly to that hostname, and the hostname's A/AAAA set must contain the source address. Domain edge filters narrow this pool and remain fail-closed when they match nothing.
513
+ RemoteIngress status responses include `capabilities`, `egressEnabled`, and source-bound egress identity. A mail edge is eligible only when it is enabled, carries the `mail` tag, permits destination port `25`, is connected with a fresh heartbeat, uses native QUIC without TCP fallback, advertises both `egressTcpV1` and `egressIdentityV1`, and reports a fresh non-stale `localSocketBind` identity. Every configured address family must have exactly one matching fresh proof: configuring both `publicIp` and `publicIpV6` requires valid IPv4 and IPv6 identities. Each identity must match the edge's declared address, the edge must have a `mailHostname`, its PTR must resolve exactly to that hostname, and the hostname's A/AAAA set must contain the source address. Domain edge filters narrow this pool and remain fail-closed when they match nothing.
514
514
 
515
515
  RemoteIngress v5 edge runtimes must configure `egressSourceIpv4` and/or `egressSourceIpv6` with concrete addresses assigned to the edge host. The equivalent CLI flags are `--egress-source-ipv4` and `--egress-source-ipv6`; the environment variables are `REMOTEINGRESS_EGRESS_SOURCE_IPV4` and `REMOTEINGRESS_EGRESS_SOURCE_IPV6`. These addresses must match dcrouter's declared edge IPs. Every outbound socket is bound to the matching family, and a missing or unbindable family fails closed. There is no OS-selected address or direct-hub fallback in `remoteIngress` mode.
516
516
 
517
- Managed email domains use the durable lifecycle `pending -> active | failed -> deleting`; the document is removed only after deletion cleanup completes. Creation must obtain a usable DKIM key before persistence, then automatically reconciles and validates MX, SPF, DKIM, DMARC, and direct per-edge A/AAAA records. Inbound acceptance is independent and remains available while outbound SMTP credentials and sending are gated until the domain has a validated `activeRevision` and a live eligible edge.
517
+ Managed email domains use the durable lifecycle `pending -> active | failed -> deleting`; the document is removed only after deletion cleanup completes. Creation must obtain a usable DKIM key before persistence, then automatically reconciles and validates MX, SPF, DKIM, DMARC, and direct per-edge A/AAAA records. Inbound acceptance is independent and remains available while outbound SMTP credentials and sending are gated until the domain has a validated `activeRevision` and both selected MX edges are live.
518
518
 
519
- One eligible edge publishes one priority 10 MX target. Two or more eligible edges publish distinct stable priority 10 and priority 20 targets, each backed directly by A/AAAA rather than CNAME. SPF uses `-all` and authorizes every IPv4/IPv6 source identity required by the active or make-before-break transition revision. Activation requires the complete MX RRset, all record intents, and exact PTR plus forward-confirmed A/AAAA validation. Topology replacement validates the desired state before activation, then removes stale owned records.
519
+ Managed domains require two eligible edges with distinct hostnames. The reconciler selects stable priority 10 and priority 20 targets and uses that same two-edge set for direct A/AAAA records, public validation, the active revision, and outbound egress. Zero or one eligible edge produces no partial desired MX/SPF set and never falls back to the hub. SPF uses `-all`; steady state authorizes the selected pair, while a make-before-break transition temporarily authorizes both the active and pending pairs until activation triggers immediate contraction. Activation requires the complete MX RRset, all record intents, and exact PTR plus forward-confirmed A/AAAA validation. Topology replacement validates the desired state before activation, then removes stale owned records; an incomplete pinned topology withdraws the previous owned MX/SPF state.
520
520
 
521
- The reconciler is globally serialized and coalesces concurrent triggers. It runs at startup, every five minutes, and after relevant domain, DNS, DKIM, topology, or egress-identity changes. Provider state is refreshed on each pass; only records marked `managedBy: 'mail-dns-reconciler'` are mutated or deleted. Unmanaged SPF, DMARC, DKIM, or MX conflicts fail visibly instead of being overwritten. Each domain persists desired and active revisions, per-record intent status, provider/validation errors, attempt and validation timestamps, capability state, and `retryAt`. Provider/API failures propagate through the email-domain API and never become false provisioning success.
521
+ The reconciler is globally serialized and coalesces concurrent triggers. It runs at startup, schedules desired-state checks no more often than every five minutes, and reacts to relevant domain, DNS, DKIM, topology, or egress-identity changes. Failed domains honor persisted `retryAt` state with exponential backoff up to one hour. Provider state is refreshed on each pass; only records marked `managedBy: 'mail-dns-reconciler'` are mutated or deleted. Unmanaged SPF, DMARC, DKIM, or MX conflicts fail visibly instead of being overwritten. Each domain persists desired and active revisions, per-record intent status, provider/validation errors, attempt and validation timestamps, capability state, and `retryAt`. Provider/API failures propagate through the email-domain API and never become false provisioning success. Provider deletion retains local ownership until the provider returns an explicit not-found response or a complete provider-ID listing confirms absence; DNS domain migration/deletion and forced provider deletion are refused while managed email references or reconciler-owned mail records remain.
522
522
 
523
- DKIM generation failure is atomic. Automatic rotation defaults to 90 days with a 30-day retiring-selector overlap. dcrouter publishes and validates a staged selector, requires SmartMTA's selector-correct signing capability, promotes it for signing only after the DNS revision validates, retains the previous selector for the overlap, and later removes the owned record.
523
+ DKIM generation failure is atomic. Automatic rotation defaults to 90 days with a 30-day retiring-selector overlap; `rotationIntervalDays` must be a positive integer. dcrouter publishes and validates a staged selector, requires SmartMTA's selector-correct signing capability, promotes it for signing only after the DNS revision validates, retains the previous selector for the overlap, and schedules retirement at `retireAfter`. Retiring metadata is pruned only after the owned DNS record has been removed successfully.
524
524
 
525
525
  The additive public contracts include `IEmailDomainActionResult`, `IEmailDomain.reconciliation`, lifecycle/error/capability/retry fields, active/pending/retiring DKIM material, DNS intents and revisions, and edge identities. For mutation requests, `success: true` means the operation was accepted and durably recorded; `lifecycleStatus: 'pending'` or `'deleting'` still means activation or cleanup has not completed.
526
526
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '18.6.2',
6
+ version: '18.7.1',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -1891,6 +1891,10 @@ export class DcRouter {
1891
1891
  // final durable authentication-event and email-traffic drain.
1892
1892
  await this.stopOpsServer();
1893
1893
 
1894
+ // Mail DNS reconciliation may legitimately outlive ServiceManager's
1895
+ // non-cancelling per-service timeout. Drain it before dependency teardown.
1896
+ await this.mailDnsSync.stop();
1897
+
1894
1898
  // ServiceManager handles reverse-dependency-ordered shutdown
1895
1899
  await this.serviceManager.stop();
1896
1900
 
@@ -560,8 +560,8 @@ export class DnsServerRuntime {
560
560
  case 'A':
561
561
  return value; // IP address as string
562
562
  case 'MX':
563
- const [priority, exchange] = value.split(' ');
564
- return { priority: parseInt(priority), exchange };
563
+ const [preference, exchange] = value.split(' ');
564
+ return { preference: parseInt(preference, 10), exchange };
565
565
  case 'TXT':
566
566
  return value;
567
567
  case 'NS':
@@ -4,6 +4,7 @@ import {
4
4
  DnsProviderDoc,
5
5
  DomainDoc,
6
6
  DnsRecordDoc,
7
+ EmailDomainDoc,
7
8
  } from '../db/documents/index.js';
8
9
  import type { IDcRouterOptions } from '../classes.dcrouter.js';
9
10
  import type { IDnsProviderClient, IProviderRecord } from './providers/interfaces.js';
@@ -41,6 +42,12 @@ export interface IDnsRuntimeRegistration {
41
42
  origin: TDnsRuntimeRegistrationOrigin;
42
43
  }
43
44
 
45
+ export interface IDnsRecordDeleteResult {
46
+ id: string;
47
+ success: boolean;
48
+ message?: string;
49
+ }
50
+
44
51
  /**
45
52
  * DnsManager — owns runtime DNS state on top of the embedded DnsServer.
46
53
  *
@@ -83,11 +90,29 @@ export class DnsManager {
83
90
  */
84
91
  private runtimeRegistrations = new Map<string, Map<string, IDnsRuntimeRegistration>>();
85
92
 
93
+ /** Serializes managed-mail reconciliation with destructive domain/provider deletion. */
94
+ private managedMailDnsMutationChain: Promise<void> = Promise.resolve();
95
+ private acceptsManagedMailDnsMutations = true;
96
+
86
97
  /** Supplies the effective authority set (bootstrap ∪ delegation-verified). */
87
98
  private authorityZonesResolver?: () => string[];
88
99
 
89
100
  constructor(private options: IDcRouterOptions) {}
90
101
 
102
+ public async runManagedMailDnsMutationExclusive<T>(task: () => Promise<T>): Promise<T> {
103
+ if (!this.acceptsManagedMailDnsMutations) {
104
+ throw new Error('DnsManager is stopping; managed mail DNS mutations are closed');
105
+ }
106
+ const run = this.managedMailDnsMutationChain.then(async () => {
107
+ if (!this.acceptsManagedMailDnsMutations) {
108
+ throw new Error('DnsManager stopped before the managed mail DNS mutation could run');
109
+ }
110
+ return await task();
111
+ });
112
+ this.managedMailDnsMutationChain = run.then(() => undefined, () => undefined);
113
+ return await run;
114
+ }
115
+
91
116
  // ==========================================================================
92
117
  // Runtime registration registry
93
118
  // ==========================================================================
@@ -161,10 +186,13 @@ export class DnsManager {
161
186
  // ==========================================================================
162
187
 
163
188
  public async start(): Promise<void> {
189
+ this.acceptsManagedMailDnsMutations = true;
164
190
  logger.log('info', 'DnsManager: starting');
165
191
  }
166
192
 
167
193
  public async stop(): Promise<void> {
194
+ this.acceptsManagedMailDnsMutations = false;
195
+ await this.managedMailDnsMutationChain;
168
196
  this.providerClients.clear();
169
197
  this.runtimeRegistrations.clear();
170
198
  this.dnsServer = undefined;
@@ -507,7 +535,7 @@ export class DnsManager {
507
535
  return value;
508
536
  case 'MX': {
509
537
  const [priorityStr, exchange] = value.split(' ');
510
- return { priority: parseInt(priorityStr, 10), exchange };
538
+ return { preference: parseInt(priorityStr, 10), exchange };
511
539
  }
512
540
  case 'SOA': {
513
541
  const parts = value.split(' ');
@@ -760,6 +788,15 @@ export class DnsManager {
760
788
  }
761
789
 
762
790
  public async deleteProvider(id: string, force: boolean): Promise<{ success: boolean; message?: string }> {
791
+ return await this.runManagedMailDnsMutationExclusive(
792
+ async () => await this.deleteProviderUnlocked(id, force),
793
+ );
794
+ }
795
+
796
+ private async deleteProviderUnlocked(
797
+ id: string,
798
+ force: boolean,
799
+ ): Promise<{ success: boolean; message?: string }> {
763
800
  const doc = await DnsProviderDoc.findById(id);
764
801
  if (!doc) return { success: false, message: 'Provider not found' };
765
802
  const linkedDomains = await DomainDoc.findByProviderId(id);
@@ -772,7 +809,16 @@ export class DnsManager {
772
809
  // If forcing, also delete the linked domains and their records.
773
810
  if (force) {
774
811
  for (const domain of linkedDomains) {
775
- await this.deleteDomain(domain.id);
812
+ const references = await this.getManagedMailDnsReferences(domain.id);
813
+ if (references.emailDomains.length > 0 || references.managedRecordCount > 0) {
814
+ return {
815
+ success: false,
816
+ message: this.managedMailDnsReferenceMessage(domain.name, references),
817
+ };
818
+ }
819
+ }
820
+ for (const domain of linkedDomains) {
821
+ await this.deleteDomainUnlocked(domain.id);
776
822
  }
777
823
  }
778
824
  await doc.delete();
@@ -950,8 +996,18 @@ export class DnsManager {
950
996
  * domain's own recorded registrations are removed.
951
997
  */
952
998
  public async deleteDomain(id: string): Promise<boolean> {
999
+ return await this.runManagedMailDnsMutationExclusive(
1000
+ async () => await this.deleteDomainUnlocked(id),
1001
+ );
1002
+ }
1003
+
1004
+ private async deleteDomainUnlocked(id: string): Promise<boolean> {
953
1005
  const doc = await DomainDoc.findById(id);
954
1006
  if (!doc) return false;
1007
+ const references = await this.getManagedMailDnsReferences(id);
1008
+ if (references.emailDomains.length > 0 || references.managedRecordCount > 0) {
1009
+ throw new Error(this.managedMailDnsReferenceMessage(doc.name, references));
1010
+ }
955
1011
  // Tear runtime state down before the durable rows go away: the registry is
956
1012
  // the authority on what to remove, so this does not depend on the records
957
1013
  // still being readable.
@@ -972,6 +1028,28 @@ export class DnsManager {
972
1028
  return true;
973
1029
  }
974
1030
 
1031
+ private async getManagedMailDnsReferences(domainId: string): Promise<{
1032
+ emailDomains: EmailDomainDoc[];
1033
+ managedRecordCount: number;
1034
+ }> {
1035
+ const emailDomains = (await EmailDomainDoc.findAll()).filter((emailDomain) => (
1036
+ emailDomain.linkedDomainId === domainId
1037
+ || emailDomain.reconciliation?.providerZoneIds?.includes(domainId)
1038
+ || emailDomain.reconciliation?.intents.some((intent) => intent.domainId === domainId)
1039
+ ));
1040
+ const managedRecordCount = (await DnsRecordDoc.findByDomainId(domainId)).filter((record) => (
1041
+ record.managedBy === 'mail-dns-reconciler'
1042
+ )).length;
1043
+ return { emailDomains, managedRecordCount };
1044
+ }
1045
+
1046
+ private managedMailDnsReferenceMessage(
1047
+ domainName: string,
1048
+ references: { emailDomains: EmailDomainDoc[]; managedRecordCount: number },
1049
+ ): string {
1050
+ return `DNS domain ${domainName} is referenced by ${references.emailDomains.length} managed email domain(s) and ${references.managedRecordCount} managed mail DNS record(s); delete those email domains first`;
1051
+ }
1052
+
975
1053
  /**
976
1054
  * Force-resync a provider-managed domain: re-pull all records from the
977
1055
  * provider API, replacing the cached DnsRecordDocs.
@@ -992,8 +1070,12 @@ export class DnsManager {
992
1070
  return { success: false, message: 'Provider client unavailable' };
993
1071
  }
994
1072
  let providerRecords: IProviderRecord[];
1073
+ let allProviderIds: string[];
995
1074
  try {
996
- providerRecords = await client.listRecords(doc.name);
1075
+ [providerRecords, allProviderIds] = await Promise.all([
1076
+ client.listRecords(doc.name),
1077
+ client.listRecordIds(doc.name),
1078
+ ]);
997
1079
  } catch (error: unknown) {
998
1080
  return { success: false, message: `Provider list failed: ${(error as Error).message}` };
999
1081
  }
@@ -1004,6 +1086,17 @@ export class DnsManager {
1004
1086
  if (new Set(providerIds).size !== providerIds.length) {
1005
1087
  return { success: false, message: 'Provider returned duplicate provider record ids' };
1006
1088
  }
1089
+ allProviderIds = allProviderIds.map((providerId) => providerId.trim());
1090
+ if (allProviderIds.some((providerId) => !providerId)) {
1091
+ return { success: false, message: 'Provider returned a record without a non-empty provider id' };
1092
+ }
1093
+ if (new Set(allProviderIds).size !== allProviderIds.length) {
1094
+ return { success: false, message: 'Provider returned duplicate provider record ids' };
1095
+ }
1096
+ const allProviderIdSet = new Set(allProviderIds);
1097
+ if (providerIds.some((providerId) => !allProviderIdSet.has(providerId))) {
1098
+ return { success: false, message: 'Provider record listings returned inconsistent record ids' };
1099
+ }
1007
1100
 
1008
1101
  // Provider ids are the only identity proof. Tuple matching could adopt an
1009
1102
  // operator replacement and transfer reconciler ownership to the wrong row.
@@ -1064,7 +1157,7 @@ export class DnsManager {
1064
1157
  return {
1065
1158
  success: true,
1066
1159
  recordCount: providerRecords.length,
1067
- listedProviderRecordIds: providerIds,
1160
+ listedProviderRecordIds: allProviderIds,
1068
1161
  };
1069
1162
  }
1070
1163
 
@@ -1094,20 +1187,29 @@ export class DnsManager {
1094
1187
  targetProviderId?: string;
1095
1188
  deleteExistingProviderRecords?: boolean;
1096
1189
  }): Promise<{ success: boolean; recordsMigrated?: number; message?: string }> {
1097
- const domain = await DomainDoc.findById(args.id);
1098
- if (!domain) return { success: false, message: 'Domain not found' };
1190
+ return await this.runManagedMailDnsMutationExclusive(async () => {
1191
+ const domain = await DomainDoc.findById(args.id);
1192
+ if (!domain) return { success: false, message: 'Domain not found' };
1099
1193
 
1100
- if (domain.source === args.targetSource && domain.providerId === args.targetProviderId) {
1101
- return { success: false, message: 'Domain is already in the target configuration' };
1102
- }
1103
-
1104
- const records = await DnsRecordDoc.findByDomainId(domain.id);
1194
+ const references = await this.getManagedMailDnsReferences(domain.id);
1195
+ if (references.emailDomains.length > 0 || references.managedRecordCount > 0) {
1196
+ return { success: false, message: this.managedMailDnsReferenceMessage(domain.name, references) };
1197
+ }
1198
+ if (domain.source === args.targetSource && domain.providerId === args.targetProviderId) {
1199
+ return { success: false, message: 'Domain is already in the target configuration' };
1200
+ }
1105
1201
 
1106
- if (args.targetSource === 'provider') {
1107
- return this.migrateToDnsProvider(domain, records, args.targetProviderId!, args.deleteExistingProviderRecords ?? false);
1108
- } else {
1109
- return this.migrateToDcrouter(domain, records);
1110
- }
1202
+ const records = await DnsRecordDoc.findByDomainId(domain.id);
1203
+ if (args.targetSource === 'provider') {
1204
+ return await this.migrateToDnsProvider(
1205
+ domain,
1206
+ records,
1207
+ args.targetProviderId!,
1208
+ args.deleteExistingProviderRecords ?? false,
1209
+ );
1210
+ }
1211
+ return await this.migrateToDcrouter(domain, records);
1212
+ });
1111
1213
  }
1112
1214
 
1113
1215
  /**
@@ -1345,6 +1447,7 @@ export class DnsManager {
1345
1447
  public async updateRecord(args: {
1346
1448
  id: string;
1347
1449
  name?: string;
1450
+ type?: TDnsRecordType;
1348
1451
  value?: string;
1349
1452
  ttl?: number;
1350
1453
  proxied?: boolean;
@@ -1357,6 +1460,7 @@ export class DnsManager {
1357
1460
  const previousName = doc.name;
1358
1461
  const previousType = doc.type;
1359
1462
  if (args.name !== undefined) doc.name = args.name.toLowerCase();
1463
+ if (args.type !== undefined) doc.type = args.type;
1360
1464
  if (args.value !== undefined) doc.value = args.value;
1361
1465
  if (args.ttl !== undefined) doc.ttl = args.ttl;
1362
1466
  if (args.proxied !== undefined) doc.proxied = args.proxied;
@@ -1384,7 +1488,7 @@ export class DnsManager {
1384
1488
  await doc.save();
1385
1489
  if (domain.source === 'dcrouter') {
1386
1490
  await this.refreshLocalRrset(doc.domainId, previousName, previousType);
1387
- if (previousName.toLowerCase() !== doc.name.toLowerCase()) {
1491
+ if (previousName.toLowerCase() !== doc.name.toLowerCase() || previousType !== doc.type) {
1388
1492
  await this.refreshLocalRrset(doc.domainId, doc.name, doc.type);
1389
1493
  }
1390
1494
  }
@@ -1392,30 +1496,126 @@ export class DnsManager {
1392
1496
  }
1393
1497
 
1394
1498
  public async deleteRecord(id: string): Promise<{ success: boolean; message?: string }> {
1395
- const doc = await DnsRecordDoc.findById(id);
1396
- if (!doc) return { success: false, message: 'Record not found' };
1397
- const domain = await DomainDoc.findById(doc.domainId);
1398
- if (!domain) return { success: false, message: 'Parent domain not found' };
1499
+ const [result] = await this.deleteRecords([id]);
1500
+ return result.message
1501
+ ? { success: result.success, message: result.message }
1502
+ : { success: result.success };
1503
+ }
1399
1504
 
1400
- if (domain.source === 'provider') {
1401
- if (!domain.providerId || !doc.providerRecordId) {
1402
- return { success: false, message: 'Provider record metadata missing' };
1505
+ public async deleteRecords(ids: string[]): Promise<IDnsRecordDeleteResult[]> {
1506
+ const results = new Map<string, IDnsRecordDeleteResult>();
1507
+ const providerGroups = new Map<string, {
1508
+ domain: DomainDoc;
1509
+ client: IDnsProviderClient;
1510
+ records: DnsRecordDoc[];
1511
+ }>();
1512
+
1513
+ for (const id of [...new Set(ids)]) {
1514
+ const doc = await DnsRecordDoc.findById(id);
1515
+ if (!doc) {
1516
+ results.set(id, { id, success: false, message: 'Record not found' });
1517
+ continue;
1403
1518
  }
1404
- const client = await this.getProviderClientById(domain.providerId);
1405
- if (!client) {
1406
- return { success: false, message: 'Provider client unavailable' };
1519
+ const domain = await DomainDoc.findById(doc.domainId);
1520
+ if (!domain) {
1521
+ results.set(id, { id, success: false, message: 'Parent domain not found' });
1522
+ continue;
1407
1523
  }
1408
- try {
1409
- await client.deleteRecord(domain.name, doc.providerRecordId);
1410
- } catch (err: unknown) {
1411
- return { success: false, message: `Provider rejected delete: ${(err as Error).message}` };
1524
+ if (domain.source !== 'provider') {
1525
+ await doc.delete();
1526
+ await this.refreshLocalRrset(doc.domainId, doc.name, doc.type);
1527
+ results.set(id, { id, success: true });
1528
+ continue;
1529
+ }
1530
+ if (!domain.providerId || !doc.providerRecordId?.trim()) {
1531
+ results.set(id, { id, success: false, message: 'Provider record metadata missing' });
1532
+ continue;
1533
+ }
1534
+ let group = providerGroups.get(domain.id);
1535
+ if (!group) {
1536
+ const client = await this.getProviderClientById(domain.providerId);
1537
+ if (!client) {
1538
+ results.set(id, { id, success: false, message: 'Provider client unavailable' });
1539
+ continue;
1540
+ }
1541
+ group = { domain, client, records: [] };
1542
+ providerGroups.set(domain.id, group);
1412
1543
  }
1544
+ group.records.push(doc);
1413
1545
  }
1414
- await doc.delete();
1415
- if (domain.source === 'dcrouter') {
1416
- await this.refreshLocalRrset(doc.domainId, doc.name, doc.type);
1546
+
1547
+ for (const { domain, client, records } of providerGroups.values()) {
1548
+ const deleteErrors = new Map<string, Error>();
1549
+ for (const record of records) {
1550
+ try {
1551
+ await client.deleteRecord(domain.name, record.providerRecordId!);
1552
+ } catch (error: unknown) {
1553
+ deleteErrors.set(record.id, error instanceof Error ? error : new Error(String(error)));
1554
+ }
1555
+ }
1556
+
1557
+ let providerRecordIds: string[] | undefined;
1558
+ let providerListError: Error | undefined;
1559
+ try {
1560
+ providerRecordIds = (await client.listRecordIds(domain.name)).map((providerRecordId) => (
1561
+ providerRecordId.trim()
1562
+ ));
1563
+ } catch (error: unknown) {
1564
+ providerListError = error instanceof Error ? error : new Error(String(error));
1565
+ }
1566
+ const invalidListingMessage = providerRecordIds?.some((providerRecordId) => !providerRecordId)
1567
+ ? 'Provider deletion could not be confirmed: provider returned an empty record id'
1568
+ : providerRecordIds && new Set(providerRecordIds).size !== providerRecordIds.length
1569
+ ? 'Provider deletion could not be confirmed: provider returned duplicate record ids'
1570
+ : undefined;
1571
+ const listedIds = providerRecordIds && !invalidListingMessage
1572
+ ? new Set(providerRecordIds)
1573
+ : undefined;
1574
+ for (const record of records) {
1575
+ const providerRecordId = record.providerRecordId!.trim();
1576
+ const deleteError = deleteErrors.get(record.id);
1577
+ if (deleteError && this.isProviderRecordNotFoundError(deleteError)) {
1578
+ await record.delete();
1579
+ results.set(record.id, { id: record.id, success: true });
1580
+ continue;
1581
+ }
1582
+ if (providerListError) {
1583
+ results.set(record.id, {
1584
+ id: record.id,
1585
+ success: false,
1586
+ message: `Provider deletion could not be confirmed: ${providerListError.message}`,
1587
+ });
1588
+ continue;
1589
+ }
1590
+ if (invalidListingMessage || !listedIds) {
1591
+ results.set(record.id, {
1592
+ id: record.id,
1593
+ success: false,
1594
+ message: invalidListingMessage || 'Provider deletion could not be confirmed',
1595
+ });
1596
+ continue;
1597
+ }
1598
+ if (listedIds.has(providerRecordId)) {
1599
+ results.set(record.id, {
1600
+ id: record.id,
1601
+ success: false,
1602
+ message: deleteError
1603
+ ? `Provider rejected delete and still lists the record: ${deleteError.message}`
1604
+ : 'Provider accepted delete but still lists the record',
1605
+ });
1606
+ continue;
1607
+ }
1608
+ await record.delete();
1609
+ results.set(record.id, { id: record.id, success: true });
1610
+ }
1417
1611
  }
1418
- return { success: true };
1612
+
1613
+ return ids.map((id) => results.get(id) || { id, success: false, message: 'Record deletion was not evaluated' });
1614
+ }
1615
+
1616
+ private isProviderRecordNotFoundError(error: Error): boolean {
1617
+ const providerError = error as Error & { status?: unknown; statusCode?: unknown };
1618
+ return providerError.status === 404 || providerError.statusCode === 404;
1419
1619
  }
1420
1620
 
1421
1621
  /**