tls: ALPN #118
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
HTTP/2, gRPC, Postgres 17's direct TLS. Listed among what
stdis missing for the first-party packages.Source
Acceptance criteria
Dependencies
None known.
Moved from the planning lists of the docs; the design record stays the specification.
[loop:torbscript] intake: in scope (author orion, label loop), source FIRST-PARTY.md gap 20, not a duplicate. No dependencies. next: waiting (refine queued, ready buffer full) until 2026-10-10T04:00:00Z
[loop:torbscript] claim refine by session:loop-torbscript-20261024 until 2026-10-24T07:39:00Z
[loop:torbscript] refine: requirements for #118 (read-only analysis of main
5d7f790; source FIRST-PARTY.md gap 20, NETWORK.md section 5 "TLS, as built")State today.
std/tls/src/lib.trbhasTlsSettings(trusted),ServerIdentity,TlsStream.connect/acceptover twelve natives ofruntime/tls/tls.c(declared inruntime/include/torb_tls.h, manifest incompiler/src/backend/c/natives.trbtlsEntries). mbedTLS 3.6.7 is vendored withMBEDTLS_SSL_ALPNalready on, and no ALPN call is made anywhere. Postgres 17 direct TLS,h2and so gRPC (gaps 20, 25; #72) wait on this.Decided by the loop (internal design, additive, follows the existing shape; no owner question): ALPN is two new natives, so no existing native or
TlsClient/tlsServersignature changes. The offered list is per session and set before the handshake; a list is protocol names in preference order, crossing as text separated by\nliketrusteddoes.Seed: adds natives, so it is a "new native" in the sense of CLAUDE.md, but nothing in
compiler/srcimportsstd/tls(onlypackage/archive.trbnames it as a capability string), so the PR is two commits and no seed refresh is needed between them: stage 1 is built from the PR's own manifest.Requirements
runtime/tls/tls.c,torb_tls.h:int64_t torb_tls_offer(int64_t session, torb_text protocols): sets the session's ALPN list (client: offered; server: accepted, in server preference order) viambedtls_ssl_conf_alpn_protocols. Called before the firsttlsHandshake. The session owns a NULL-terminated copy of the names that lives untiltorb_tls_session_free. A name that is empty or longer than 255 bytes, or a call after the handshake began, answers a packedTORB_TLS_FAILED(isTlsFailure()), never truncates. Emptyprotocolsis a no-op (today's behaviour).torb_text torb_tls_negotiated(int64_t session):mbedtls_ssl_get_alpn_protocol, empty text when none was chosen.compiler/src/backend/c/natives.trb(tlsOffer,tlsNegotiated, both parttls);runtime/include/torb_natives.handruntime/machine_natives.cregenerated withtorb natives --header(never by hand); two rows indocs/design/NATIVES.md(keep, tls) so the drift test stays green.std/tls:TlsSettings.protocols: List<String> = [](client offer, preference order).TlsStream.accept(stream, identity, protocols: List<String> = [])(server accepts the first of its own list that the client offers).TlsStream.negotiatedProtocol(): String?:Some("h2")etc. after the handshake,Noneif neither side offered or the server chose none.connectandacceptcalltlsOfferright after creating the session and fail with theNetworkErrorof the native's failure. Existing callers (std/http,std/dns) compile unchanged.docs/design/NETWORK.mdsection 5 "as built" gets a short ALPN paragraph (API, no-overlap behaviour);docs/standard-library/tls.md; FIRST-PARTY.md gap 20 marked done with the names, "Until then" column updated like gaps 13/14; regenerateskills/(torb docs skill docs skills,--checkis a tier A gate).std/http's client or server, HTTP/2 (#72), HTTPS/SVCB hints (#71), the Postgressslnegotiation=directdriver (torb/postgres).Acceptance criteria
["h2", "http/1.1"]against a server accepting["http/1.1"]: both sides'negotiatedProtocol()isSome("http/1.1"); server preference wins when both match.negotiatedProtocol()isNoneon both sides and data still flows (RFC 7301).None, identical behaviour and handshake to today (existingtls-loopback,dns-over-tls,https-exchangeunchanged and green).NetworkErrorthatisTlsFailure()(no_application_protocol), on the server at least, and the client'sconnectfails or sees the alert, no hang."postgresql"offer negotiatesSome("postgresql")(the Postgres 17 case).connect/acceptwithisTlsFailure(), no truncation.--native(tier B runs the conformance suite in both).docs/standard-library/tls.mdandskills/updated and--checkgreen.Tests
runtime/tests/tls_test.c:torb_tls_offer/torb_tls_negotiatedon a client/server pair (match, no overlap, empty and 256-byte name, offer after handshake start).tests/conformance/tls-alpn.trb+.expected/.exit/.allocations(the allocation budget set from the first CI run, as fortls-loopback), reusing the test root, certificate and key oftests/conformance/tls-loopback.trb: the five negotiation cases above over loopback, both sides in one program.compilernatives drift test (bfafbd8): green with the two new rows.Commits (one PR, about 1 h)
feat(runtime): ALPN natives for TLS sessions:tls.c,torb_tls.h, manifest, regeneratedtorb_natives.handmachine_natives.c, NATIVES.md rows,tls_test.c. Says "new natives, no std change yet" like5dd2b9a.feat(tls): ALPN, offered and negotiated protocols:std/tls, conformance test, docs, FIRST-PARTY.md gap 20, regeneratedskills/.Gates:
sh tools/gates.sh a, andb(touchesruntime/), in forge CI only. Implementer: sonnet/high (runtime/). Dependencies: none (#115, #116, #117 touch the same files, rebase conflicts inlib.trb/tls.conly).[loop:torbscript] refine: ready, one PR in two commits (2 natives + std/tls API, tests, docs), decided by the loop, no owner question, next: implement
TlsStream#116[loop:torbscript] claim implement by session:loop-torbscript-20261024 until 2026-10-24T08:10:31Z
[loop:torbscript] implement: checkpoint (context guard), CONTINUE. Branch
loop/118-tls-alpnpushed (one WIP commit, to be squashed into commit 1), worktreetorbscript-language-118-alpn(ws wt).Done:
runtime/include/torb_natives.h(tlsNegotiated, tlsOffer entries) andruntime/machine_natives.c(two thunks, renumbered 300..310, table, count 311) hand-generated in the generator's sorted order (torb_tls_negotiated after _identity, torb_tls_offer after _notify_close), since there is no torb binary here.Open for commit 1:
compiler/src/backend/c/natives.trbtlsEntries(twotlsRuntimeOfrows:int64_t torb_tls_offer(int64_t session, torb_text protocols);,torb_text torb_tls_negotiated(int64_t session);),runtime/include/torb_tls.hprototypes,runtime/tls/tls.c(session getschar *alpn_names+const char **alpn_list, freed intorb_tls_session_free; reject after handshake began viambedtls_ssl_is_handshake_over/state != HELLO_REQUEST, empty or >255 byte names), NATIVES.md rows (keep, tls; counts keep 178->180? check current, total +2),runtime/tests/tls_test.c.Open for commit 2: std/tls API,
tests/conformance/tls-alpn.trb+ expected files, docs (NETWORK.md s5, standard-library/tls.md, FIRST-PARTY.md gap 20), skills/ regeneration (no torb binary here: CI--checkwill tell; regenerate needs torb), then PRCloses #118.[loop:torbscript] implement: checkpoint 2 (context guard), CONTINUE. Branch
loop/118-tls-alpn, worktreetorbscript-language-118-alpn.Done and pushed: commit 1
42c3d8c1"feat(runtime): ALPN natives for TLS sessions" is complete (tls.c, torb_tls.h, natives.trb rows, hand-generated thunks/header, NATIVES.md rows and counts 180/535, tls_test.c with 4 ALPN tests; names are newline-TERMINATED so[""]is a lone newline and an empty name,""is no list). On top: one WIP commit withstd/tls/src/lib.trbdone (TlsSettings.protocols, TlsStream.accept(..., protocols), negotiatedProtocol(), tlsOffer/tlsNegotiated natives,alpnTexthelper; connect/accept close the session on an offer failure). Squash the WIP into commit 2feat(tls): ALPN, offered and negotiated protocols.Open for commit 2:
tests/conformance/tls-alpn.trb+.expected/.exit/.allocations(model: tls-loopback.trb, which holds testRoot/serverCertificate/serverKey; the five cases + empty/256-byte name refusals),docs/standard-library/tls.md(declarations in source order:negotiatedProtocol()afterprotocol(),accept(stream, identity, protocols: List<String> = []),protocols: List<String> = []aftertrusted) with the same edits by hand inskills/torbscript-networking/references/standard-library/tls.md(only link lines differ), NETWORK.md section 5 "as built" ALPN bullet (after "No switch turns verification off", ~line 482), FIRST-PARTY.md gap 20 row 280 marked done 2026-10-24 like gap 13. Then PRCloses #118(CI-only verification, skills--checkin CI) and the closing comment +ws wt rm.[loop:torbscript] implement: PR 466 (
cd3738c6), next: waiting (forge CI ofcd3738c6)