Summary
Investigate and roll out HTTP/2 by default for NetScript services. HTTP/2 is largely "free" at the protocol layer — Deno's runtime already speaks it — but making it the default is gated almost entirely by TLS provisioning in local dev, because browsers only negotiate HTTP/2 over TLS (ALPN). This issue captures the feasibility verdict, the concrete requirements, a phased rollout, and the open questions.
Motivation
Current state (grounded)
packages/service/src/builder/service-listener.ts starts a plain Deno.serve({ port, signal, onListen }) — no cert/key, and the startup banner hardcodes http://${hostname}:${port}. The file docstring already anticipates this: "Kept separate from the builder class so the listener concern can evolve (e.g. TLS, clustering)…".
- The SDK client (
packages/sdk/src/client/http-client-link.ts / service-client.ts) and the tutorial's OpenAPILink/RPCLink are built on fetch. Deno's fetch already negotiates HTTP/2 automatically over TLS — no client code change is needed for the happy path, only trust of the dev cert.
- So the runtime pieces are present; the gap is TLS wiring + dev-cert trust + Aspire scheme, not a new HTTP/2 implementation.
Feasibility verdict
Feasible with no new dependency. Deno.serve serves HTTP/2 automatically when given a TLS cert/key and the client negotiates h2 via ALPN (Deno.serve({ cert, key, ... })). The work is plumbing and dev-ergonomics, not protocol implementation.
Key constraint: browsers do not support h2c (cleartext HTTP/2). So "h2 by default" for a browser-facing service necessarily means "TLS by default in dev." That TLS-in-dev requirement is the real cost of this issue.
Requirements
- Dev TLS certificate + local trust (the hard part). Browsers reject untrusted certs, so the generated dev experience needs a trusted localhost cert. Options to evaluate:
- Lean on Aspire dev-certs (
dotnet dev-certs https --trust) — Aspire already provisions/trusts a localhost cert for its own HTTPS endpoints; reuse it for NetScript service endpoints.
mkcert-style locally-trusted CA (extra install step).
- Self-signed + explicit trust env for non-browser clients only (
DENO_CERT / NODE_EXTRA_CA_CERTS) — insufficient for browsers.
- Cross-platform trust friction (Windows cert store vs. macOS keychain vs. linux) is the dominant UX risk.
- Service listener TLS support. Extend
ServeOptions with an optional tls: { cert, key } (or certFile/keyFile), branch startServiceListener to pass them to Deno.serve, and flip the onListen banner to https:// when TLS is active. Keep plain HTTP as a fallback (opt-out / no-cert path).
- Aspire AppHost scheme. Generated AppHost endpoints, service-discovery URLs, and health-check probes must flip
http→https when services serve TLS. (Note: the Aspire dashboard URL was already corrected to https in the docs; this is about the service endpoints.)
- Client / SDK trust.
fetch gets h2 for free over TLS, but Deno must trust the dev cert (DENO_CERT, --cert, or the OS trust store via Aspire dev-certs). Verify OpenAPILink/RPCLink and generated typed clients work end-to-end over h2.
- Fresh / Vite dev server. Separate listener from the service. Vite's dev server + HMR over HTTPS/h2 has its own config (
server.https); confirm HMR still works. Decide whether the web layer also goes TLS-by-default or stays HTTP in dev while only backend services go h2.
- Production. Essentially free: TLS typically terminates at the ingress/proxy in an Aspire-published deployment, which already speaks h2 to browsers. Internal service-to-service h2 is optional and can be a later phase.
Proposed phased rollout
Risks / open questions
Non-goals
- HTTP/3 / QUIC (separate, larger effort).
- Rewriting the client transport —
fetch already handles h2.
Filed out of the alpha.18 docs-conformance work (the streams HTTP/1.1 ~6-connection caveat is the motivating case). Tracked standalone rather than folded into docs. Related: #218, #231, #215, docs umbrella #232.
Summary
Investigate and roll out HTTP/2 by default for NetScript services. HTTP/2 is largely "free" at the protocol layer — Deno's runtime already speaks it — but making it the default is gated almost entirely by TLS provisioning in local dev, because browsers only negotiate HTTP/2 over TLS (ALPN). This issue captures the feasibility verdict, the concrete requirements, a phased rollout, and the open questions.
Motivation
Current state (grounded)
packages/service/src/builder/service-listener.tsstarts a plainDeno.serve({ port, signal, onListen })— nocert/key, and the startup banner hardcodeshttp://${hostname}:${port}. The file docstring already anticipates this: "Kept separate from the builder class so the listener concern can evolve (e.g. TLS, clustering)…".packages/sdk/src/client/http-client-link.ts/service-client.ts) and the tutorial'sOpenAPILink/RPCLinkare built onfetch. Deno'sfetchalready negotiates HTTP/2 automatically over TLS — no client code change is needed for the happy path, only trust of the dev cert.Feasibility verdict
Feasible with no new dependency.
Deno.serveserves HTTP/2 automatically when given a TLS cert/key and the client negotiatesh2via ALPN (Deno.serve({ cert, key, ... })). The work is plumbing and dev-ergonomics, not protocol implementation.Key constraint: browsers do not support h2c (cleartext HTTP/2). So "h2 by default" for a browser-facing service necessarily means "TLS by default in dev." That TLS-in-dev requirement is the real cost of this issue.
Requirements
dotnet dev-certs https --trust) — Aspire already provisions/trusts a localhost cert for its own HTTPS endpoints; reuse it for NetScript service endpoints.mkcert-style locally-trusted CA (extra install step).DENO_CERT/NODE_EXTRA_CA_CERTS) — insufficient for browsers.ServeOptionswith an optionaltls: { cert, key }(orcertFile/keyFile), branchstartServiceListenerto pass them toDeno.serve, and flip theonListenbanner tohttps://when TLS is active. Keep plain HTTP as a fallback (opt-out / no-cert path).http→httpswhen services serve TLS. (Note: the Aspire dashboard URL was already corrected to https in the docs; this is about the service endpoints.)fetchgets h2 for free over TLS, but Deno must trust the dev cert (DENO_CERT,--cert, or the OS trust store via Aspire dev-certs). VerifyOpenAPILink/RPCLinkand generated typed clients work end-to-end over h2.server.https); confirm HMR still works. Decide whether the web layer also goes TLS-by-default or stays HTTP in dev while only backend services go h2.Proposed phased rollout
Deno.serve({ cert, key })serves h2 end-to-end:curl --http2 -k, Chrome DevToolsProtocolcolumn =h2, and a Denofetchclient. Confirm the ALPN path and measure the SSE-slot win.ServeOptions.tls+ banner flip; document how to run a service over h2. No behavior change by default.Risks / open questions
fetch→ self-signed trust story for non-browser clients (CI, tests, server-to-server):DENO_CERTvs. OS store.aspire start --isolated(aspire: --isolated should randomize profile-pinned dashboard/OTLP/resource-service ports for parallel isolated apphosts #215/fix(aspire): randomize dashboard/OTLP/resource-service ports under --isolated (#215) #222) randomized ports — cert SANs / wildcard localhost.http://endpoints).Non-goals
fetchalready handles h2.Filed out of the alpha.18 docs-conformance work (the streams HTTP/1.1 ~6-connection caveat is the motivating case). Tracked standalone rather than folded into docs. Related: #218, #231, #215, docs umbrella #232.