@serve.zone/dcrouter 18.7.1 → 18.8.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.
- package/deno.json +1 -1
- package/dist_serve/bundle.js +2528 -2206
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.js +15 -27
- package/dist_ts/dns/classes.dns-server-runtime.d.ts +26 -69
- package/dist_ts/dns/classes.dns-server-runtime.js +136 -96
- package/dist_ts/dns/domain-ownership.d.ts +4 -5
- package/dist_ts/dns/domain-ownership.js +1 -1
- package/dist_ts/dns/manager.dns.d.ts +11 -13
- package/dist_ts/dns/manager.dns.js +155 -73
- package/dist_ts/email/classes.mail-dns-sync.d.ts +0 -1
- package/dist_ts/email/classes.mail-dns-sync.js +10 -38
- package/dist_ts_migrations/index.js +32 -10
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +25 -24
- package/readme.hints.md +98 -84
- package/readme.md +8 -6
- package/third-party-notices.md +13 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +18 -34
- package/ts/dns/classes.dns-server-runtime.ts +154 -105
- package/ts/dns/domain-ownership.ts +4 -5
- package/ts/dns/manager.dns.ts +180 -73
- package/ts/email/classes.mail-dns-sync.ts +9 -35
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/dcrouter',
|
|
6
|
-
version: '18.
|
|
6
|
+
version: '18.8.0',
|
|
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.
|
|
4
|
+
"version": "18.8.0",
|
|
5
5
|
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -16,59 +16,60 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@git.zone/tsbuild": "^4.4.2",
|
|
19
|
-
"@git.zone/tsbundle": "^2.11.
|
|
19
|
+
"@git.zone/tsbundle": "^2.11.4",
|
|
20
20
|
"@git.zone/tsdeno": "^1.7.0",
|
|
21
|
-
"@git.zone/tsdocker": "^3.
|
|
21
|
+
"@git.zone/tsdocker": "^3.4.2",
|
|
22
22
|
"@git.zone/tsrun": "^2.0.6",
|
|
23
|
-
"@git.zone/tstest": "^
|
|
23
|
+
"@git.zone/tstest": "^4.0.0",
|
|
24
24
|
"@git.zone/tswatch": "^3.3.5",
|
|
25
|
-
"@
|
|
25
|
+
"@push.rocks/smartdb-v2": "npm:@push.rocks/smartdb@2.14.6",
|
|
26
|
+
"@types/node": "26.2.0",
|
|
26
27
|
"@types/web-push": "3.6.4"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@api.global/typedrequest": "^3.
|
|
30
|
-
"@api.global/typedrequest-interfaces": "^
|
|
31
|
-
"@api.global/typedserver": "^8.
|
|
32
|
-
"@api.global/typedsocket": "^5.
|
|
30
|
+
"@api.global/typedrequest": "^3.8.0",
|
|
31
|
+
"@api.global/typedrequest-interfaces": "^4.0.0",
|
|
32
|
+
"@api.global/typedserver": "^8.12.0",
|
|
33
|
+
"@api.global/typedsocket": "^5.1.2",
|
|
33
34
|
"@apiclient.xyz/cloudflare": "^8.0.0",
|
|
34
|
-
"@design.estate/dees-catalog": "^3.
|
|
35
|
+
"@design.estate/dees-catalog": "^3.104.0",
|
|
35
36
|
"@design.estate/dees-element": "^2.4.0",
|
|
36
|
-
"@idp.global/sdk": "^
|
|
37
|
+
"@idp.global/sdk": "^15.0.1",
|
|
37
38
|
"@push.rocks/lik": "^6.4.1",
|
|
38
39
|
"@push.rocks/projectinfo": "^5.1.0",
|
|
39
40
|
"@push.rocks/qenv": "^6.1.4",
|
|
40
41
|
"@push.rocks/smartacme": "^9.5.0",
|
|
41
|
-
"@push.rocks/smartbucket": "^4.
|
|
42
|
+
"@push.rocks/smartbucket": "^4.13.0",
|
|
42
43
|
"@push.rocks/smartchallenge": "^1.1.0",
|
|
43
44
|
"@push.rocks/smartdata": "^7.4.0",
|
|
44
|
-
"@push.rocks/smartdb": "2.
|
|
45
|
-
"@push.rocks/smartdns": "^
|
|
46
|
-
"@push.rocks/smartfs": "^1.
|
|
45
|
+
"@push.rocks/smartdb": "5.2.2",
|
|
46
|
+
"@push.rocks/smartdns": "^8.2.1",
|
|
47
|
+
"@push.rocks/smartfs": "^1.7.0",
|
|
47
48
|
"@push.rocks/smartguard": "^3.1.2",
|
|
48
49
|
"@push.rocks/smartjwt": "^2.5.1",
|
|
49
50
|
"@push.rocks/smartlog": "^3.2.2",
|
|
50
51
|
"@push.rocks/smartmcp": "^0.3.0",
|
|
51
52
|
"@push.rocks/smartmetrics": "^3.0.3",
|
|
52
|
-
"@push.rocks/smartmigration": "1.
|
|
53
|
-
"@push.rocks/smartmta": "^9.
|
|
54
|
-
"@push.rocks/smartnetwork": "^4.
|
|
53
|
+
"@push.rocks/smartmigration": "1.5.0",
|
|
54
|
+
"@push.rocks/smartmta": "^9.5.0",
|
|
55
|
+
"@push.rocks/smartnetwork": "^4.10.1",
|
|
55
56
|
"@push.rocks/smartpath": "^6.0.0",
|
|
56
|
-
"@push.rocks/smartpromise": "^4.2.
|
|
57
|
+
"@push.rocks/smartpromise": "^4.2.5",
|
|
57
58
|
"@push.rocks/smartproxy": "^27.20.0",
|
|
58
59
|
"@push.rocks/smartradius": "^1.3.0",
|
|
59
60
|
"@push.rocks/smartrequest": "^5.0.4",
|
|
60
61
|
"@push.rocks/smartrx": "^3.0.12",
|
|
61
|
-
"@push.rocks/smartserve": "^
|
|
62
|
-
"@push.rocks/smartstate": "^2.3.
|
|
62
|
+
"@push.rocks/smartserve": "^3.0.0",
|
|
63
|
+
"@push.rocks/smartstate": "^2.3.3",
|
|
63
64
|
"@push.rocks/smartunique": "^3.0.10",
|
|
64
65
|
"@push.rocks/smartvpn": "1.22.0",
|
|
65
66
|
"@push.rocks/taskbuffer": "^8.0.2",
|
|
66
67
|
"@serve.zone/catalog": "^2.21.1",
|
|
67
|
-
"@serve.zone/interfaces": "^
|
|
68
|
-
"@serve.zone/remoteingress": "^5.1.
|
|
68
|
+
"@serve.zone/interfaces": "^27.3.1",
|
|
69
|
+
"@serve.zone/remoteingress": "^5.1.1",
|
|
69
70
|
"@tsclass/tsclass": "^9.5.1",
|
|
70
71
|
"@types/qrcode": "^1.5.6",
|
|
71
|
-
"ipaddr.js": "^2.
|
|
72
|
+
"ipaddr.js": "^2.5.0",
|
|
72
73
|
"lru-cache": "^11.5.2",
|
|
73
74
|
"qrcode": "^1.5.4",
|
|
74
75
|
"uuid": "^14.0.1",
|
package/readme.hints.md
CHANGED
|
@@ -26,11 +26,10 @@ Corollaries, all of which are review-blocking:
|
|
|
26
26
|
- A registration is torn down by the same operation that removes what it was
|
|
27
27
|
derived from. Track what you actually registered; do not recompute "would I
|
|
28
28
|
have registered this?" at teardown time. `DnsServer.unregisterHandler(pattern,
|
|
29
|
-
types)` removes **every** handler for that pattern/type pair
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
torn down by `tearDownDomainRuntimeRegistrations(domainId)`.
|
|
29
|
+
types)` removes **every** handler for that pattern/type pair. `DnsManager`
|
|
30
|
+
retains every handle returned by `registerHandler()` under a per-domain
|
|
31
|
+
`name|type` key, tagged `persisted` or `generated-default`, and invokes only
|
|
32
|
+
those exact handles during teardown or server replacement.
|
|
34
33
|
- A negative cache is invalidated by the event that **resolves its cause**, not
|
|
35
34
|
only by elapsed time. Time-only expiry plus a skip-while-cooling-down rule is a
|
|
36
35
|
deadlock: the sweep that would clear the entry is the sweep the entry blocks.
|
|
@@ -127,9 +126,10 @@ A timeout is not evidence that a zone is not ours. A mutation refuses on
|
|
|
127
126
|
**The drift audit is advisory and never mutates.** It reports three directions —
|
|
128
127
|
`delegated-but-unclaimed` (a live zone we could serve but do not),
|
|
129
128
|
`claimed-but-not-delegated` (authority we still assert after delegation moved
|
|
130
|
-
away), and `verified-but-unhosted` (authority with no `DomainDoc
|
|
131
|
-
|
|
132
|
-
zone off the air, which is the outage this path exists to
|
|
129
|
+
away), and `verified-but-unhosted` (authority with no `DomainDoc`-backed records
|
|
130
|
+
or generated apex NS). It must not revoke: a resolver blip at boot would
|
|
131
|
+
otherwise take every zone off the air, which is the outage this path exists to
|
|
132
|
+
avoid.
|
|
133
133
|
|
|
134
134
|
### Deploy sequence — seeding is mandatory, not advisable
|
|
135
135
|
|
|
@@ -144,29 +144,44 @@ restart itself introduces no reconciliation gap.
|
|
|
144
144
|
floor left to fall back on: a zone that is not in `DnsAuthorityDoc` when the new
|
|
145
145
|
build starts is REFUSED outright, and that now includes the zone that used to be
|
|
146
146
|
declared in `dnsScopes` and was the only healthy one. Insert the singleton
|
|
147
|
-
`DnsAuthorityDoc`
|
|
148
|
-
`{ zone, origin: 'verified', verifiedAt,
|
|
149
|
-
entry per delegated zone
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
147
|
+
`DnsAuthorityDoc` with `settingsId: 'dns-authority-settings'`, a `verifiedZones`
|
|
148
|
+
array containing one `{ zone, origin: 'verified', verifiedAt,
|
|
149
|
+
observedNameservers, verifiedBy }` entry per delegated zone, plus `updatedAt` and
|
|
150
|
+
`updatedBy`, *before* starting the new build. The first startup then has proof
|
|
151
|
+
already. The advisory startup drift audit re-probes and reports evidence that no
|
|
152
|
+
longer holds; it never corrects the seed automatically, so an operator must
|
|
153
|
+
re-run the seed or use the authority mutation API.
|
|
153
154
|
|
|
154
155
|
The production database is an embedded single-writer `LocalSmartDb` reached over
|
|
155
156
|
a Unix socket in `os.tmpdir()`, so it is not reachable off-host and the seed must
|
|
156
|
-
run on the dcrouter host
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
run on the dcrouter host while no dcrouter process owns it. The complete first
|
|
158
|
+
18.8.0 sequence is:
|
|
159
|
+
|
|
160
|
+
1. Stop 18.7.1, suppress automatic restart, and prove no dcrouter process remains.
|
|
161
|
+
2. Take and verify an offline backup of the complete runtime tree and configured
|
|
162
|
+
data root (normally `~/.serve.zone/dcrouter`).
|
|
163
|
+
3. Seed the authority singleton while dcrouter remains stopped.
|
|
164
|
+
4. Start exactly one 18.8.0 runtime and verify its 18.8.0 migration ledger,
|
|
165
|
+
sparse DNS ownership index, authority drift, persisted data, and direct NS,
|
|
166
|
+
SOA, and AAAA answers.
|
|
167
|
+
5. Restore automatic service management only after those checks pass.
|
|
168
|
+
|
|
169
|
+
`.nogit/debug/seed-dns-authority.ts` is the deployment-local convenience tool:
|
|
170
|
+
it probes every zone over DoH first and writes nothing on an unproven zone, and
|
|
171
|
+
it is re-runnable (it replaces the verified set with what it just proved, so
|
|
172
|
+
dropping a zone from the list drops it from authority).
|
|
173
|
+
|
|
174
|
+
SmartDB 2 must never open a data root that SmartDB 5 has written. Rollback is
|
|
175
|
+
therefore restore-only: stop 18.8.0, restore the exact pre-upgrade runtime tree
|
|
176
|
+
and offline data backup, and only then start 18.7.1.
|
|
161
177
|
|
|
162
178
|
That script lives under `.nogit/` and is therefore **not committed** — it is a
|
|
163
179
|
one-off operational tool for this specific upgrade, not product code, and it is
|
|
164
180
|
deliberately not a smartmigration step (see the next section for why). So it
|
|
165
|
-
cannot be the only record of the step: the changelog spells out the
|
|
166
|
-
shape
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
after startup with no restart. The script is a convenience, not a dependency.
|
|
181
|
+
cannot be the only record of the step: the changelog spells out the complete
|
|
182
|
+
document shape, including the `verifiedZones` array, and the same result is
|
|
183
|
+
reachable through `verifyDnsAuthorityZone` after startup with no restart. The
|
|
184
|
+
script is a convenience, not a dependency.
|
|
170
185
|
|
|
171
186
|
**If a zone was missed** — claim it after startup, no restart needed. Order:
|
|
172
187
|
|
|
@@ -221,22 +236,18 @@ on it, so dual-stack clients see a degraded site. Registering handlers for a
|
|
|
221
236
|
verified zone without moving this set would have reproduced the identical defect
|
|
222
237
|
for every runtime-verified zone.
|
|
223
238
|
|
|
224
|
-
**How it is reconciled without a restart.**
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
is re-creating the `DnsServer`, i.e. dropping UDP/TCP 53 — the outage this path
|
|
229
|
-
exists to remove. `assertLiveAuthorityZones()` checks the reference is still
|
|
230
|
-
shared right after construction and logs at `error` if it is not, so a future
|
|
231
|
-
smartdns that copies the array fails loudly instead of silently freezing
|
|
232
|
-
authority at boot; a test asserts the identity against the installed smartdns.
|
|
239
|
+
**How it is reconciled without a restart.** SmartDNS 8.2 copies constructor
|
|
240
|
+
options and exposes `setAuthoritativeZones()`. `DnsServerRuntime` calls that
|
|
241
|
+
setter on every changed database authority set and updates its local snapshot
|
|
242
|
+
only after the call succeeds. No listener is recreated, so UDP/TCP 53 stays up.
|
|
233
243
|
|
|
234
244
|
`dnssecZone` cannot be reconciled the same way — it is baked into the Rust config
|
|
235
245
|
at start — so it is taken from the boot-time set, which is **sorted** and
|
|
236
246
|
therefore stable across restarts rather than dependent on database insertion
|
|
237
|
-
order. When the set is empty
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
order. When the set is empty the required keying zone becomes
|
|
248
|
+
`no-authority.invalid`. An explicit empty `authoritativeZones` array in SmartDNS
|
|
249
|
+
8.2 claims no zone and does not fall back to `dnssecZone`; `.invalid` remains a
|
|
250
|
+
safe DNSSEC keying sentinel.
|
|
240
251
|
|
|
241
252
|
### Why removing `dnsScopes` needs no smartmigration step
|
|
242
253
|
|
|
@@ -250,8 +261,8 @@ worth stating so its absence does not read as an oversight:
|
|
|
250
261
|
carried `'bootstrap'` — `persist()` only ever wrote `verifiedZones`, and
|
|
251
262
|
`verifyZone()` is the only thing that builds an entry. The bootstrap entries
|
|
252
263
|
existed solely as a read-time projection inside `getSettings()`.
|
|
253
|
-
|
|
254
|
-
|
|
264
|
+
`DnsAuthorityDoc` already shipped in 18.7.1, and this change does not alter its
|
|
265
|
+
persisted shape.
|
|
255
266
|
- **Seeding from `dnsScopes` would forge the proof.** A migration step *could*
|
|
256
267
|
read it — `createMigrationRunner` runs in-process at startup and already takes
|
|
257
268
|
deployment-derived seeds — but a `dnsScopes` entry carries no delegation
|
|
@@ -266,50 +277,35 @@ same upgrade would claim different zones on different runs, and a migration does
|
|
|
266
277
|
not re-run to correct itself. Nondeterministic authority is worse than no
|
|
267
278
|
automation.
|
|
268
279
|
|
|
269
|
-
The upgrade path is instead **seed,
|
|
270
|
-
independent loud signals if
|
|
271
|
-
`
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
280
|
+
The upgrade path is instead **stop, back up, seed, start, verify** (above), backed
|
|
281
|
+
by three independent loud signals if seeding is skipped:
|
|
282
|
+
`DnsAuthorityManager.start()` logs at `error` on an empty set,
|
|
283
|
+
`DnsServerRuntime.setup()` logs at `error`, and the startup drift audit enumerates
|
|
284
|
+
precisely the zones to claim. Every one of them is remediable through
|
|
285
|
+
`dns-authority:write` without a restart, which makes a missed seed recoverable
|
|
286
|
+
without weakening the proof model.
|
|
275
287
|
|
|
276
|
-
###
|
|
288
|
+
### SmartDNS 8.2 integration
|
|
277
289
|
|
|
278
|
-
|
|
279
|
-
land in the same change that takes the bump. All are recorded at their code
|
|
280
|
-
sites so they cannot be lost.
|
|
290
|
+
The upstream authority and lifecycle APIs are consumed directly:
|
|
281
291
|
|
|
282
292
|
1. **The private-route overlay must declare itself non-authoritative.** It
|
|
283
293
|
registers `*` for `A` (`registerPrivateRouteHandler`). The hardened smartdns
|
|
284
294
|
authority model *suppresses* a default-`authoritative` handler for any name
|
|
285
295
|
outside every configured zone, so overlay hostnames proven by `provider-zone`
|
|
286
296
|
— owned, but not delegation-verified, therefore not in `authoritativeZones` —
|
|
287
|
-
would silently stop being answered. It
|
|
288
|
-
`{ authority: 'non-authoritative', owner: 'private-route-overlay' }
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
because a `setAuthoritativeZones()` cannot be added without releasing
|
|
300
|
-
smartdns first. Guarded two ways in the meantime:
|
|
301
|
-
`assertLiveAuthorityZones()` logs at `error` at startup if the reference is
|
|
302
|
-
no longer shared, and a test asserts the identity against the installed
|
|
303
|
-
smartdns so a copying release fails the suite rather than production. Replace
|
|
304
|
-
both with the supported setter when it ships.
|
|
305
|
-
|
|
306
|
-
3. **Per-registration teardown.** `unregisterHandler(pattern, types)` is coarse:
|
|
307
|
-
two owners on the identical pattern and type cannot be separated. Nothing
|
|
308
|
-
does that today — `DnsManager` is the single owner of apex NS, and
|
|
309
|
-
`DnsServerRuntime`'s glue records use different patterns — but the registry
|
|
310
|
-
in `DnsManager` exists to work around exactly this. `unregisterHandlerById` /
|
|
311
|
-
`unregisterHandlersByOwner` and the handle returned by `registerHandler` are
|
|
312
|
-
the released fix.
|
|
297
|
+
would silently stop being answered. It registers with
|
|
298
|
+
`{ authority: 'non-authoritative', owner: 'private-route-overlay' }`.
|
|
299
|
+
|
|
300
|
+
2. **A supported way to re-zone a running server.** `DnsServerRuntime` calls
|
|
301
|
+
`setAuthoritativeZones()` and never depends on constructor-array identity.
|
|
302
|
+
Setter failure leaves the prior local snapshot intact so authority rollback
|
|
303
|
+
can reconcile against the last successfully applied state.
|
|
304
|
+
|
|
305
|
+
3. **Per-registration teardown.** `DnsManager` retains the handle returned by
|
|
306
|
+
every `registerHandler()` call. RRset refresh, authority revocation, domain
|
|
307
|
+
deletion, shutdown, and server replacement invoke those handles, so another
|
|
308
|
+
owner's identical pattern/type registration survives.
|
|
313
309
|
|
|
314
310
|
### Generated apex NS has exactly one owner
|
|
315
311
|
|
|
@@ -321,9 +317,10 @@ unrelated restart. `DnsManager.registerAuthoritativeZoneDefaults()` is now the
|
|
|
321
317
|
only source, for every authoritative zone, reconciled in-process both ways.
|
|
322
318
|
|
|
323
319
|
Consequence worth knowing: generated apex NS hangs off a dcrouter-hosted
|
|
324
|
-
`DomainDoc
|
|
325
|
-
|
|
326
|
-
|
|
320
|
+
`DomainDoc`. A verified zone without one still claims authority and returns its
|
|
321
|
+
configured SOA plus authoritative NODATA/NXDOMAIN, but serves no persisted records
|
|
322
|
+
or generated apex NS. That lame, effectively empty zone is reported as
|
|
323
|
+
`verified-but-unhosted` drift.
|
|
327
324
|
|
|
328
325
|
## Domain ownership gates certificates and authoritative DNS (2026-07-25)
|
|
329
326
|
|
|
@@ -368,12 +365,11 @@ synthetic NS/SOA is installed only after activation, never on intent. That work
|
|
|
368
365
|
needs a persisted status field, a release-version-matched `ts_migrations` step,
|
|
369
366
|
new ops API methods, and UI — it is a feature program, not a defect fix.
|
|
370
367
|
|
|
371
|
-
Why this matters:
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
authority over it, and dcrouter listens on a public UDP/TCP 53.
|
|
368
|
+
Why this matters: SmartDNS now derives authority solely from
|
|
369
|
+
`authoritativeZones`; default-authority handler matches outside that set are
|
|
370
|
+
suppressed. dcrouter still gates handler registration on the same ownership
|
|
371
|
+
proof so a future authority change cannot activate stale or self-asserted data,
|
|
372
|
+
and the non-authoritative private overlay remains explicitly attributable.
|
|
377
373
|
|
|
378
374
|
Enforcement points:
|
|
379
375
|
|
|
@@ -392,6 +388,24 @@ Enforcement points:
|
|
|
392
388
|
- `certProvisionFunction` and `reprovisionCertificateDomain` — refuse before
|
|
393
389
|
starting an ACME order that cannot succeed.
|
|
394
390
|
|
|
391
|
+
## DoH path matching happens after TLS termination (2026-08-23)
|
|
392
|
+
|
|
393
|
+
SmartProxy cannot match an HTTP path while the incoming stream is still TLS
|
|
394
|
+
ciphertext. dcrouter therefore persists one nameserver-domain/port socket route
|
|
395
|
+
with `tls.mode: 'terminate'` and no `match.path`, then hands the cleartext stream
|
|
396
|
+
to SmartDNS `handleHttpSocket()`. SmartDNS performs the RFC 8484 dispatch for
|
|
397
|
+
both canonical `/dns-query` and the already-shipped `/resolve` alias. Keeping two
|
|
398
|
+
path-scoped socket routes would make the route decision before either path exists.
|
|
399
|
+
|
|
400
|
+
## DNS source transitions commit before runtime state (2026-08-23)
|
|
401
|
+
|
|
402
|
+
A provider-to-dcrouter migration is one SmartData session transaction: re-read
|
|
403
|
+
the `DomainDoc` and all `DnsRecordDoc` rows in the session, convert every record,
|
|
404
|
+
remove provider-only metadata, then convert the domain. Any record or domain save
|
|
405
|
+
failure aborts the whole transition. Runtime RRset/default handlers are rebuilt
|
|
406
|
+
only after commit, so a failed transaction cannot report success or expose a
|
|
407
|
+
partially local zone.
|
|
408
|
+
|
|
395
409
|
## ACME retry budgets are three independent mechanisms (2026-07-25)
|
|
396
410
|
|
|
397
411
|
Do not assume they share semantics, caps, or re-arm behaviour:
|
|
@@ -1182,4 +1196,4 @@ const dcRouter = new DcRouter({
|
|
|
1182
1196
|
- `ts/cache/classes.cachedb.ts` - CacheDb singleton wrapper
|
|
1183
1197
|
- `ts/cache/classes.cached.document.ts` - Base class with TTL support
|
|
1184
1198
|
- `ts/cache/classes.cache.cleaner.ts` - Periodic cleanup service
|
|
1185
|
-
- `ts/cache/documents/*.ts` - Document class definitions
|
|
1199
|
+
- `ts/cache/documents/*.ts` - Document class definitions
|
package/readme.md
CHANGED
|
@@ -139,7 +139,7 @@ Important runtime behavior:
|
|
|
139
139
|
- `dbConfig.enabled` defaults to enabled. Without `mongoDbUrl`, dcrouter uses embedded LocalSmartDb.
|
|
140
140
|
- If the DB is disabled, constructor-defined proxy traffic can still run, but persistent API routes, tokens, managed domains, and stored certificate state are unavailable. The embedded DNS server is also skipped entirely, because DNS authority is delegation-verified database state — the DoH routes are still generated, but nothing answers behind them.
|
|
141
141
|
- Qualifying HTTPS forward routes on port `443` are HTTP/3-augmented unless `http3.enabled === false` or the route opts out.
|
|
142
|
-
- DNS-over-HTTPS
|
|
142
|
+
- One TLS-terminating DNS-over-HTTPS socket route is generated on the first `dnsNsDomains` entry. SmartDNS accepts RFC 8484 requests on `/dns-query` and the compatibility `/resolve` alias after TLS termination.
|
|
143
143
|
- Email listener ports can be remapped internally, for example public `25`, `587`, and `465` to unprivileged internal ports.
|
|
144
144
|
- `emailOutboundMode: 'remoteIngress'` requires an enabled RemoteIngress hub and an eligible connected QUIC edge with SMTP egress enabled. If no eligible edge is available, outbound delivery fails or defers instead of silently falling back to direct SMTP from the hub.
|
|
145
145
|
|
|
@@ -214,11 +214,13 @@ the running DNS server's authoritative zone set, its generated apex NS records,
|
|
|
214
214
|
route certificate warnings, and the private-route overlay together.
|
|
215
215
|
|
|
216
216
|
**Cold start.** A database with no authority document is a legitimate state, and
|
|
217
|
-
it means dcrouter
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
217
|
+
it means dcrouter claims no configured zone and REFUSES ordinary unhandled
|
|
218
|
+
queries. RFC 6761 `localhost` answers and the explicitly non-authoritative
|
|
219
|
+
private-route overlay remain deliberate exceptions. The DNS server still starts
|
|
220
|
+
— so DoH keeps serving and a zone verified a moment later takes effect
|
|
221
|
+
immediately — and the condition is logged at `error` alongside a startup drift
|
|
222
|
+
audit listing every dcrouter-hosted zone delegated to us that is not being
|
|
223
|
+
served.
|
|
222
224
|
An authority document that cannot be *read* is treated differently: the set is
|
|
223
225
|
unknown rather than empty, so the DNS services fail and retry instead of quietly
|
|
224
226
|
revoking every zone. dcrouter itself still comes up — both services are optional
|
package/third-party-notices.md
CHANGED
|
@@ -12,6 +12,19 @@ obtain one at <https://mozilla.org/MPL/2.0/>.
|
|
|
12
12
|
|
|
13
13
|
Source: <https://github.com/web-push-libs/web-push>
|
|
14
14
|
|
|
15
|
+
## mediabunny
|
|
16
|
+
|
|
17
|
+
`mediabunny` 1.54.0 is used transitively through
|
|
18
|
+
`@design.estate/dees-wcctools` under the Mozilla Public License 2.0.
|
|
19
|
+
|
|
20
|
+
Author: Vanilagy
|
|
21
|
+
|
|
22
|
+
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
23
|
+
v. 2.0. If a copy of the MPL was not distributed with this file, you can
|
|
24
|
+
obtain one at <https://mozilla.org/MPL/2.0/>.
|
|
25
|
+
|
|
26
|
+
Source: <https://github.com/Vanilagy/mediabunny/tree/v1.54.0>
|
|
27
|
+
|
|
15
28
|
## http_ece
|
|
16
29
|
|
|
17
30
|
`http_ece` is used transitively through `web-push`.
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -966,11 +966,6 @@ export class DcRouter {
|
|
|
966
966
|
await this.dnsServerRuntime.setup();
|
|
967
967
|
})
|
|
968
968
|
.withStop(async () => {
|
|
969
|
-
if (this.dnsServer) {
|
|
970
|
-
this.dnsServer.removeAllListeners();
|
|
971
|
-
await this.dnsServer.stop();
|
|
972
|
-
this.dnsServer = undefined;
|
|
973
|
-
}
|
|
974
969
|
await this.dnsServerRuntime.stop();
|
|
975
970
|
})
|
|
976
971
|
.withRetry({ maxRetries: 3, baseDelayMs: 2000, maxDelayMs: 30_000 }),
|
|
@@ -1785,36 +1780,21 @@ export class DcRouter {
|
|
|
1785
1780
|
}
|
|
1786
1781
|
|
|
1787
1782
|
const includeSocketHandler = options?.includeSocketHandler !== false;
|
|
1788
|
-
const dnsRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
|
1789
|
-
|
|
1790
|
-
// Create routes for DNS-over-HTTPS paths
|
|
1791
|
-
const dohPaths = ['/dns-query', '/resolve'];
|
|
1792
|
-
|
|
1793
|
-
// Use the first nameserver domain for DoH routes
|
|
1794
1783
|
const primaryNameserver = this.options.dnsNsDomains[0];
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
},
|
|
1804
|
-
|
|
1805
|
-
? {
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
: {
|
|
1810
|
-
type: 'socket-handler' as any,
|
|
1811
|
-
} as any
|
|
1812
|
-
};
|
|
1813
|
-
|
|
1814
|
-
dnsRoutes.push(dohRoute);
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
return dnsRoutes;
|
|
1784
|
+
return [{
|
|
1785
|
+
name: 'dns-over-https-dns-query',
|
|
1786
|
+
match: {
|
|
1787
|
+
ports: [443],
|
|
1788
|
+
domains: [primaryNameserver],
|
|
1789
|
+
},
|
|
1790
|
+
action: {
|
|
1791
|
+
type: 'socket-handler',
|
|
1792
|
+
tls: { mode: 'terminate', certificate: 'auto' },
|
|
1793
|
+
...(includeSocketHandler
|
|
1794
|
+
? { socketHandler: this.dnsServerRuntime.createSocketHandler() }
|
|
1795
|
+
: {}),
|
|
1796
|
+
},
|
|
1797
|
+
}];
|
|
1818
1798
|
}
|
|
1819
1799
|
|
|
1820
1800
|
/**
|
|
@@ -1895,6 +1875,10 @@ export class DcRouter {
|
|
|
1895
1875
|
// non-cancelling per-service timeout. Drain it before dependency teardown.
|
|
1896
1876
|
await this.mailDnsSync.stop();
|
|
1897
1877
|
|
|
1878
|
+
// DNS process termination must be confirmed before ServiceManager's
|
|
1879
|
+
// non-cancelling timeout can return control to the CLI's process exit.
|
|
1880
|
+
await this.dnsServerRuntime.stop();
|
|
1881
|
+
|
|
1898
1882
|
// ServiceManager handles reverse-dependency-ordered shutdown
|
|
1899
1883
|
await this.serviceManager.stop();
|
|
1900
1884
|
|