@push.rocks/smartdns 8.2.1 → 8.4.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 (70) hide show
  1. package/.smartconfig.json +10 -4
  2. package/dist_rust/rustdns-client_linux_amd64_musl +0 -0
  3. package/dist_rust/rustdns-client_linux_amd64_musl.tsrust-build.json +14 -0
  4. package/dist_rust/{rustdns-client_linux_arm64 → rustdns-client_linux_arm64_musl} +0 -0
  5. package/dist_rust/rustdns-client_linux_arm64_musl.tsrust-build.json +14 -0
  6. package/dist_rust/rustdns_linux_amd64_musl +0 -0
  7. package/dist_rust/rustdns_linux_amd64_musl.tsrust-build.json +14 -0
  8. package/dist_rust/rustdns_linux_arm64_musl +0 -0
  9. package/dist_rust/rustdns_linux_arm64_musl.tsrust-build.json +14 -0
  10. package/dist_ts/00_commitinfo_data.js +1 -1
  11. package/dist_ts_client/classes.rustdnsclientbridge.js +4 -1
  12. package/dist_ts_server/classes.dnsserver.d.ts +1 -1
  13. package/dist_ts_server/classes.dnsserver.js +2 -2
  14. package/dist_ts_server/classes.manageddnsserver.d.ts +34 -0
  15. package/dist_ts_server/classes.manageddnsserver.js +141 -0
  16. package/dist_ts_server/classes.rustdnsbridge.d.ts +37 -1
  17. package/dist_ts_server/classes.rustdnsbridge.js +47 -14
  18. package/dist_ts_server/index.d.ts +2 -0
  19. package/dist_ts_server/index.js +3 -1
  20. package/dist_ts_server/interfaces.managed.d.ts +79 -0
  21. package/dist_ts_server/interfaces.managed.js +2 -0
  22. package/notices/cargo-dependencies.html +3412 -0
  23. package/notices/cfg_aliases-0.2.1-license.txt +9 -0
  24. package/notices/cfg_aliases-0.2.1-notices.txt +26 -0
  25. package/notices/compiler-builtins-license.txt +275 -0
  26. package/notices/fiat-crypto-0.2.9-copyright.txt +7 -0
  27. package/notices/group-0.13.0-copyright.txt +14 -0
  28. package/notices/inventory.json +1290 -0
  29. package/notices/llvm-libunwind-license.txt +311 -0
  30. package/notices/lru-slab-0.1.2-license.txt +7 -0
  31. package/notices/musl-copyright.txt +193 -0
  32. package/notices/quinn-0.11.9-license.txt +7 -0
  33. package/notices/quinn-proto-0.11.13-license.txt +7 -0
  34. package/notices/quinn-udp-0.5.14-license.txt +7 -0
  35. package/notices/rand-0.8.5-copyright.txt +12 -0
  36. package/notices/rand-0.9.2-copyright.txt +12 -0
  37. package/notices/rand_chacha-0.3.1-copyright.txt +12 -0
  38. package/notices/rand_chacha-0.9.0-copyright.txt +12 -0
  39. package/notices/rand_core-0.6.4-copyright.txt +12 -0
  40. package/notices/rand_core-0.9.5-copyright.txt +12 -0
  41. package/notices/ring-0.17.14-fiat-license.txt +15 -0
  42. package/notices/ring-0.17.14-license-boringssl.txt +272 -0
  43. package/notices/ring-0.17.14-license-other-bits.txt +13 -0
  44. package/notices/ring-0.17.14-license.txt +9 -0
  45. package/notices/ring-0.17.14-once-cell-license-apache.txt +201 -0
  46. package/notices/ring-0.17.14-once-cell-license-mit.txt +23 -0
  47. package/notices/ring-0.17.14-source-attributions.txt +1544 -0
  48. package/notices/rust-standard-library.html +8266 -0
  49. package/notices/rustc-hash-2.1.1-license.txt +23 -0
  50. package/notices/thiserror-2.0.18-license.txt +23 -0
  51. package/notices/thiserror-impl-2.0.18-license.txt +23 -0
  52. package/notices/tinyvec-1.10.0-license.txt +5 -0
  53. package/notices/tinyvec_macros-0.1.1-license.txt +21 -0
  54. package/notices/utf8_iter-1.0.4-copyright.txt +42 -0
  55. package/notices/valuable-0.1.1-license.txt +25 -0
  56. package/notices/web-time-1.1.0-license.txt +21 -0
  57. package/notices/zerocopy-derive-0.8.39-license.txt +26 -0
  58. package/package.json +18 -8
  59. package/readme.md +186 -1
  60. package/third-party-notices.md +96 -0
  61. package/ts/00_commitinfo_data.ts +1 -1
  62. package/dist_rust/rustdns-client_linux_amd64 +0 -0
  63. package/dist_rust/rustdns-client_linux_amd64.tsrust-build.json +0 -14
  64. package/dist_rust/rustdns-client_linux_arm64.tsrust-build.json +0 -14
  65. package/dist_rust/rustdns_linux_amd64 +0 -0
  66. package/dist_rust/rustdns_linux_amd64.tsrust-build.json +0 -14
  67. package/dist_rust/rustdns_linux_arm64 +0 -0
  68. package/dist_rust/rustdns_linux_arm64.tsrust-build.json +0 -14
  69. package/readme.hints.md +0 -294
  70. /package/{license → license.md} +0 -0
@@ -1,14 +0,0 @@
1
- {
2
- "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "b2d37952a49874fc35b8ee7bdd5d4c794458dd93e1f2c315425351aa111f2c49",
4
- "buildInfo": {
5
- "projectName": "@push.rocks/smartdns",
6
- "projectVersion": "8.2.1",
7
- "gitCommit": "81f8e6aa1589378fc9883a3038bc8e39f6db6be4",
8
- "gitDirty": false,
9
- "builtAt": "2026-08-23T09:52:00.225Z",
10
- "tsrustVersion": "1.10.0",
11
- "binary": "rustdns",
12
- "target": "linux_arm64"
13
- }
14
- }
package/readme.hints.md DELETED
@@ -1,294 +0,0 @@
1
- # smartdns - Implementation Hints
2
-
3
- ## Architecture Overview
4
-
5
- The smartdns library is structured into three main modules:
6
-
7
- 1. **Client Module** (`ts_client/`) - DNS client functionality
8
- 2. **Server Module** (`ts_server/`) - DNS server with Rust backend
9
- 3. **Main Module** (`ts/`) - Re-exports both client and server
10
- 4. **Rust Module** (`rust/`) - High-performance DNS server binary
11
-
12
- ## Rust Backend Architecture (v8.0+)
13
-
14
- The DNS server's default UDP, TCP, and HTTPS network I/O, packet
15
- parsing/encoding, and DNSSEC signing run in Rust. TypeScript retains handler
16
- registration, ACME orchestration, and the public API. `handleHttpSocket()` is
17
- the deliberate manual-transport exception: after another component terminates
18
- TLS, TypeScript bounds and parses HTTP/1.1 or prior-knowledge HTTP/2, then sends
19
- the contained DNS packet to Rust for resolution and response construction.
20
-
21
- ### Rust Crate Structure
22
- - `rustdns` - Main binary with IPC management loop (`--management` flag)
23
- - `rustdns-protocol` - DNS wire format parsing/encoding, record types
24
- - `rustdns-server` - Async UDP + HTTPS DoH servers (tokio, hyper, rustls)
25
- - `rustdns-dnssec` - ECDSA P-256 / ED25519 key generation and RRset signing
26
-
27
- ### IPC Flow
28
- ```
29
- DNS Query -> Rust (UDP/HTTPS) -> Parse packet
30
- -> Try local resolution (localhost)
31
- -> If handler needed: emit "dnsQuery" event to TypeScript
32
- -> TypeScript runs minimatch handlers and authorizes DNSKEY, sends "dnsQueryResult" back
33
- -> Rust builds response, signs DNSSEC if requested, sends packet
34
- ```
35
-
36
- ### Key Files
37
- - `ts_server/classes.rustdnsbridge.ts` - TypeScript IPC bridge wrapping smartrust.RustBridge
38
- - `ts_server/classes.dnsserver.ts` - DnsServer class (public API, delegates to Rust bridge)
39
- - `rust/crates/rustdns/src/management.rs` - IPC management loop
40
- - `rust/crates/rustdns/src/resolver.rs` - DNS resolver with callback support
41
-
42
- ## Client Module (Smartdns class)
43
-
44
- ### Key Features:
45
- - DNS record queries (A, AAAA, TXT, MX, etc.)
46
- - Support for multiple DNS providers (Google DNS, Cloudflare)
47
- - DNS propagation checking with retry logic
48
- - DNSSEC verification support
49
- - Both HTTP-based (DoH) and Node.js DNS resolver fallback
50
-
51
- ### Implementation Details:
52
- - Uses Cloudflare's DNS-over-HTTPS API as primary resolver
53
- - Falls back to Node.js DNS module for local resolution
54
- - Implements automatic retry logic with configurable intervals
55
- - Properly handles quoted TXT records and trailing dots in domain names
56
-
57
- ### Key Methods:
58
- - `getRecordsA()`, `getRecordsAAAA()`, `getRecordsTxt()` - Type-specific queries
59
- - `getRecords()` - Generic record query with retry support
60
- - `checkUntilAvailable()` - DNS propagation verification
61
- - `getNameServers()` - NS record lookup
62
- - `makeNodeProcessUseDnsProvider()` - Configure system DNS resolver
63
-
64
- ## Server Module (DnsServer class)
65
-
66
- ### Key Features:
67
- - Full DNS server supporting UDP and HTTPS (DoH) protocols
68
- - DNSSEC implementation with multiple algorithms
69
- - Dynamic handler registration for custom responses
70
- - Let's Encrypt integration for automatic SSL certificates
71
- - Wildcard domain support with pattern matching
72
-
73
- ### DNSSEC Implementation:
74
- - Supports ECDSA (algorithm 13), ED25519 (algorithm 15), and RSA (algorithm 8)
75
- - Automatic DNSKEY and DS record generation
76
- - RRSIG signature generation for authenticated responses
77
- - Key tag computation following RFC 4034
78
-
79
- ### Let's Encrypt Integration:
80
- - Automatic SSL certificate retrieval using DNS-01 challenges
81
- - Dynamic TXT record handler registration for ACME validation
82
- - Certificate renewal and HTTPS server restart capability
83
- - Domain authorization filtering for security
84
-
85
- ### Handler System:
86
- - Pattern-based domain matching using minimatch
87
- - Support for all common record types
88
- - **Multiple Handler Support**: As of v7.4.2+, multiple handlers can contribute records of the same type
89
- - Handler chaining for complex scenarios
90
- - Automatic SOA response for unhandled queries
91
-
92
- ### Multiple Records Support (v7.4.2+):
93
- - Server now processes ALL matching handlers for a query (previously stopped after first match)
94
- - Enables proper multi-NS record support for domain registration
95
- - Supports round-robin DNS with multiple A/AAAA records
96
- - Allows multiple TXT records (SPF, DKIM, domain verification)
97
- - Each handler contributes its record to the response
98
-
99
- ## Key Dependencies
100
-
101
- - `dns-packet`: DNS packet encoding/decoding (wire format, used by TS fallback path)
102
- - `acme-client`: Let's Encrypt certificate automation
103
- - `minimatch`: Glob pattern matching for domains
104
- - `@push.rocks/smartrust`: TypeScript-to-Rust IPC bridge
105
- - `@tsclass/tsclass`: Type definitions for DNS records
106
-
107
- ## Testing Insights
108
-
109
- The test suite demonstrates:
110
- - Mock ACME client for testing Let's Encrypt integration
111
- - Self-signed certificate generation for HTTPS testing
112
- - Unique port allocation to avoid conflicts
113
- - Proper server cleanup between tests
114
- - Both UDP and HTTPS query validation
115
-
116
- ## Common Patterns
117
-
118
- 1. **DNS Record Types**: Internally mapped to numeric values (A=1, AAAA=28, etc.)
119
- 2. **Error Handling**: Graceful fallback and retry mechanisms
120
- 3. **DNSSEC Workflow**: Zone → Key Generation → Signing → Verification
121
- 4. **Certificate Flow**: Domain validation → Challenge setup → Verification → Certificate retrieval
122
-
123
- ## Performance Considerations
124
-
125
- - Client implements caching via DNS-over-HTTPS responses
126
- - Server can handle concurrent UDP and HTTPS requests
127
- - DNSSEC signing is performed on-demand for efficiency
128
- - Handler registration is O(n) lookup but uses pattern caching
129
-
130
- ## Security Notes
131
-
132
- - DNSSEC provides authentication but not encryption
133
- - DoH (DNS-over-HTTPS) provides both privacy and integrity
134
- - Let's Encrypt integration requires proper domain authorization
135
- - Handler patterns should be carefully designed to avoid open resolvers
136
-
137
- ## Recent Improvements (v7.4.3)
138
-
139
- 1. **DNSSEC RRset Signing**: Fixed to properly sign entire RRsets together instead of individual records
140
- 2. **SOA Record Serialization**: Implemented proper SOA record encoding for DNSSEC compatibility
141
- 3. **Configurable Primary Nameserver**: Added `primaryNameserver` option to customize SOA mname field
142
-
143
- ## Recent Improvements (v7.4.8)
144
-
145
- 1. **MX Record DNSSEC Support**: Implemented MX record serialization for DNSSEC signing
146
- - MX records consist of a 16-bit preference value followed by the exchange domain name
147
- - Properly serializes both components for DNSSEC signature generation
148
- - Enables mail exchange records to be authenticated with DNSSEC
149
-
150
- ## Authority Model (server)
151
-
152
- Authority comes from **configuration**, never from the presence of a handler.
153
-
154
- - `authoritativeZones` is the single source of truth for the `aa` bit. Omission
155
- falls back to `[dnssecZone]`; an explicit empty array means no authority.
156
- `setAuthoritativeZones()` validates and copies a replacement set synchronously,
157
- so listeners stay up while subsequent queries see the new authority.
158
- - DNSKEY queries for `dnssecZone` cross the TypeScript query callback before
159
- Rust synthesizes the signed RRset. This keeps built-in DNSSEC answers inside
160
- the same live authority boundary; an empty set cannot leak an authoritative
161
- DNSKEY answer from the fixed Rust configuration.
162
- - `registerHandler(pattern, types, handler, options?)` takes
163
- `options.authority`:
164
- - `'authoritative'` (default) — the handler only contributes for names inside
165
- a configured zone. A match outside every zone is **suppressed**, not
166
- answered, and reported on the `refused` event. A registration that cannot
167
- match any configured zone also logs a warning once, at registration time.
168
- - `'non-authoritative'` — the handler may answer any matching name, but the
169
- response never carries `aa` and is never DNSSEC-signed. This is the mode
170
- for split-horizon / private-route overlays.
171
- - `aa` is a per-message flag, so a single non-authoritative contribution
172
- downgrades the whole response.
173
-
174
- Why this matters: before v8, registering a handler *was* the public claim of
175
- authority. A private-route overlay registered for a domain delegated elsewhere
176
- made the public nameserver answer with `aa` and an RFC1918 address, while
177
- refusing every other query type for the same name. Both halves came from the
178
- same defect — two disagreeing sources of truth.
179
-
180
- ### Response outcomes
181
-
182
- `TDnsQueryResponseKind` maps one-to-one onto the wire:
183
-
184
- | kind | rcode | `aa` | authority section |
185
- | --- | --- | --- | --- |
186
- | `answer` | 0 | yes | — |
187
- | `nonAuthoritativeAnswer` | 0 | no | — |
188
- | `authoritativeNegative` (NODATA) | 0 | yes | zone SOA |
189
- | `authoritativeNameError` (NXDOMAIN) | 3 | yes | zone SOA |
190
- | `refused` | 5 | no | — |
191
- | `servfail` | 2 | no | — |
192
-
193
- NXDOMAIN is only emitted when the name provably does not exist: no handler
194
- pattern matches it for any type, it is not the apex of a configured zone, no
195
- configured zone sits below it, and no handler pattern is anchored strictly
196
- below it (literal `.name` suffix, or a wildcard that matches a probe label —
197
- mid-label wildcards such as `x.*.example.com` are not fully inverted by this
198
- check and can still leave an ancestor at NXDOMAIN). The apex always exists
199
- (it answers its own SOA), and a name with covered descendants is an empty
200
- non-terminal — both get NODATA, because NXDOMAIN denies the entire subtree
201
- (RFC 8020) and a wrong one lets aggressive negative caching darken the zone.
202
- A catch-all pattern such as `'*'` therefore suppresses NXDOMAIN entirely —
203
- that is deliberate.
204
-
205
- The zone apex answers its own SOA even with no registered SOA handler; an
206
- authoritative server that cannot answer SOA for its own zone breaks negative
207
- caching and resolver heuristics.
208
-
209
- One deliberate exception to the table: RFC 6761 localhost handling (when
210
- `enableLocalhostHandling` is on) answers `localhost` A/AAAA with `aa` set even
211
- though `localhost` is outside every configured zone — special-use names are
212
- authoritative by definition, not by delegation.
213
-
214
- ## Handler Lifecycle
215
-
216
- - `registerHandler` returns an `IDnsHandlerRegistration` with a unique `id` and
217
- an `unregister()` that removes **only** that registration.
218
- - `unregisterHandlerById(id)` and `unregisterHandlersByOwner(owner)` are the
219
- precise teardown primitives; `listHandlers()` exposes the current set for
220
- leak checks.
221
- - `unregisterHandler(pattern, types)` is retained and unchanged: it still
222
- removes *every* handler matching that pattern/type pair. It cannot distinguish
223
- two consumers sharing a key — prefer the handle.
224
- - ACME authorization follows the same rule: `filterAuthorizedDomains` /
225
- `retrieveSslCertificate` admit exactly the domains inside a configured
226
- authoritative zone. Handler registrations play no part — a DNS-01 challenge
227
- for an out-of-zone domain could never validate, because its challenge TXT
228
- handler would be suppressed.
229
-
230
- ## Transport And Protocol Notes
231
-
232
- - **EDNS0**: a request carrying an OPT record always gets an OPT back
233
- (RFC 6891 6.1.1), advertising `DNS_ADVERTISED_UDP_PAYLOAD` (1232). Requests
234
- without OPT get no OPT. Client-advertised payload sizes are clamped up to 512
235
- so a hostile OPT cannot force pathological truncation.
236
- - **Truncation**: UDP responses are truncated to the client's advertised payload
237
- size with the TC bit set, so resolvers retry over TCP. Without this, oversized
238
- datagrams were emitted and silently dropped by real networks. Truncation is
239
- size arithmetic, never a re-encode per dropped record — the encoder does not
240
- compress names, so record sizes are additive (`DnsPacket::encoded_len`).
241
- `processRawDnsPacketAsync(packet, 'udp')` declares the same UDP bound for the
242
- manual path (`handleUdpMessage` passes it automatically); without the hint no
243
- size bound is applied, which is correct for TCP and DoH framing. Manual packet
244
- work runs through a bounded task set so the management loop remains available
245
- for handler callbacks and their results. The TypeScript bridge mirrors the
246
- Rust task bound by admitting at most 256 pending `processPacket` requests;
247
- excess requests fail before serialization with
248
- `ERR_RUST_BRIDGE_REQUEST_LIMIT`, while control methods remain unbounded.
249
- - **RA** is cleared on every path — this server never recurses.
250
- - **RDATA validation** fails closed: a handler returning an unencodable value
251
- (for example a non-IPv4 string for an A record) produces SERVFAIL and an error
252
- log, rather than a malformed record on the wire.
253
- - **SOA serial** is fixed for the lifetime of the server process and passed to
254
- Rust via `soaSerial`, so apex answers and negative authorities agree.
255
-
256
- ## Observability
257
-
258
- - `query` events carry `responseKind`, `authorityZone` and `suppressedHandlers`.
259
- - `refused` events fire for every refusal and every suppressed handler match,
260
- carrying the handler `id`, `domainPattern` and `owner`. Wire this up: the
261
- original incident was invisible for weeks because nothing surfaced it.
262
-
263
- ## Server Lifecycle
264
-
265
- - `start()` and `stop()` share a serialized lifecycle queue. Each caller keeps
266
- its own operation result, and a rejection does not poison later operations.
267
- - Repeated `start()` calls are idempotent at the `DnsServer` facade. Rust also
268
- rejects duplicate active `start` commands, so low-level bridge callers cannot
269
- replace listeners while detached HTTPS connection work still exists.
270
- - `stop()` awaits confirmed Rust child closure before clearing ownership and
271
- legacy server markers. A failed termination retains them so cleanup can be
272
- retried safely.
273
- - If an operation and confirmed cleanup both fail, both errors are preserved in
274
- an `AggregateError`. A failed stop command is suppressed only after process
275
- closure has been confirmed.
276
- - A server may be started again after a confirmed stop; smartrust activates a
277
- fresh process generation on the same bridge.
278
- - Packet IPC is admitted only while no lifecycle operation is queued and the
279
- facade state, ownership marker, and bridge liveness all agree. Certificate
280
- updates use the same queue so they cannot race startup or shutdown.
281
-
282
- ## Known Limitations
283
-
284
- 1. **Handler Deduplication**: If the same handler is registered multiple times, it will contribute duplicate records (this may be desired behavior for some use cases)
285
- 2. **NXDOMAIN under catch-all patterns**: see the authority model above.
286
- 3. **SOA serial does not track zone changes** — it is a process-lifetime
287
- constant, not a change counter. Secondaries driven by AXFR/IXFR are not
288
- supported.
289
- ## Static Rust Binaries
290
-
291
- - The Rust binaries in `dist_rust/` are statically linked (static-pie) via `"static": true` in the `@git.zone/tsrust` block of `.smartconfig.json` (tsrust >= 1.4.1). They run on both glibc (Debian/Ubuntu) and musl (Alpine) systems.
292
- - tsrust injects `RUSTFLAGS="-C target-feature=+crt-static"` only into its own per-target cargo invocations and verifies the result (no `PT_INTERP` ELF header). Keep `rust/.cargo/config.toml` free of `rustflags` entries: the injected env variable would replace them, and a repo-wide `rustflags` would also break plain `cargo test`/`cargo check` (proc-macros cannot build with `+crt-static` on linux-gnu without an explicit `--target`).
293
- - `pnpm run test:rust` runs the Rust unit tests; plain `cargo test` in `rust/` works as well.
294
- - Verify linkage manually with `ldd dist_rust/<binary>_linux_amd64` → "statically linked".
File without changes