@serve.zone/dcrouter 16.4.1 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/deno.json +1 -1
  2. package/dist_serve/bundle.js +852 -852
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/classes.dcrouter.d.ts +5 -1
  5. package/dist_ts/classes.dcrouter.js +42 -9
  6. package/dist_ts/db/documents/classes.dns-record.doc.d.ts +4 -0
  7. package/dist_ts/db/documents/classes.dns-record.doc.js +33 -2
  8. package/dist_ts/db/documents/classes.email-domain.doc.d.ts +5 -1
  9. package/dist_ts/db/documents/classes.email-domain.doc.js +26 -2
  10. package/dist_ts/db/documents/classes.smartmta-storage.doc.d.ts +8 -0
  11. package/dist_ts/db/documents/classes.smartmta-storage.doc.js +91 -0
  12. package/dist_ts/db/documents/index.d.ts +1 -0
  13. package/dist_ts/db/documents/index.js +2 -1
  14. package/dist_ts/dns/manager.dns.d.ts +6 -0
  15. package/dist_ts/dns/manager.dns.js +81 -32
  16. package/dist_ts/dns/providers/cloudflare.provider.d.ts +2 -0
  17. package/dist_ts/dns/providers/cloudflare.provider.js +45 -28
  18. package/dist_ts/email/classes.email-domain.manager.d.ts +43 -38
  19. package/dist_ts/email/classes.email-domain.manager.js +391 -324
  20. package/dist_ts/email/classes.email-settings.manager.js +2 -1
  21. package/dist_ts/email/classes.mail-dns-sync.d.ts +58 -17
  22. package/dist_ts/email/classes.mail-dns-sync.js +857 -141
  23. package/dist_ts/email/classes.mail-edge-eligibility.d.ts +55 -0
  24. package/dist_ts/email/classes.mail-edge-eligibility.js +168 -0
  25. package/dist_ts/email/classes.mail-egress-coordinator.d.ts +3 -1
  26. package/dist_ts/email/classes.mail-egress-coordinator.js +43 -22
  27. package/dist_ts/email/classes.smartmta-blob-storage-manager.d.ts +27 -0
  28. package/dist_ts/email/classes.smartmta-blob-storage-manager.js +109 -0
  29. package/dist_ts/email/classes.smartmta-storage-manager.d.ts +6 -8
  30. package/dist_ts/email/classes.smartmta-storage-manager.js +63 -86
  31. package/dist_ts/email/classes.workapp-mail-manager.d.ts +3 -0
  32. package/dist_ts/email/classes.workapp-mail-manager.js +63 -14
  33. package/dist_ts/email/index.d.ts +3 -0
  34. package/dist_ts/email/index.js +4 -1
  35. package/dist_ts/email/smartmta-storage-policy.d.ts +9 -0
  36. package/dist_ts/email/smartmta-storage-policy.js +58 -0
  37. package/dist_ts/opsserver/handlers/email-domain.handler.d.ts +1 -0
  38. package/dist_ts/opsserver/handlers/email-domain.handler.js +138 -22
  39. package/dist_ts/opsserver/handlers/stats.handler.js +3 -5
  40. package/dist_ts/plugins.d.ts +2 -1
  41. package/dist_ts/plugins.js +3 -2
  42. package/dist_ts/remoteingress/classes.hub-lifecycle.js +2 -1
  43. package/dist_ts/remoteingress/classes.tunnel-manager.d.ts +6 -0
  44. package/dist_ts/remoteingress/classes.tunnel-manager.js +40 -2
  45. package/dist_ts_interfaces/data/dns-record.d.ts +6 -0
  46. package/dist_ts_interfaces/data/email-domain.d.ts +81 -0
  47. package/dist_ts_interfaces/requests/email-domains.d.ts +25 -17
  48. package/dist_ts_migrations/index.d.ts +2 -0
  49. package/dist_ts_migrations/index.js +441 -16
  50. package/dist_ts_web/00_commitinfo_data.js +1 -1
  51. package/dist_ts_web/appstate/email-domains.d.ts +16 -0
  52. package/dist_ts_web/appstate/email-domains.js +140 -19
  53. package/dist_ts_web/elements/email/ops-view-email-domains.d.ts +1 -0
  54. package/dist_ts_web/elements/email/ops-view-email-domains.js +36 -13
  55. package/package.json +4 -3
  56. package/readme.md +13 -1
  57. package/ts/00_commitinfo_data.ts +1 -1
  58. package/ts/classes.dcrouter.ts +55 -11
  59. package/ts/db/documents/classes.dns-record.doc.ts +20 -0
  60. package/ts/db/documents/classes.email-domain.doc.ts +14 -0
  61. package/ts/db/documents/classes.smartmta-storage.doc.ts +25 -0
  62. package/ts/db/documents/index.ts +1 -0
  63. package/ts/dns/manager.dns.ts +89 -29
  64. package/ts/dns/providers/cloudflare.provider.ts +46 -27
  65. package/ts/email/classes.email-domain.manager.ts +416 -368
  66. package/ts/email/classes.email-settings.manager.ts +1 -0
  67. package/ts/email/classes.mail-dns-sync.ts +913 -146
  68. package/ts/email/classes.mail-edge-eligibility.ts +205 -0
  69. package/ts/email/classes.mail-egress-coordinator.ts +46 -23
  70. package/ts/email/classes.smartmta-blob-storage-manager.ts +138 -0
  71. package/ts/email/classes.smartmta-storage-manager.ts +86 -83
  72. package/ts/email/classes.workapp-mail-manager.ts +82 -16
  73. package/ts/email/index.ts +3 -0
  74. package/ts/email/smartmta-storage-policy.ts +73 -0
  75. package/ts/opsserver/handlers/email-domain.handler.ts +154 -21
  76. package/ts/opsserver/handlers/stats.handler.ts +2 -4
  77. package/ts/plugins.ts +2 -1
  78. package/ts/remoteingress/classes.hub-lifecycle.ts +1 -0
  79. package/ts/remoteingress/classes.tunnel-manager.ts +53 -7
  80. package/ts_web/00_commitinfo_data.ts +1 -1
  81. package/ts_web/appstate/email-domains.ts +187 -18
  82. package/ts_web/elements/email/ops-view-email-domains.ts +44 -12
@@ -1,196 +1,912 @@
1
+ import * as plugins from '../plugins.js';
1
2
  import { logger } from '../logger.js';
2
3
  import { DnsRecordDoc } from '../db/documents/classes.dns-record.doc.js';
3
4
  import { DomainDoc } from '../db/documents/classes.domain.doc.js';
4
5
  import { EmailDomainDoc } from '../db/documents/classes.email-domain.doc.js';
5
- export const MAIL_DNS_SYNC_CREATED_BY = 'mail-edge-sync';
6
+ import { MailEdgeEligibility, ReleasedRemoteIngressEgressIdentitySource, } from './classes.mail-edge-eligibility.js';
7
+ export const MAIL_DNS_SYNC_CREATED_BY = 'mail-dns-reconciler';
8
+ export const MAIL_DNS_MANAGED_BY = 'mail-dns-reconciler';
9
+ export const MAIL_DNS_RETRY_INTERVAL_MS = 5 * 60_000;
10
+ function normalizeHostname(value) {
11
+ return value.trim().toLowerCase().replace(/\.$/, '');
12
+ }
13
+ function normalizeMx(value) {
14
+ const [priority, exchange = ''] = value.trim().split(/\s+/);
15
+ return `${Number(priority)} ${normalizeHostname(exchange)}`;
16
+ }
17
+ function expandIpv6(address) {
18
+ let normalized = address.toLowerCase();
19
+ if (normalized.includes('.')) {
20
+ const lastColon = normalized.lastIndexOf(':');
21
+ const ipv4 = normalized.slice(lastColon + 1).split('.').map(Number);
22
+ if (ipv4.length !== 4 || ipv4.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
23
+ return undefined;
24
+ }
25
+ normalized = `${normalized.slice(0, lastColon)}:${((ipv4[0] << 8) | ipv4[1]).toString(16)}:${((ipv4[2] << 8) | ipv4[3]).toString(16)}`;
26
+ }
27
+ const halves = normalized.split('::');
28
+ if (halves.length > 2)
29
+ return undefined;
30
+ const left = halves[0] ? halves[0].split(':') : [];
31
+ const right = halves[1] ? halves[1].split(':') : [];
32
+ const missing = 8 - left.length - right.length;
33
+ if ((halves.length === 1 && missing !== 0) || missing < 0)
34
+ return undefined;
35
+ const groups = [...left, ...Array.from({ length: missing }, () => '0'), ...right];
36
+ if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/.test(group)))
37
+ return undefined;
38
+ return groups.map((group) => group.padStart(4, '0'));
39
+ }
40
+ export function toReverseDnsName(address) {
41
+ const family = plugins.smartnetwork.getIpVersion(address);
42
+ if (family === 4) {
43
+ return `${address.split('.').reverse().join('.')}.in-addr.arpa`;
44
+ }
45
+ if (family === 6) {
46
+ const groups = expandIpv6(address);
47
+ if (!groups)
48
+ throw new Error(`Invalid IPv6 address: ${address}`);
49
+ return `${groups.join('').split('').reverse().join('.')}.ip6.arpa`;
50
+ }
51
+ throw new Error(`Invalid IP address: ${address}`);
52
+ }
53
+ export class SmartDnsMailVerifier {
54
+ client;
55
+ getClient() {
56
+ this.client ||= new plugins.smartdns.dnsClientMod.Smartdns({});
57
+ return this.client;
58
+ }
59
+ async verifyRecord(intent) {
60
+ try {
61
+ const records = await this.getClient().getRecords(intent.name, intent.type);
62
+ const values = (records || []).map((record) => String(record.value).trim());
63
+ if (intent.type === 'MX') {
64
+ return values.some((value) => normalizeMx(value) === normalizeMx(intent.value))
65
+ ? { valid: true }
66
+ : { valid: false, reason: `MX ${intent.value} has not propagated` };
67
+ }
68
+ if (intent.type === 'TXT') {
69
+ const exact = values.filter((value) => value === intent.value.trim());
70
+ const relevant = intent.value.startsWith('v=spf1')
71
+ ? values.filter((value) => value.toLowerCase().startsWith('v=spf1'))
72
+ : intent.value.startsWith('v=DMARC1')
73
+ ? values.filter((value) => value.toLowerCase().startsWith('v=dmarc1'))
74
+ : values;
75
+ return exact.length === 1 && relevant.length === 1
76
+ ? { valid: true }
77
+ : { valid: false, reason: `TXT ${intent.name} is missing, conflicting, or duplicated` };
78
+ }
79
+ const exact = values.filter((value) => normalizeHostname(value) === normalizeHostname(intent.value));
80
+ return exact.length === 1 && values.length === 1
81
+ ? { valid: true }
82
+ : { valid: false, reason: `${intent.type} ${intent.name} is missing, conflicting, or duplicated` };
83
+ }
84
+ catch (error) {
85
+ return { valid: false, reason: `DNS lookup failed: ${error.message}` };
86
+ }
87
+ }
88
+ async verifyMxSet(intents) {
89
+ if (intents.length === 0)
90
+ return { valid: false, reason: 'No desired MX records' };
91
+ try {
92
+ const records = await this.getClient().getRecords(intents[0].name, 'MX');
93
+ const actual = (records || []).map((record) => normalizeMx(String(record.value))).sort();
94
+ const desired = intents.map((intent) => normalizeMx(intent.value)).sort();
95
+ return actual.length === desired.length && actual.every((value, index) => value === desired[index])
96
+ ? { valid: true }
97
+ : {
98
+ valid: false,
99
+ reason: `MX set for ${intents[0].name} does not exactly match priorities and exchanges`,
100
+ };
101
+ }
102
+ catch (error) {
103
+ return { valid: false, reason: `MX lookup failed: ${error.message}` };
104
+ }
105
+ }
106
+ async verifyForwardConfirmedIdentity(identity) {
107
+ const checks = [];
108
+ if (identity.ipv4)
109
+ checks.push({ family: 'A', address: identity.ipv4 });
110
+ if (identity.ipv6)
111
+ checks.push({ family: 'AAAA', address: identity.ipv6 });
112
+ try {
113
+ for (const check of checks) {
114
+ const ptrRecords = await this.getClient().getRecords(toReverseDnsName(check.address), 'PTR');
115
+ const ptrNames = (ptrRecords || []).map((record) => normalizeHostname(String(record.value)));
116
+ if (ptrNames.length !== 1 || ptrNames[0] !== normalizeHostname(identity.hostname)) {
117
+ return {
118
+ valid: false,
119
+ reason: `PTR for ${check.address} must be exactly ${identity.hostname}`,
120
+ };
121
+ }
122
+ const forwardRecords = await this.getClient().getRecords(identity.hostname, check.family);
123
+ const forwardIps = (forwardRecords || []).map((record) => String(record.value));
124
+ if (!forwardIps.includes(check.address)) {
125
+ return {
126
+ valid: false,
127
+ reason: `${check.family} ${identity.hostname} does not resolve back to ${check.address}`,
128
+ };
129
+ }
130
+ }
131
+ return checks.length > 0
132
+ ? { valid: true }
133
+ : { valid: false, reason: `No source-bound address is available for ${identity.edgeId}` };
134
+ }
135
+ catch (error) {
136
+ return { valid: false, reason: `FCrDNS lookup failed: ${error.message}` };
137
+ }
138
+ }
139
+ destroy() {
140
+ this.client?.destroy();
141
+ this.client = undefined;
142
+ }
143
+ }
6
144
  /**
7
- * Reconciles the mail-facing A/AAAA records that make edges — never the hub —
8
- * the public identity of the mail platform:
9
- * (a) shared mail hostname → publicIp/publicIpV6 of every enabled mail edge,
10
- * (b) each edge's mailHostname → that edge's IPs (FCrDNS for outbound EHLO),
11
- * (c) mx.<domain> → the pinned edges' IPs for every pinned email domain.
12
- * Records are DB-backed DnsRecordDoc rows owned via createdBy 'mail-edge-sync';
13
- * provider-hosted zones (e.g. Cloudflare) are written through DnsManager's
14
- * provider path, dcrouter-authoritative zones serve immediately.
145
+ * Sole owner of managed mail DNS. API, topology, timer, and startup events only
146
+ * mark this globally serialized reconciler dirty.
15
147
  */
16
148
  export class MailDnsSync {
17
149
  dcRouterRef;
18
- syncInFlight;
19
- resyncQueued = false;
20
- constructor(dcRouterRef) {
150
+ verifier;
151
+ started = false;
152
+ stopped = false;
153
+ dirty = false;
154
+ runPromise;
155
+ retryTimer;
156
+ edgeEligibility;
157
+ constructor(dcRouterRef, edgeEligibility, verifier = new SmartDnsMailVerifier()) {
21
158
  this.dcRouterRef = dcRouterRef;
159
+ this.verifier = verifier;
160
+ this.edgeEligibility = edgeEligibility
161
+ || dcRouterRef.mailEdgeEligibility
162
+ || new MailEdgeEligibility(dcRouterRef, new ReleasedRemoteIngressEgressIdentitySource(dcRouterRef));
163
+ }
164
+ async start() {
165
+ if (this.started) {
166
+ await this.runPromise;
167
+ return;
168
+ }
169
+ this.started = true;
170
+ this.stopped = false;
171
+ this.retryTimer = setInterval(() => this.requestSync('periodic retry'), MAIL_DNS_RETRY_INTERVAL_MS);
172
+ this.dirty = true;
173
+ try {
174
+ await this.kick('startup');
175
+ }
176
+ catch (error) {
177
+ if (this.retryTimer)
178
+ clearInterval(this.retryTimer);
179
+ this.retryTimer = undefined;
180
+ this.started = false;
181
+ this.dirty = false;
182
+ throw error;
183
+ }
184
+ }
185
+ async stop() {
186
+ this.stopped = true;
187
+ this.started = false;
188
+ this.dirty = false;
189
+ if (this.retryTimer) {
190
+ clearInterval(this.retryTimer);
191
+ this.retryTimer = undefined;
192
+ }
193
+ await this.runPromise?.catch(() => undefined);
194
+ this.verifier.destroy?.();
22
195
  }
23
- /** Fire-and-forget trigger for CRUD hooks; failures are logged, not thrown. */
24
196
  requestSync(reason) {
25
- this.sync(reason).catch((err) => {
26
- logger.log('error', `MailDnsSync failed (${reason}): ${err.message}`);
197
+ if (this.stopped)
198
+ return;
199
+ this.dirty = true;
200
+ if (!this.started)
201
+ return;
202
+ this.kick(reason).catch((error) => {
203
+ logger.log('error', `MailDnsSync failed (${reason}): ${error.message}`);
27
204
  });
28
205
  }
29
- /** Coalescing entry point: concurrent triggers fold into one follow-up pass. */
206
+ /** Coalescing entry point used by API actions that need the durable outcome. */
30
207
  async sync(reason) {
31
- if (this.syncInFlight) {
32
- this.resyncQueued = true;
33
- return this.syncInFlight;
34
- }
35
- this.syncInFlight = this.reconcile(reason).finally(() => {
36
- this.syncInFlight = undefined;
37
- if (this.resyncQueued) {
38
- this.resyncQueued = false;
39
- this.requestSync(`${reason} (coalesced)`);
208
+ if (this.stopped)
209
+ throw new Error('MailDnsSync is stopped');
210
+ this.dirty = true;
211
+ await this.kick(reason);
212
+ }
213
+ async kick(reason) {
214
+ if (this.runPromise)
215
+ return await this.runPromise;
216
+ // Defer execution one microtask so runPromise is assigned before the first
217
+ // awaited provider/eligibility hook can synchronously enqueue another event.
218
+ this.runPromise = Promise.resolve().then(() => this.runLoop(reason, false)).finally(() => {
219
+ this.runPromise = undefined;
220
+ if (this.dirty && this.started && !this.stopped) {
221
+ this.requestSync(`${reason} (follow-up)`);
40
222
  }
41
223
  });
42
- return this.syncInFlight;
224
+ return await this.runPromise;
225
+ }
226
+ async runLoop(reason, singlePass) {
227
+ do {
228
+ this.dirty = false;
229
+ await this.reconcile(reason);
230
+ if (singlePass)
231
+ break;
232
+ // An event during the run leaves dirty=true and forces exactly one
233
+ // immediate follow-up; repeated events coalesce into that pass.
234
+ } while (this.dirty && !this.stopped);
43
235
  }
44
236
  async reconcile(reason) {
45
- if (this.dcRouterRef.options.dbConfig?.enabled === false) {
237
+ if (this.dcRouterRef.options.dbConfig?.enabled === false)
46
238
  return;
47
- }
48
239
  const dnsManager = this.dcRouterRef.dnsManager;
49
- const remoteIngressManager = this.dcRouterRef.remoteIngressManager;
50
- if (!dnsManager || !remoteIngressManager) {
51
- // Without both managers the edge registry / record store is not
52
- // authoritative — skip entirely rather than delete managed records.
240
+ if (!dnsManager) {
241
+ logger.log('warn', `MailDnsSync (${reason}): DnsManager unavailable; retry intent retained`);
53
242
  return;
54
243
  }
55
- const desired = await this.computeDesiredRecords();
244
+ // Refresh the broad operational status independently from per-domain pins.
245
+ await this.edgeEligibility.resolveForDomain();
56
246
  const zones = await DomainDoc.findAll();
57
- // Place each desired record in its zone (longest-suffix match). Records
58
- // without a managed zone are reported — the operator must onboard the zone.
59
- const placed = new Map();
60
- const unplacedNames = new Set();
61
- for (const record of desired) {
62
- const zone = this.findZoneForName(record.name, zones);
63
- if (!zone) {
64
- unplacedNames.add(record.name);
247
+ const docs = await EmailDomainDoc.findAll();
248
+ const plans = [];
249
+ for (const loadedDoc of docs) {
250
+ const doc = await this.dcRouterRef.emailDomainManager?.stageDkimRotationIfDue?.(loadedDoc.id)
251
+ || loadedDoc;
252
+ if (doc.reconciliation?.lifecycleStatus === 'deleting') {
253
+ await this.persistDeletingAttempt(doc);
65
254
  continue;
66
255
  }
67
- placed.set(`${zone.id}|${record.name}|${record.type}|${record.value}`, {
68
- domainId: zone.id,
69
- record,
70
- });
256
+ const plan = await this.buildPlan(doc, zones);
257
+ await this.persistDesiredPlan(plan);
258
+ plans.push(plan);
71
259
  }
72
- for (const name of unplacedNames) {
73
- logger.log('warn', `MailDnsSync: no managed DNS zone covers ${name} — record skipped`);
74
- }
75
- const existing = (await DnsRecordDoc.findAll())
76
- .filter((record) => record.createdBy === MAIL_DNS_SYNC_CREATED_BY);
77
- let created = 0;
78
- let deleted = 0;
79
- for (const record of existing) {
80
- const key = `${record.domainId}|${record.name}|${record.type}|${record.value}`;
81
- if (placed.has(key)) {
82
- placed.delete(key); // already present — nothing to create
83
- continue;
260
+ const uniqueIntents = new Map();
261
+ for (const plan of plans) {
262
+ for (const intent of plan.intents) {
263
+ uniqueIntents.set(intent.key, intent);
84
264
  }
85
- const result = await dnsManager.deleteRecord(record.id);
86
- if (result.success) {
87
- deleted++;
88
- }
89
- else {
90
- logger.log('warn', `MailDnsSync: failed to delete stale ${record.type} ${record.name} → ${record.value}: ${result.message}`);
265
+ }
266
+ const zoneErrors = await this.reconcileZones([...uniqueIntents.values()], zones);
267
+ for (const plan of plans) {
268
+ const errors = [...plan.preliminaryErrors];
269
+ for (const intent of plan.intents) {
270
+ const outcome = uniqueIntents.get(intent.key);
271
+ if (outcome)
272
+ Object.assign(intent, outcome);
273
+ if (intent.error)
274
+ errors.push(intent.error);
91
275
  }
276
+ await this.validateAndActivate(plan, errors, zoneErrors);
92
277
  }
93
- for (const { domainId, record } of placed.values()) {
94
- const result = await dnsManager.createRecord({
95
- domainId,
96
- name: record.name,
97
- type: record.type,
98
- value: record.value,
99
- // Short TTL: edge IPs are burnable identities and failover heals via DNS.
100
- ttl: 300,
101
- createdBy: MAIL_DNS_SYNC_CREATED_BY,
278
+ await this.cleanupStaleManagedRecords(zones);
279
+ await this.finalizeDeletingDomains();
280
+ await this.dcRouterRef.emailDomainManager?.syncManagedDomainsToRuntime();
281
+ await this.dcRouterRef.workAppMailManager?.applyStoredIdentitiesToRuntime();
282
+ }
283
+ async buildPlan(doc, zones) {
284
+ const now = new Date().toISOString();
285
+ const eligibility = await this.edgeEligibility.resolveForDomain(doc);
286
+ const preliminaryErrors = [
287
+ ...(doc.reconciliation?.errors || []).filter((error) => error.code === 'DKIM_ROTATION_STAGE_FAILED'),
288
+ ];
289
+ if (!eligibility.supported) {
290
+ preliminaryErrors.push({
291
+ code: 'EGRESS_IDENTITY_PROOF_UNAVAILABLE',
292
+ message: eligibility.reason || 'RemoteIngress egress identity proof is unavailable',
293
+ retryable: true,
102
294
  });
103
- if (result.success) {
104
- created++;
105
- }
106
- else {
107
- logger.log('warn', `MailDnsSync: failed to create ${record.type} ${record.name} → ${record.value}: ${result.message}`);
295
+ }
296
+ else if (eligibility.edges.length === 0) {
297
+ preliminaryErrors.push({
298
+ code: 'NO_ELIGIBLE_MAIL_EDGE',
299
+ message: eligibility.reason || 'No eligible mail edge is connected',
300
+ retryable: true,
301
+ });
302
+ }
303
+ const activeDkim = this.getActiveDkim(doc);
304
+ const upstreamSelectorCapability = this.dcRouterRef.emailDomainManager?.supportsSelectorCorrectSigning?.() === true;
305
+ const selectorCorrectSigningSupported = ((!activeDkim || activeDkim.selector === 'default') && !doc.pendingDkim) || upstreamSelectorCapability;
306
+ if (!activeDkim) {
307
+ preliminaryErrors.push({
308
+ code: 'DKIM_KEY_UNAVAILABLE',
309
+ message: `No usable active DKIM key exists for ${doc.domain}`,
310
+ retryable: false,
311
+ });
312
+ }
313
+ else if (!selectorCorrectSigningSupported) {
314
+ preliminaryErrors.push({
315
+ code: 'SMARTMTA_SELECTOR_SIGNING_UNSUPPORTED',
316
+ message: 'The consumed SmartMTA release cannot safely sign with a non-default selector',
317
+ retryable: false,
318
+ });
319
+ }
320
+ const edgeIdentities = eligibility.edges.map((candidate) => this.toDomainEdgeIdentity(candidate));
321
+ const mxSlots = this.allocateMxSlots(doc, eligibility.edges);
322
+ if (eligibility.edges.length >= 2 && mxSlots.length < 2) {
323
+ preliminaryErrors.push({
324
+ code: 'INSUFFICIENT_DISTINCT_MX_IDENTITIES',
325
+ message: 'At least two eligible mail edges exist but they do not provide two distinct MX hostnames',
326
+ retryable: false,
327
+ });
328
+ }
329
+ const revisionId = plugins.smartunique.shortId();
330
+ const intents = [];
331
+ for (const candidate of eligibility.edges) {
332
+ for (const identity of candidate.identities) {
333
+ const type = identity.family === 4 ? 'A' : 'AAAA';
334
+ intents.push(this.intentForName(zones, {
335
+ key: `mail-topology:${candidate.edge.id}:${type}:${candidate.hostname}:${identity.address}`,
336
+ ownerType: 'topology',
337
+ ownerId: `topology:${candidate.edge.id}`,
338
+ name: candidate.hostname,
339
+ type,
340
+ value: identity.address,
341
+ ttl: 300,
342
+ }));
108
343
  }
109
344
  }
110
- if (created > 0 || deleted > 0) {
111
- logger.log('info', `MailDnsSync (${reason}): ${created} record(s) created, ${deleted} deleted`);
345
+ if (eligibility.edges.length > 0) {
346
+ for (const slot of mxSlots) {
347
+ intents.push(this.intentForName(zones, {
348
+ key: `mail-domain:${doc.id}:mx:${slot.priority}:${slot.edgeId}:${slot.hostname}`,
349
+ ownerType: 'domain',
350
+ ownerId: `domain:${doc.id}`,
351
+ name: doc.domain,
352
+ type: 'MX',
353
+ value: `${slot.priority} ${slot.hostname}`,
354
+ ttl: 300,
355
+ }));
356
+ }
357
+ const spfIdentities = [
358
+ ...edgeIdentities,
359
+ ...(doc.reconciliation?.activeRevision?.edgeIdentities || []),
360
+ ];
361
+ intents.push(this.intentForName(zones, {
362
+ key: `mail-domain:${doc.id}:spf`,
363
+ ownerType: 'domain',
364
+ ownerId: `domain:${doc.id}`,
365
+ name: doc.domain,
366
+ type: 'TXT',
367
+ value: this.buildSpfValue(spfIdentities),
368
+ ttl: 300,
369
+ }));
112
370
  }
113
- }
114
- async computeDesiredRecords() {
115
- const remoteIngressManager = this.dcRouterRef.remoteIngressManager;
116
- const desired = [];
117
- const pushEdgeIps = (name, edge) => {
118
- if (!name)
119
- return;
120
- const normalizedName = name.toLowerCase();
121
- if (edge.publicIp) {
122
- desired.push({ name: normalizedName, type: 'A', value: edge.publicIp });
371
+ if (activeDkim)
372
+ intents.push(this.buildDkimIntent(doc, activeDkim, zones));
373
+ if (doc.pendingDkim)
374
+ intents.push(this.buildDkimIntent(doc, doc.pendingDkim, zones));
375
+ for (const retiring of doc.retiringDkim || []) {
376
+ if (!retiring.retireAfter || Date.parse(retiring.retireAfter) > Date.now()) {
377
+ intents.push(this.buildDkimIntent(doc, retiring, zones));
123
378
  }
124
- if (edge.publicIpV6) {
125
- desired.push({ name: normalizedName, type: 'AAAA', value: edge.publicIpV6 });
379
+ }
380
+ intents.push(this.intentForName(zones, {
381
+ key: `mail-domain:${doc.id}:dmarc`,
382
+ ownerType: 'domain',
383
+ ownerId: `domain:${doc.id}`,
384
+ name: `_dmarc.${doc.domain}`,
385
+ type: 'TXT',
386
+ value: `v=DMARC1; p=none; rua=mailto:dmarc@${doc.domain}`,
387
+ ttl: 3600,
388
+ }));
389
+ for (const intent of intents) {
390
+ if (!intent.domainId) {
391
+ intent.status = 'failed';
392
+ intent.error = {
393
+ code: 'DNS_ZONE_UNMANAGED',
394
+ message: `No managed DNS zone covers ${intent.name}`,
395
+ recordKey: intent.key,
396
+ retryable: false,
397
+ };
126
398
  }
399
+ }
400
+ const revision = {
401
+ id: revisionId,
402
+ createdAt: now,
403
+ selector: doc.pendingDkim?.selector || activeDkim?.selector || doc.dkim.selector || 'default',
404
+ edgeIdentities,
405
+ mxSlots,
406
+ recordKeys: intents.map((intent) => intent.key),
407
+ };
408
+ return {
409
+ doc,
410
+ revision,
411
+ intents,
412
+ preliminaryErrors,
413
+ egressIdentitySupported: eligibility.supported,
414
+ selectorCorrectSigningSupported,
415
+ };
416
+ }
417
+ getActiveDkim(doc) {
418
+ if (doc.activeDkim?.publicKey)
419
+ return doc.activeDkim;
420
+ const publicKey = doc.dkim?.publicKey?.trim();
421
+ if (!publicKey)
422
+ return undefined;
423
+ return {
424
+ selector: doc.dkim.selector || 'default',
425
+ keySize: doc.dkim.keySize || 2048,
426
+ publicKey,
427
+ createdAt: doc.createdAt,
127
428
  };
128
- // (a) shared mail hostname → every enabled mail edge
129
- const sharedHostname = this.getSharedMailHostname();
130
- const mailEdges = remoteIngressManager.getMailEdges();
131
- for (const edge of mailEdges) {
132
- pushEdgeIps(sharedHostname, edge);
133
- }
134
- // (b) per-edge FCrDNS hostname → that edge only
135
- for (const edge of remoteIngressManager.getAllEdges()) {
136
- if (!edge.enabled)
429
+ }
430
+ buildDkimIntent(doc, material, zones) {
431
+ return this.intentForName(zones, {
432
+ key: `mail-domain:${doc.id}:dkim:${material.selector}`,
433
+ ownerType: 'domain',
434
+ ownerId: `domain:${doc.id}`,
435
+ name: `${material.selector}._domainkey.${doc.domain}`,
436
+ type: 'TXT',
437
+ value: `v=DKIM1; h=sha256; k=rsa; p=${material.publicKey}`,
438
+ ttl: 3600,
439
+ });
440
+ }
441
+ intentForName(zones, input) {
442
+ return {
443
+ ...input,
444
+ domainId: this.findZoneForName(input.name, zones)?.id || '',
445
+ status: 'pending',
446
+ };
447
+ }
448
+ toDomainEdgeIdentity(candidate) {
449
+ const ipv4 = candidate.identities.find((identity) => identity.family === 4);
450
+ const ipv6 = candidate.identities.find((identity) => identity.family === 6);
451
+ const observedAt = Math.min(...candidate.identities.map((identity) => identity.observedAt));
452
+ const hubReceivedAt = Math.min(...candidate.identities.map((identity) => identity.hubReceivedAt));
453
+ return {
454
+ edgeId: candidate.edge.id,
455
+ hostname: candidate.hostname,
456
+ ...(ipv4 ? { ipv4: ipv4.address } : {}),
457
+ ...(ipv6 ? { ipv6: ipv6.address } : {}),
458
+ observedAt: new Date(observedAt).toISOString(),
459
+ hubReceivedAt: new Date(hubReceivedAt).toISOString(),
460
+ };
461
+ }
462
+ allocateMxSlots(doc, candidates) {
463
+ if (candidates.length === 1) {
464
+ return [{
465
+ priority: 10,
466
+ edgeId: candidates[0].edge.id,
467
+ hostname: candidates[0].hostname,
468
+ }];
469
+ }
470
+ const byId = new Map(candidates.map((candidate) => [candidate.edge.id, candidate]));
471
+ const result = [];
472
+ const usedEdges = new Set();
473
+ const usedHostnames = new Set();
474
+ const previous = [
475
+ ...(doc.reconciliation?.activeRevision?.mxSlots || []),
476
+ ...(doc.reconciliation?.pendingRevision?.mxSlots || []),
477
+ ];
478
+ for (const priority of [10, 20]) {
479
+ const old = previous.find((slot) => slot.priority === priority);
480
+ const candidate = old ? byId.get(old.edgeId) : undefined;
481
+ if (!candidate || candidate.hostname !== old?.hostname)
482
+ continue;
483
+ if (usedEdges.has(candidate.edge.id) || usedHostnames.has(candidate.hostname))
137
484
  continue;
138
- pushEdgeIps(edge.mailHostname, edge);
485
+ result.push({ priority, edgeId: candidate.edge.id, hostname: candidate.hostname });
486
+ usedEdges.add(candidate.edge.id);
487
+ usedHostnames.add(candidate.hostname);
139
488
  }
140
- // (c) mx.<domain> → pinned edges for every pinned email domain
141
- const emailDomainDocs = await EmailDomainDoc.findAll();
142
- for (const doc of emailDomainDocs) {
143
- const edgeFilter = doc.remoteIngress?.edgeFilter;
144
- if (!edgeFilter?.length)
489
+ for (const priority of [10, 20]) {
490
+ if (result.some((slot) => slot.priority === priority))
491
+ continue;
492
+ const candidate = candidates.find((entry) => (!usedEdges.has(entry.edge.id) && !usedHostnames.has(entry.hostname)));
493
+ if (!candidate)
494
+ continue;
495
+ result.push({ priority, edgeId: candidate.edge.id, hostname: candidate.hostname });
496
+ usedEdges.add(candidate.edge.id);
497
+ usedHostnames.add(candidate.hostname);
498
+ }
499
+ return result.sort((left, right) => left.priority - right.priority);
500
+ }
501
+ buildSpfValue(identities) {
502
+ const terms = new Set();
503
+ for (const identity of identities) {
504
+ if (identity.ipv4)
505
+ terms.add(`ip4:${identity.ipv4}`);
506
+ if (identity.ipv6)
507
+ terms.add(`ip6:${identity.ipv6}`);
508
+ }
509
+ return `v=spf1 ${[...terms].sort().join(' ')} -all`.replace(/\s+/g, ' ').trim();
510
+ }
511
+ async persistDesiredPlan(plan) {
512
+ const previous = plan.doc.reconciliation;
513
+ const previousSignature = JSON.stringify((previous?.intents || []).map((intent) => [intent.key, intent.value]));
514
+ const nextSignature = JSON.stringify(plan.intents.map((intent) => [intent.key, intent.value]));
515
+ const generation = (previous?.desiredGeneration || 0) + (previousSignature === nextSignature ? 0 : 1);
516
+ plan.doc.reconciliation = {
517
+ lifecycleStatus: previous?.lifecycleStatus === 'active' ? 'active' : 'pending',
518
+ desiredGeneration: Math.max(generation, 1),
519
+ activeRevision: previous?.activeRevision,
520
+ pendingRevision: plan.revision,
521
+ intents: plan.intents,
522
+ errors: plan.preliminaryErrors,
523
+ capability: {
524
+ egressIdentityProof: plan.egressIdentitySupported ? 'supported' : 'unsupported',
525
+ selectorCorrectSigning: plan.selectorCorrectSigningSupported ? 'supported' : 'unsupported',
526
+ },
527
+ lastAttemptAt: new Date().toISOString(),
528
+ retryAt: new Date(Date.now() + MAIL_DNS_RETRY_INTERVAL_MS).toISOString(),
529
+ };
530
+ plan.doc.updatedAt = new Date().toISOString();
531
+ await plan.doc.save();
532
+ }
533
+ async persistDeletingAttempt(doc) {
534
+ doc.reconciliation = {
535
+ ...(doc.reconciliation || {
536
+ lifecycleStatus: 'deleting',
537
+ desiredGeneration: 1,
538
+ intents: [],
539
+ errors: [],
540
+ }),
541
+ lifecycleStatus: 'deleting',
542
+ intents: [],
543
+ errors: (doc.reconciliation?.errors || []).filter((error) => error.code !== 'DNS_CLEANUP_FAILED'),
544
+ lastAttemptAt: new Date().toISOString(),
545
+ retryAt: new Date(Date.now() + MAIL_DNS_RETRY_INTERVAL_MS).toISOString(),
546
+ };
547
+ await doc.save();
548
+ }
549
+ async reconcileZones(intents, zones) {
550
+ const errors = new Map();
551
+ const byZone = new Map();
552
+ for (const intent of intents) {
553
+ if (!intent.domainId || intent.error)
145
554
  continue;
146
- const pinnedEdges = remoteIngressManager.resolveEdgesByFilter(edgeFilter);
147
- if (pinnedEdges.length === 0) {
148
- logger.log('warn', `MailDnsSync: email domain ${doc.domain} pin [${edgeFilter.join(', ')}] matches no enabled edge — mx.${doc.domain} not published`);
555
+ const group = byZone.get(intent.domainId) || [];
556
+ group.push(intent);
557
+ byZone.set(intent.domainId, group);
558
+ }
559
+ for (const [zoneId, zoneIntents] of byZone) {
560
+ const zone = zones.find((candidate) => candidate.id === zoneId);
561
+ if (!zone)
149
562
  continue;
563
+ try {
564
+ if (zone.source === 'provider') {
565
+ const result = await this.dcRouterRef.dnsManager.syncDomain(zone.id);
566
+ if (!result.success)
567
+ throw new Error(result.message || `Failed to refresh ${zone.name}`);
568
+ }
569
+ for (const intent of this.sortIntents(zoneIntents)) {
570
+ await this.reconcileIntent(intent, zoneIntents);
571
+ }
150
572
  }
151
- for (const edge of pinnedEdges) {
152
- pushEdgeIps(`mx.${doc.domain}`, edge);
573
+ catch (error) {
574
+ const outcome = {
575
+ code: 'DNS_PROVIDER_ZONE_FAILED',
576
+ message: `DNS zone ${zone.name} reconciliation failed: ${error.message}`,
577
+ retryable: true,
578
+ };
579
+ errors.set(zone.id, outcome);
580
+ for (const intent of zoneIntents) {
581
+ intent.status = 'failed';
582
+ intent.error = { ...outcome, recordKey: intent.key };
583
+ }
153
584
  }
154
585
  }
155
- // Hard hub-hiding guard: a hub IP must never become a mail-facing answer.
156
- const hubIps = new Set([this.dcRouterRef.options.publicIp, this.dcRouterRef.detectedPublicIp].filter(Boolean));
157
- const guarded = desired.filter((record) => {
158
- if (hubIps.has(record.value)) {
159
- logger.log('error', `MailDnsSync: refusing to publish ${record.type} ${record.name} → ${record.value} — value is the hub public IP`);
160
- return false;
586
+ return errors;
587
+ }
588
+ sortIntents(intents) {
589
+ const rank = (intent) => {
590
+ if (intent.type === 'A' || intent.type === 'AAAA')
591
+ return 0;
592
+ if (intent.type === 'TXT' && intent.name.includes('._domainkey.'))
593
+ return 1;
594
+ if (intent.type === 'TXT')
595
+ return 2;
596
+ return 3; // MX is exposed only after identity/auth records exist.
597
+ };
598
+ return [...intents].sort((left, right) => rank(left) - rank(right) || left.key.localeCompare(right.key));
599
+ }
600
+ async reconcileIntent(intent, zoneIntents) {
601
+ const records = await DnsRecordDoc.findByDomainId(intent.domainId);
602
+ const rrset = records.filter((record) => (record.name.toLowerCase() === intent.name.toLowerCase() && record.type === intent.type));
603
+ const exact = rrset.filter((record) => this.valuesEqual(intent.type, record.value, intent.value));
604
+ const desiredValues = zoneIntents
605
+ .filter((candidate) => candidate.name.toLowerCase() === intent.name.toLowerCase() && candidate.type === intent.type)
606
+ .map((candidate) => candidate.value);
607
+ const unmanaged = rrset.filter((record) => (record.managedBy !== MAIL_DNS_MANAGED_BY && this.isRelevantUnmanagedTxt(intent, record.value)));
608
+ const exactUnmanaged = exact.filter((record) => record.managedBy !== MAIL_DNS_MANAGED_BY);
609
+ const conflictingUnmanaged = unmanaged.filter((record) => (!desiredValues.some((desired) => this.valuesEqual(intent.type, record.value, desired))));
610
+ if (conflictingUnmanaged.length > 0 || (intent.type === 'TXT' && exactUnmanaged.length > 1)) {
611
+ intent.status = 'conflict';
612
+ intent.error = {
613
+ code: 'DNS_UNMANAGED_CONFLICT',
614
+ message: `Unmanaged ${intent.type} record conflicts with ${intent.name}`,
615
+ recordKey: intent.key,
616
+ retryable: false,
617
+ };
618
+ return;
619
+ }
620
+ if (exactUnmanaged.length === 1) {
621
+ // Exact operator record satisfies the intent without adoption/deletion.
622
+ // Remove only a dcrouter-owned duplicate of this same intent; other MX
623
+ // values in the desired RRset remain independently managed.
624
+ const ownedDuplicates = rrset.filter((record) => (record.managedBy === MAIL_DNS_MANAGED_BY
625
+ && (record.managedRecordKey === intent.key
626
+ || this.valuesEqual(intent.type, record.value, intent.value))));
627
+ for (const duplicate of ownedDuplicates) {
628
+ const result = await this.dcRouterRef.dnsManager.deleteRecord(duplicate.id);
629
+ if (!result.success)
630
+ throw new Error(result.message || `Failed to delete duplicate ${intent.key}`);
161
631
  }
162
- return true;
632
+ intent.status = 'satisfied';
633
+ return;
634
+ }
635
+ let managed = rrset.find((record) => record.managedRecordKey === intent.key);
636
+ if (!managed) {
637
+ const adoptable = exact.find((record) => record.managedBy === MAIL_DNS_MANAGED_BY);
638
+ if (adoptable) {
639
+ adoptable.managedOwnerId = intent.ownerId;
640
+ adoptable.managedRecordKey = intent.key;
641
+ await adoptable.save();
642
+ managed = adoptable;
643
+ }
644
+ }
645
+ if (managed) {
646
+ if (!this.valuesEqual(intent.type, managed.value, intent.value) || managed.name !== intent.name) {
647
+ const result = await this.dcRouterRef.dnsManager.updateRecord({
648
+ id: managed.id,
649
+ name: intent.name,
650
+ value: intent.value,
651
+ ttl: intent.ttl,
652
+ });
653
+ if (!result.success)
654
+ throw new Error(result.message || `Failed to update ${intent.key}`);
655
+ }
656
+ intent.providerRecordId = managed.providerRecordId;
657
+ intent.status = 'satisfied';
658
+ intent.lastAttemptAt = new Date().toISOString();
659
+ return;
660
+ }
661
+ const result = await this.dcRouterRef.dnsManager.createRecord({
662
+ domainId: intent.domainId,
663
+ name: intent.name,
664
+ type: intent.type,
665
+ value: intent.value,
666
+ ttl: intent.ttl,
667
+ createdBy: MAIL_DNS_SYNC_CREATED_BY,
668
+ managedBy: MAIL_DNS_MANAGED_BY,
669
+ managedOwnerId: intent.ownerId,
670
+ managedRecordKey: intent.key,
163
671
  });
164
- // Dedupe (several edges may share a tag and a pin may overlap the mail pool)
165
- const seen = new Set();
166
- return guarded.filter((record) => {
167
- const key = `${record.name}|${record.type}|${record.value}`;
168
- if (seen.has(key))
169
- return false;
170
- seen.add(key);
672
+ if (!result.success)
673
+ throw new Error(result.message || `Failed to create ${intent.key}`);
674
+ intent.status = 'satisfied';
675
+ intent.lastAttemptAt = new Date().toISOString();
676
+ }
677
+ valuesEqual(type, left, right) {
678
+ if (type === 'MX')
679
+ return normalizeMx(left) === normalizeMx(right);
680
+ if (type === 'A' || type === 'AAAA')
681
+ return left.trim() === right.trim();
682
+ return left.trim() === right.trim();
683
+ }
684
+ isRelevantUnmanagedTxt(intent, value) {
685
+ if (intent.type !== 'TXT')
171
686
  return true;
687
+ const normalizedIntent = intent.value.trim().toLowerCase();
688
+ const normalizedValue = value.trim().toLowerCase();
689
+ if (normalizedIntent.startsWith('v=spf1'))
690
+ return normalizedValue.startsWith('v=spf1');
691
+ if (normalizedIntent.startsWith('v=dmarc1'))
692
+ return normalizedValue.startsWith('v=dmarc1');
693
+ if (normalizedIntent.startsWith('v=dkim1'))
694
+ return normalizedValue.startsWith('v=dkim1');
695
+ return this.valuesEqual(intent.type, value, intent.value);
696
+ }
697
+ async validateAndActivate(plan, errors, zoneErrors) {
698
+ const verifiable = plan.intents.filter((intent) => (!intent.error && !zoneErrors.has(intent.domainId)));
699
+ const markResult = (intent, result) => {
700
+ if (result.valid) {
701
+ intent.status = 'valid';
702
+ intent.validatedAt = new Date().toISOString();
703
+ intent.error = undefined;
704
+ }
705
+ else {
706
+ intent.status = 'pending';
707
+ intent.error = {
708
+ code: 'DNS_PROPAGATION_PENDING',
709
+ message: result.reason || `${intent.type} ${intent.name} has not propagated`,
710
+ recordKey: intent.key,
711
+ retryable: true,
712
+ };
713
+ }
714
+ };
715
+ const mxByName = new Map();
716
+ for (const intent of verifiable.filter((candidate) => candidate.type === 'MX')) {
717
+ const group = mxByName.get(intent.name) || [];
718
+ group.push(intent);
719
+ mxByName.set(intent.name, group);
720
+ }
721
+ await Promise.all([
722
+ ...verifiable.filter((intent) => intent.type !== 'MX').map(async (intent) => {
723
+ const result = await this.verifier.verifyRecord(intent)
724
+ .catch((error) => ({ valid: false, reason: error.message }));
725
+ markResult(intent, result);
726
+ }),
727
+ ...[...mxByName.values()].map(async (intents) => {
728
+ const previousMx = (plan.doc.reconciliation?.activeRevision?.mxSlots || [])
729
+ .map((slot) => `${slot.priority} ${slot.hostname}`).sort();
730
+ const desiredMx = intents.map((intent) => normalizeMx(intent.value)).sort();
731
+ const isMakeBeforeBreakTransition = previousMx.length > 0
732
+ && JSON.stringify(previousMx) !== JSON.stringify(desiredMx);
733
+ const result = await (this.verifier.verifyMxSet && !isMakeBeforeBreakTransition
734
+ ? this.verifier.verifyMxSet(intents)
735
+ : Promise.all(intents.map((intent) => this.verifier.verifyRecord(intent))).then((results) => ({
736
+ valid: results.every((entry) => entry.valid),
737
+ reason: results.find((entry) => !entry.valid)?.reason,
738
+ })))
739
+ .catch((error) => ({ valid: false, reason: error.message }));
740
+ for (const intent of intents)
741
+ markResult(intent, result);
742
+ }),
743
+ ]);
744
+ for (const intent of plan.intents) {
745
+ if (intent.error && !errors.some((error) => error.code === intent.error.code && error.recordKey === intent.key)) {
746
+ errors.push(intent.error);
747
+ }
748
+ }
749
+ if (plan.egressIdentitySupported && plan.revision.edgeIdentities.length > 0) {
750
+ const identityResults = await Promise.all(plan.revision.edgeIdentities.map(async (identity) => ({
751
+ identity,
752
+ result: await this.verifier.verifyForwardConfirmedIdentity(identity)
753
+ .catch((error) => ({ valid: false, reason: error.message })),
754
+ })));
755
+ for (const { identity, result } of identityResults) {
756
+ if (!result.valid) {
757
+ errors.push({
758
+ code: 'FCRDNS_INVALID',
759
+ message: result.reason || `FCrDNS validation failed for ${identity.edgeId}`,
760
+ retryable: true,
761
+ });
762
+ }
763
+ }
764
+ }
765
+ const canActivate = plan.egressIdentitySupported
766
+ && plan.revision.edgeIdentities.length > 0
767
+ && (plan.revision.edgeIdentities.length >= 2
768
+ ? plan.revision.mxSlots.length === 2
769
+ : plan.revision.mxSlots.length === 1)
770
+ && plan.selectorCorrectSigningSupported
771
+ && Boolean(this.getActiveDkim(plan.doc))
772
+ && plan.intents.length > 0
773
+ && plan.intents.every((intent) => intent.status === 'valid')
774
+ && errors.length === 0;
775
+ const hasPermanentError = errors.some((error) => error.retryable === false)
776
+ || errors.some((error) => ['DNS_PROVIDER_ZONE_FAILED', 'DNS_UNMANAGED_CONFLICT'].includes(error.code));
777
+ const previousActive = plan.doc.reconciliation?.activeRevision;
778
+ const topologyChanged = Boolean(previousActive) && JSON.stringify({
779
+ selector: previousActive?.selector,
780
+ edgeIdentities: previousActive?.edgeIdentities,
781
+ mxSlots: previousActive?.mxSlots,
782
+ }) !== JSON.stringify({
783
+ selector: plan.revision.selector,
784
+ edgeIdentities: plan.revision.edgeIdentities,
785
+ mxSlots: plan.revision.mxSlots,
172
786
  });
787
+ const now = new Date().toISOString();
788
+ if (canActivate && plan.doc.pendingDkim) {
789
+ this.promotePendingDkim(plan.doc, now);
790
+ }
791
+ plan.doc.reconciliation = {
792
+ lifecycleStatus: canActivate ? 'active' : hasPermanentError ? 'failed' : 'pending',
793
+ desiredGeneration: plan.doc.reconciliation?.desiredGeneration || 1,
794
+ activeRevision: canActivate
795
+ ? { ...plan.revision, activatedAt: now }
796
+ : plan.doc.reconciliation?.activeRevision,
797
+ pendingRevision: canActivate ? undefined : plan.revision,
798
+ intents: plan.intents,
799
+ errors,
800
+ capability: {
801
+ egressIdentityProof: plan.egressIdentitySupported ? 'supported' : 'unsupported',
802
+ selectorCorrectSigning: plan.selectorCorrectSigningSupported ? 'supported' : 'unsupported',
803
+ },
804
+ lastAttemptAt: now,
805
+ ...(canActivate ? { lastSuccessAt: now } : { retryAt: new Date(Date.now() + MAIL_DNS_RETRY_INTERVAL_MS).toISOString() }),
806
+ };
807
+ plan.doc.dnsStatus = {
808
+ mx: this.statusForIntent(plan.intents, (intent) => intent.type === 'MX'),
809
+ spf: this.statusForIntent(plan.intents, (intent) => intent.type === 'TXT' && intent.value.startsWith('v=spf1')),
810
+ dkim: this.statusForIntent(plan.intents, (intent) => intent.type === 'TXT' && intent.name.includes('._domainkey.')),
811
+ dmarc: this.statusForIntent(plan.intents, (intent) => intent.type === 'TXT' && intent.value.startsWith('v=DMARC1')),
812
+ lastCheckedAt: now,
813
+ };
814
+ plan.doc.updatedAt = now;
815
+ await plan.doc.save();
816
+ if (canActivate && topologyChanged) {
817
+ // The transition SPF intentionally authorizes old + new identities. Run
818
+ // one immediate follow-up to contract it to the newly active revision.
819
+ this.dirty = true;
820
+ }
173
821
  }
174
- getSharedMailHostname() {
175
- const hostname = this.dcRouterRef.options.emailConfig?.hostname
176
- || this.dcRouterRef.options.tls?.domain;
177
- if (!hostname || hostname === 'localhost') {
178
- return undefined;
822
+ promotePendingDkim(doc, now) {
823
+ const pending = doc.pendingDkim;
824
+ if (!pending)
825
+ return;
826
+ const previous = this.getActiveDkim(doc);
827
+ const overlapDays = Math.max(1, doc.dkim.retirementOverlapDays || 30);
828
+ const retiring = (doc.retiringDkim || []).filter((material) => (material.selector !== pending.selector
829
+ && (!material.retireAfter || Date.parse(material.retireAfter) > Date.now())));
830
+ if (previous && previous.selector !== pending.selector) {
831
+ retiring.push({
832
+ ...previous,
833
+ retireAfter: new Date(Date.now() + overlapDays * 86_400_000).toISOString(),
834
+ });
179
835
  }
180
- return hostname;
836
+ doc.activeDkim = { ...pending, validatedAt: now, promotedAt: now };
837
+ doc.pendingDkim = undefined;
838
+ doc.retiringDkim = retiring;
839
+ doc.dkim.selector = pending.selector;
840
+ doc.dkim.keySize = pending.keySize;
841
+ doc.dkim.publicKey = pending.publicKey;
842
+ doc.dkim.lastRotatedAt = now;
181
843
  }
182
- /** Longest-suffix zone match: 'mx.gated.one' → zone 'gated.one'. */
183
- findZoneForName(name, zones) {
184
- let best;
185
- for (const zone of zones) {
186
- const zoneName = zone.name.toLowerCase();
187
- if (name === zoneName || name.endsWith(`.${zoneName}`)) {
188
- if (!best || zoneName.length > best.name.length) {
189
- best = zone;
844
+ statusForIntent(intents, predicate) {
845
+ const selected = intents.filter(predicate);
846
+ if (selected.length === 0)
847
+ return 'missing';
848
+ if (selected.every((intent) => intent.status === 'valid'))
849
+ return 'valid';
850
+ if (selected.some((intent) => intent.status === 'conflict' || intent.status === 'failed'))
851
+ return 'invalid';
852
+ return 'missing';
853
+ }
854
+ async cleanupStaleManagedRecords(zones) {
855
+ const docs = await EmailDomainDoc.findAll();
856
+ const keep = new Set();
857
+ for (const doc of docs) {
858
+ if (doc.reconciliation?.lifecycleStatus === 'deleting')
859
+ continue;
860
+ for (const intent of doc.reconciliation?.intents || [])
861
+ keep.add(intent.key);
862
+ if (doc.reconciliation?.lifecycleStatus !== 'active') {
863
+ for (const key of doc.reconciliation?.activeRevision?.recordKeys || [])
864
+ keep.add(key);
865
+ }
866
+ }
867
+ const managed = (await DnsRecordDoc.findAll()).filter((record) => record.managedBy === MAIL_DNS_MANAGED_BY);
868
+ for (const record of managed) {
869
+ if (record.managedRecordKey && keep.has(record.managedRecordKey))
870
+ continue;
871
+ try {
872
+ const result = await this.dcRouterRef.dnsManager.deleteRecord(record.id);
873
+ if (!result.success)
874
+ throw new Error(result.message || 'delete failed');
875
+ }
876
+ catch (error) {
877
+ const ownerId = record.managedOwnerId?.replace(/^domain:/, '');
878
+ const owner = ownerId ? docs.find((doc) => doc.id === ownerId) : undefined;
879
+ if (owner?.reconciliation) {
880
+ owner.reconciliation.errors.push({
881
+ code: 'DNS_CLEANUP_FAILED',
882
+ message: `Failed to delete ${record.type} ${record.name}: ${error.message}`,
883
+ recordKey: record.managedRecordKey,
884
+ retryable: true,
885
+ });
886
+ owner.reconciliation.retryAt = new Date(Date.now() + MAIL_DNS_RETRY_INTERVAL_MS).toISOString();
887
+ await owner.save();
190
888
  }
191
889
  }
192
890
  }
193
- return best;
891
+ }
892
+ async finalizeDeletingDomains() {
893
+ const docs = await EmailDomainDoc.findAll();
894
+ const records = await DnsRecordDoc.findAll();
895
+ for (const doc of docs) {
896
+ if (doc.reconciliation?.lifecycleStatus !== 'deleting')
897
+ continue;
898
+ const owned = records.some((record) => (record.managedBy === MAIL_DNS_MANAGED_BY && record.managedOwnerId === `domain:${doc.id}`));
899
+ if (owned || doc.reconciliation.errors.some((error) => error.code === 'DNS_CLEANUP_FAILED'))
900
+ continue;
901
+ await doc.delete();
902
+ logger.log('info', `Email domain deletion finalized: ${doc.domain}`);
903
+ }
904
+ }
905
+ findZoneForName(name, zones) {
906
+ const normalized = normalizeHostname(name);
907
+ return [...zones]
908
+ .filter((zone) => normalized === normalizeHostname(zone.name) || normalized.endsWith(`.${normalizeHostname(zone.name)}`))
909
+ .sort((left, right) => right.name.length - left.name.length)[0];
194
910
  }
195
911
  }
196
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYWlsLWRucy1zeW5jLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvZW1haWwvY2xhc3Nlcy5tYWlsLWRucy1zeW5jLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ3pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNsRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNkNBQTZDLENBQUM7QUFJN0UsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsZ0JBQWdCLENBQUM7QUFRekQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxPQUFPLFdBQVc7SUFJRjtJQUhaLFlBQVksQ0FBaUI7SUFDN0IsWUFBWSxHQUFHLEtBQUssQ0FBQztJQUU3QixZQUFvQixXQUFxQjtRQUFyQixnQkFBVyxHQUFYLFdBQVcsQ0FBVTtJQUFHLENBQUM7SUFFN0MsK0VBQStFO0lBQ3hFLFdBQVcsQ0FBQyxNQUFjO1FBQy9CLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBWSxFQUFFLEVBQUU7WUFDdkMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsdUJBQXVCLE1BQU0sTUFBTyxHQUFhLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNuRixDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxnRkFBZ0Y7SUFDekUsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFjO1FBQzlCLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ3RCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO1lBQ3pCLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQztRQUMzQixDQUFDO1FBQ0QsSUFBSSxDQUFDLFlBQVksR0FBRyxJ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
912
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYWlsLWRucy1zeW5jLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvZW1haWwvY2xhc3Nlcy5tYWlsLWRucy1zeW5jLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJDQUEyQyxDQUFDO0FBQ3pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNsRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sNkNBQTZDLENBQUM7QUFXN0UsT0FBTyxFQUNMLG1CQUFtQixFQUNuQix5Q0FBeUMsR0FFMUMsTUFBTSxvQ0FBb0MsQ0FBQztBQUU1QyxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxxQkFBcUIsQ0FBQztBQUM5RCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxxQkFBcUIsQ0FBQztBQUN6RCxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDO0FBaUJyRCxTQUFTLGlCQUFpQixDQUFDLEtBQWE7SUFDdEMsT0FBTyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztBQUN2RCxDQUFDO0FBRUQsU0FBUyxXQUFXLENBQUMsS0FBYTtJQUNoQyxNQUFNLENBQUMsUUFBUSxFQUFFLFFBQVEsR0FBRyxFQUFFLENBQUMsR0FBRyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVELE9BQU8sR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksaUJBQWlCLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztBQUM5RCxDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsT0FBZTtJQUNqQyxJQUFJLFVBQVUsR0FBRyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDdkMsSUFBSSxVQUFVLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDN0IsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM5QyxNQUFNLElBQUksR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3BFLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksR0FBRyxDQUFDLElBQUksSUFBSSxHQUFHLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDaEcsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELFVBQVUsR0FBRyxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDekksQ0FBQztJQUNELE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdEMsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUN4QyxNQUFN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