dns: HTTPS and SVCB records for ALPN (DNS slice 7) #71

Open
opened 2026-10-05 21:07:16 +00:00 by orion · 6 comments
Collaborator

Context

RFC 9460: the client takes alpn, port and the address hints of an origin. The records themselves are built (Https and Svcb with ServiceParameter in std/dns, DNS.md slice 1); ALPN in std/tls is built (#118). What is missing is the client side: Client has no resolver option, so it cannot ask for an HTTPS record at all.

This is an epic: it needs a shared resolver abstraction and a client option first (DNS.md section 7 names it as "the next step"), which is its own PR, then the record handling.

Source

Steps

  • 1. The client connects through a resolver: trait shared by Resolver, TlsResolver, DohResolver, Client option #525 (landed in PR #527)
  • 2. The client reads the HTTPS record of an origin #558
  • 3. h2 joins the offered protocols (after #72; otherwise only http/1.1 is offered)

Step 2 (filed as #558, decided by the loop)

For an https origin and a client with a resolver:

  • Ask for the HTTPS record of the host (_<port>._https.<host> where the port is not 443, RFC 9460 section 9.5). An AliasMode record (priority 0) is followed to its target, at most 8 hops; ServiceMode records are tried by ascending priority. No record, a failed lookup or an unusable record means today's behaviour, never a failure of the request.
  • port replaces the origin's port for the connection (not the Host header); alpn (plus the default http/1.1 unless no-default-alpn) is intersected with what the client supports and offered through TlsSettings.protocols; ipv4hint and ipv6hint are used only when A/AAAA answer nothing (RFC 9460 section 7.3). ech and other keys are ignored (own issues).
  • Acceptance: a test name server and an HTTPS server in one program (as dns-over-https does) prove each rule above in VM and --native; Mandatory keys the client does not know make the record unusable.

Design record / seed

Implements DNS.md slice 7 and section 7. Seed: no (std only, no syntax, no native, no string lookup by the compiler).

Dependencies

#118 (tls: ALPN) is closed and landed. Step 3 depends on #72. No ready on the epic itself; each step is its own ready issue.

Moved from the planning lists of the docs; the design record stays the specification.

## Context RFC 9460: the client takes `alpn`, `port` and the address hints of an origin. The records themselves are built (`Https` and `Svcb` with `ServiceParameter` in `std/dns`, DNS.md slice 1); ALPN in `std/tls` is built (#118). What is missing is the client side: `Client` has no resolver option, so it cannot ask for an `HTTPS` record at all. This is an epic: it needs a shared resolver abstraction and a client option first (DNS.md section 7 names it as "the next step"), which is its own PR, then the record handling. ## Source - [DNS.md slice 7](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/DNS.md) (RFC 9460); `h2` waits for HTTP/2 (#72, NETWORK.md slice 12). ## Steps - [x] 1. The client connects through a resolver: trait shared by `Resolver`, `TlsResolver`, `DohResolver`, `Client` option #525 (landed in PR #527) - [ ] 2. The client reads the `HTTPS` record of an origin #558 - [ ] 3. `h2` joins the offered protocols (after #72; otherwise only `http/1.1` is offered) ## Step 2 (filed as #558, decided by the loop) For an `https` origin and a client with a resolver: - Ask for the `HTTPS` record of the host (`_<port>._https.<host>` where the port is not 443, RFC 9460 section 9.5). An `AliasMode` record (priority 0) is followed to its target, at most 8 hops; `ServiceMode` records are tried by ascending priority. No record, a failed lookup or an unusable record means today's behaviour, never a failure of the request. - `port` replaces the origin's port for the connection (not the `Host` header); `alpn` (plus the default `http/1.1` unless `no-default-alpn`) is intersected with what the client supports and offered through `TlsSettings.protocols`; `ipv4hint` and `ipv6hint` are used only when `A`/`AAAA` answer nothing (RFC 9460 section 7.3). `ech` and other keys are ignored (own issues). - Acceptance: a test name server and an HTTPS server in one program (as `dns-over-https` does) prove each rule above in VM and `--native`; `Mandatory` keys the client does not know make the record unusable. ## Design record / seed Implements DNS.md slice 7 and section 7. Seed: no (std only, no syntax, no native, no string lookup by the compiler). ## Dependencies #118 (tls: ALPN) is closed and landed. Step 3 depends on #72. No `ready` on the epic itself; each step is its own `ready` issue. _Moved from the planning lists of the docs; the design record stays the specification._
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), source DNS.md slice 7 (RFC 9460), not a duplicate. Depends on #118 (TLS ALPN). next: waiting (dependency #118) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), source DNS.md slice 7 (RFC 9460), not a duplicate. Depends on #118 (TLS ALPN). next: waiting (dependency #118) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261027 until 2026-10-27T05:00Z

[loop:torbscript] claim refine by session:loop-torbscript-20261027 until 2026-10-27T05:00Z
Author
Collaborator

[loop:torbscript] decided by the loop: slice 7 is larger than one PR (no client resolver option exists, DNS.md section 7 lists it as the next step), so #71 is an epic with a checklist; only step 1 is filed (#525, ready, dependency #118 closed). Step 2 is filed when step 1 has landed; h2 waits for #72, until then only http/1.1 is offered. Seed impact: none.
[loop:torbscript] refine: epic body rewritten to the definition of ready, step 1 filed as #525 with ready, next: waiting (step #525). Tokens: ~60000

[loop:torbscript] decided by the loop: slice 7 is larger than one PR (no client resolver option exists, DNS.md section 7 lists it as the next step), so #71 is an epic with a checklist; only step 1 is filed (#525, ready, dependency #118 closed). Step 2 is filed when step 1 has landed; `h2` waits for #72, until then only `http/1.1` is offered. Seed impact: none. [loop:torbscript] refine: epic body rewritten to the definition of ready, step 1 filed as #525 with `ready`, next: waiting (step #525). Tokens: ~60000
Author
Collaborator

[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T01:13:11Z

[loop:torbscript] claim refine by session:loop-torbscript-20261029 until 2026-10-29T01:13:11Z
Author
Collaborator

[loop:torbscript] decided by the loop: step 1 (#525, PR #527) is on main (NameResolver in std/network, Client.resolver, conformance test http-client-resolver) and main CI is green on its head, so step 2 is unblocked and filed just in time as #558 with ready (rules as planned in the epic body; keyword search of open issues for HTTPS/SVCB/resolver found no duplicate; backlog gate off). Step 3 (h2) stays blocked on #72 (HTTP/2, open); until then only http/1.1 is offered, which #558 accounts for. Seed impact: none.
[loop:torbscript] refine: step 1 ticked and step 2 linked in the epic checklist, next: waiting (step #558, then step 3 after #72). Tokens: ~45000

[loop:torbscript] decided by the loop: step 1 (#525, PR #527) is on `main` (`NameResolver` in `std/network`, `Client.resolver`, conformance test `http-client-resolver`) and `main` CI is green on its head, so step 2 is unblocked and filed just in time as #558 with `ready` (rules as planned in the epic body; keyword search of open issues for HTTPS/SVCB/resolver found no duplicate; backlog gate off). Step 3 (`h2`) stays blocked on #72 (HTTP/2, open); until then only `http/1.1` is offered, which #558 accounts for. Seed impact: none. [loop:torbscript] refine: step 1 ticked and step 2 linked in the epic checklist, next: waiting (step #558, then step 3 after #72). Tokens: ~45000
Author
Collaborator

[loop:torbscript] wait until 2026-11-06T04:00Z: step 2 (#558) is closed; step 3 (h2) stays blocked on #72 (HTTP/2, still open).
next: waiting (step 3 after #72)

[loop:torbscript] wait until 2026-11-06T04:00Z: step 2 (#558) is closed; step 3 (h2) stays blocked on #72 (HTTP/2, still open). next: waiting (step 3 after #72)
Sign in to join this conversation.
No labels
loop
ready
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#71
No description provided.