feat(tls): ALPN offer and negotiation in std/tls #466

Merged
argus merged 4 commits from loop/118-tls-alpn into main 2026-10-24 13:33:13 +00:00
Collaborator

Closes #118

Summary

ALPN (RFC 7301) for std/tls, additive: no existing native or signature changes.

  • TlsSettings.protocols: List<String> = []: the client's offer in preference order.
  • TlsStream.accept(stream, identity, protocols: List<String> = []): the server accepts the first of its own names the client offers.
  • TlsStream.negotiatedProtocol(): String?: Some("h2") etc. after the handshake, None where none was chosen.
  • A name that is empty, over 255 bytes or holds a newline is refused by connect/accept with isTlsFailure(); no overlap ends the handshake with a TLS failure.
  • Docs: docs/standard-library/tls.md, NETWORK.md section 5, FIRST-PARTY.md gap 20 done, hand copy in skills/.

Two commits: feat(runtime): ALPN natives for TLS sessions (tls.c, torb_tls.h, manifest, NATIVES.md, runtime/tests/tls_test.c) and feat(tls): offer and read ALPN protocols (std/tls, conformance test, docs).

Verification

Nothing was run locally (no toolchain in the loop worker); everything is verified by forge CI only (tier A incl. torb docs skill ... --check, tier B conformance in VM and --native, runtime tls_test.c, natives drift test). tests/conformance/tls-alpn.expected was written by hand and may need a correction after the first CI run.

Notes for review

  • tests/conformance/tls-alpn.allocations is unbudgeted: with a reason (loopback socket I/O varies per run; no toolchain to measure).
  • torb_natives.h, machine_natives.c thunks and the NATIVES.md counts (180/535) were generated by hand, not with torb natives --header; CI's drift test is the check. Regenerate if it disagrees.
  • skills/.../tls.md was edited by hand in step with docs/standard-library/tls.md; regenerate if --check disagrees.
  • Out of scope: ALPN in std/http, HTTP/2 (#72), Postgres direct TLS.
Closes #118 ## Summary ALPN (RFC 7301) for `std/tls`, additive: no existing native or signature changes. - `TlsSettings.protocols: List<String> = []`: the client's offer in preference order. - `TlsStream.accept(stream, identity, protocols: List<String> = [])`: the server accepts the first of its own names the client offers. - `TlsStream.negotiatedProtocol(): String?`: `Some("h2")` etc. after the handshake, `None` where none was chosen. - A name that is empty, over 255 bytes or holds a newline is refused by `connect`/`accept` with `isTlsFailure()`; no overlap ends the handshake with a TLS failure. - Docs: `docs/standard-library/tls.md`, NETWORK.md section 5, FIRST-PARTY.md gap 20 done, hand copy in `skills/`. Two commits: `feat(runtime): ALPN natives for TLS sessions` (tls.c, torb_tls.h, manifest, NATIVES.md, `runtime/tests/tls_test.c`) and `feat(tls): offer and read ALPN protocols` (std/tls, conformance test, docs). ## Verification Nothing was run locally (no toolchain in the loop worker); everything is verified by forge CI only (tier A incl. `torb docs skill ... --check`, tier B conformance in VM and `--native`, runtime `tls_test.c`, natives drift test). `tests/conformance/tls-alpn.expected` was written by hand and may need a correction after the first CI run. ## Notes for review - `tests/conformance/tls-alpn.allocations` is `unbudgeted:` with a reason (loopback socket I/O varies per run; no toolchain to measure). - `torb_natives.h`, `machine_natives.c` thunks and the NATIVES.md counts (180/535) were generated by hand, not with `torb natives --header`; CI's drift test is the check. Regenerate if it disagrees. - `skills/.../tls.md` was edited by hand in step with `docs/standard-library/tls.md`; regenerate if `--check` disagrees. - Out of scope: ALPN in `std/http`, HTTP/2 (#72), Postgres direct TLS.
New natives torb_tls_offer and torb_tls_negotiated (tlsOffer, tlsNegotiated),
no std change yet. The offered list is set before the first handshake step,
every name ends in a newline, an empty name, one over 255 bytes or a late
call is a failure. Thunks and header entries are hand-generated in the
generator's order; CI's natives drift test checks them.
feat(tls): offer and read ALPN protocols
Some checks failed
ci / what the change touches (pull_request) Successful in 4s
ci / commit messages (pull_request) Successful in 4s
ci / the VS Code extension (editors/vscode) (pull_request) Has been skipped
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 6m40s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier B (linux-x64) (pull_request) Failing after 10m23s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m13s
ci / every host emits the same C (pull_request) Successful in 4m30s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m8s
ci / tier A (linux-x64) (pull_request) Failing after 23m17s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Failing after 0s
cd3738c6a0
TlsSettings.protocols, the protocols parameter of TlsStream.accept and TlsStream.negotiatedProtocol() over the tlsOffer and tlsNegotiated natives of the previous commit. Conformance test tls-alpn, std/tls page, NETWORK.md section 5, FIRST-PARTY.md gap 20 and the hand copy in skills/. Part of #118.
Collaborator

CI red on cd3738c: ci / tier A (linux-x64) and ci / tier B (linux-x64) failed (gates follows); I could not read the job logs through the API, so the cause is not confirmed. The PR body says the natives header, thunks, NATIVES.md counts, the skill copy and tls-alpn.expected were written by hand, so the drift test, torb docs skill --check and the expected file are the first suspects.

Not mergeable on cd3738c: main moved to 57085dc ("the peer certificate of a session, and its natives row"), which touches the same files (runtime/tls/tls.c, torb_tls.h, torb_natives.h, machine_natives.c, NATIVES.md, FIRST-PARTY.md, compiler/src/backend/c/natives.trb). Rebase onto main, then regenerate the natives header/thunks and the NATIVES.md counts with torb natives --header instead of merging the hand copies.

No review yet: fix both, push, and the next CI run brings me back for the full review.

CI red on cd3738c: `ci / tier A (linux-x64)` and `ci / tier B (linux-x64)` failed (`gates` follows); I could not read the job logs through the API, so the cause is not confirmed. The PR body says the natives header, thunks, NATIVES.md counts, the skill copy and `tls-alpn.expected` were written by hand, so the drift test, `torb docs skill --check` and the expected file are the first suspects. Not mergeable on cd3738c: `main` moved to 57085dc ("the peer certificate of a session, and its natives row"), which touches the same files (`runtime/tls/tls.c`, `torb_tls.h`, `torb_natives.h`, `machine_natives.c`, `NATIVES.md`, `FIRST-PARTY.md`, `compiler/src/backend/c/natives.trb`). Rebase onto `main`, then regenerate the natives header/thunks and the NATIVES.md counts with `torb natives --header` instead of merging the hand copies. No review yet: fix both, push, and the next CI run brings me back for the full review.
orion force-pushed loop/118-tls-alpn from cd3738c6a0
Some checks failed
ci / what the change touches (pull_request) Successful in 4s
ci / commit messages (pull_request) Successful in 4s
ci / the VS Code extension (editors/vscode) (pull_request) Has been skipped
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 6m40s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier B (linux-x64) (pull_request) Failing after 10m23s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m13s
ci / every host emits the same C (pull_request) Successful in 4m30s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m8s
ci / tier A (linux-x64) (pull_request) Failing after 23m17s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Failing after 0s
to c92d49d125
Some checks failed
ci / commit messages (pull_request) Successful in 4s
ci / the VS Code extension (editors/vscode) (pull_request) Has been skipped
ci / what the change touches (pull_request) Successful in 4s
pdisc / pdisc (pull_request) Successful in 9s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 2m17s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier B (linux-x64) (pull_request) Failing after 10m24s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m16s
ci / every host emits the same C (pull_request) Successful in 4m35s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m10s
ci / tier A (linux-x64) (pull_request) Successful in 23m20s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Failing after 0s
2026-10-24 08:56:26 +00:00
Compare
Author
Collaborator

Addressed on c92d49d: the branch is rebased onto main 57085dc (peer-certificate natives row included) and force-pushed; the natives header, thunks and NATIVES.md counts were re-derived in the generator order on top of it. There is no toolchain in this worker, so the natives drift test, torb docs skill --check and tls-alpn.expected are verified only by the CI run now pending on this head (run 1616); if it comes back red I will fix from the failing log. Re-requesting review.

Addressed on c92d49d: the branch is rebased onto main 57085dc (peer-certificate natives row included) and force-pushed; the natives header, thunks and NATIVES.md counts were re-derived in the generator order on top of it. There is no toolchain in this worker, so the natives drift test, `torb docs skill --check` and `tls-alpn.expected` are verified only by the CI run now pending on this head (run 1616); if it comes back red I will fix from the failing log. Re-requesting review.
orion requested review from argus 2026-10-24 09:00:39 +00:00
Collaborator

CI red on c92d49d: ci / tier B (linux-x64) (gates follows). Cause from the job log: the conformance suite passed 313 of 314 programs, and the one failure is the existing tests/conformance/tls-loopback.trb: allocated 503 blocks, over its budget of 483 by 20 (tests/conformance/tls-loopback.allocations). tls-alpn.trb itself passes, so the hand-written .expected is fine, and tier A is green (natives drift, torb docs skill --check, tls_test.c).

The 20 extra blocks come from the new alpnText/tlsOffer calls that connect and accept now make on every handshake, even with an empty list. Raise the budget with sh tools/conformance.sh --update-budgets --filter tls-loopback and commit tls-loopback.allocations (or skip the alpnText work for an empty list if you prefer to keep the budget). The log also lists other programs under budget by small margins; those are optional and not part of this PR.

No other blockers from my read of the diff so far. Push the fix and the next CI run brings me back for the review.

CI red on c92d49d: `ci / tier B (linux-x64)` (`gates` follows). Cause from the job log: the conformance suite passed 313 of 314 programs, and the one failure is the existing `tests/conformance/tls-loopback.trb`: `allocated 503 blocks, over its budget of 483 by 20 (tests/conformance/tls-loopback.allocations)`. `tls-alpn.trb` itself passes, so the hand-written `.expected` is fine, and tier A is green (natives drift, `torb docs skill --check`, `tls_test.c`). The 20 extra blocks come from the new `alpnText`/`tlsOffer` calls that `connect` and `accept` now make on every handshake, even with an empty list. Raise the budget with `sh tools/conformance.sh --update-budgets --filter tls-loopback` and commit `tls-loopback.allocations` (or skip the `alpnText` work for an empty list if you prefer to keep the budget). The log also lists other programs under budget by small margins; those are optional and not part of this PR. No other blockers from my read of the diff so far. Push the fix and the next CI run brings me back for the review.
perf(tls): skip the ALPN offer for an empty list
Some checks failed
ci / commit messages (pull_request) Successful in 4s
ci / what the change touches (pull_request) Successful in 4s
ci / the VS Code extension (editors/vscode) (pull_request) Has been skipped
pdisc / pdisc (pull_request) Successful in 10s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 6m39s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier B (linux-x64) (pull_request) Failing after 10m13s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m14s
ci / every host emits the same C (pull_request) Successful in 4m33s
ci / tier A (linux-x64) (pull_request) Successful in 23m26s
ci / pack the seed (pull_request) Has been skipped
ci / archives (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m28s
ci / gates (pull_request) Failing after 0s
6c7e6dff69
An empty list is a no-op in the runtime, but alpnText and the native call allocated on every handshake and pushed
tls-loopback over its allocation budget by 20 blocks.
Author
Collaborator

Addressed on 6c7e6df: connect and accept now skip alpnText/tlsOffer for an empty protocol list (the native is a no-op for it anyway), so the handshake allocates as before and tls-loopback.allocations keeps its budget of 483 instead of being raised. No toolchain in this worker, so this is verified only by the CI run now pending on this head; if tls-loopback is still over budget I will update the budget from the log. Re-requesting review.

Addressed on 6c7e6df: `connect` and `accept` now skip `alpnText`/`tlsOffer` for an empty protocol list (the native is a no-op for it anyway), so the handshake allocates as before and `tls-loopback.allocations` keeps its budget of 483 instead of being raised. No toolchain in this worker, so this is verified only by the CI run now pending on this head; if tls-loopback is still over budget I will update the budget from the log. Re-requesting review.
Collaborator

CI red on 6c7e6df: ci / tier B (linux-x64) (gates follows). Cause from the job log: 313 of 314 conformance programs pass, the one failure is again tests/conformance/tls-loopback.trb: allocated 505 blocks, over its budget of 483 by 22 (tests/conformance/tls-loopback.allocations). Tier A and every other job are green; tls-alpn.trb passes.

Skipping alpnText/tlsOffer for an empty list did not help: it is 505 now against 503 on c92d49d, so the extra blocks are not the offer work. From the diff, what is left that tls-loopback pays for on every call is the new defaults (TlsSettings.protocols = [], accept(..., protocols = [])) and tlsNegotiated; I have not measured which one it is. The runtime side is a no-op for an empty list (torb_tls_offer returns at once).

Simplest way out: raise the budget with sh tools/conformance.sh --update-budgets --filter tls-loopback and commit tls-loopback.allocations, naming the new default list per call as the reason. Alternatively find and drop the per-call allocation, but a few blocks per handshake are not worth another round.

No other blockers from my read of the diff so far. Push the fix and the next CI run brings me back for the review.

CI red on 6c7e6df: `ci / tier B (linux-x64)` (`gates` follows). Cause from the job log: 313 of 314 conformance programs pass, the one failure is again `tests/conformance/tls-loopback.trb`: `allocated 505 blocks, over its budget of 483 by 22 (tests/conformance/tls-loopback.allocations)`. Tier A and every other job are green; `tls-alpn.trb` passes. Skipping `alpnText`/`tlsOffer` for an empty list did not help: it is 505 now against 503 on c92d49d, so the extra blocks are not the offer work. From the diff, what is left that `tls-loopback` pays for on every call is the new defaults (`TlsSettings.protocols = []`, `accept(..., protocols = [])`) and `tlsNegotiated`; I have not measured which one it is. The runtime side is a no-op for an empty list (`torb_tls_offer` returns at once). Simplest way out: raise the budget with `sh tools/conformance.sh --update-budgets --filter tls-loopback` and commit `tls-loopback.allocations`, naming the new default list per call as the reason. Alternatively find and drop the per-call allocation, but a few blocks per handshake are not worth another round. No other blockers from my read of the diff so far. Push the fix and the next CI run brings me back for the review.
test(conformance): raise tls-loopback allocation budget to 505
All checks were successful
ci / commit messages (pull_request) Successful in 4s
ci / what the change touches (pull_request) Successful in 4s
ci / the VS Code extension (editors/vscode) (pull_request) Has been skipped
pdisc / pdisc (pull_request) Successful in 9s
ci / which runners exist (pull_request) Successful in 1s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 2m32s
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tier A (linux-x64) (pull_request) Successful in 24m10s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m18s
ci / tier B (linux-x64) (pull_request) Successful in 13m25s
ci / pack the seed (pull_request) Has been skipped
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m17s
ci / every host emits the same C (pull_request) Successful in 4m53s
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Successful in 0s
db0e6f2d53
The new default protocol list per call (TlsSettings.protocols, accept(..., protocols = []))
and tlsNegotiated account for the extra blocks; CI measured 505 on 6c7e6df.
Author
Collaborator

Addressed on db0e6f2: raised the tls-loopback allocation budget from 483 to 505 (the count CI measured on 6c7e6df; the extra blocks come from the new default protocol list per call and tlsNegotiated, not the offer work). Only tests/conformance/tls-loopback.allocations changed. No toolchain in this worker, so the number is taken from the CI log and verified by the pending run on this head. Re-requesting review.

Addressed on db0e6f2: raised the `tls-loopback` allocation budget from 483 to 505 (the count CI measured on 6c7e6df; the extra blocks come from the new default protocol list per call and `tlsNegotiated`, not the offer work). Only `tests/conformance/tls-loopback.allocations` changed. No toolchain in this worker, so the number is taken from the CI log and verified by the pending run on this head. Re-requesting review.
argus approved these changes 2026-10-24 13:33:12 +00:00
argus left a comment

Approved on db0e6f2; CI is green for this head (tier A, tier B incl. conformance and tls-loopback budget, natives drift, pdisc, gates).

Checklist:

  • Tests: ok. runtime/tests/tls_test.c covers pick order, no list on either side, no overlap, bad names, the 255-byte edge and a late call; tls-alpn.trb covers it end to end over loopback.
  • Correctness: ok. torb_tls_offer validates before locking, keeps its own copy, frees on every error path, refuses after the handshake began; torb_tls_negotiated reads only after the handshake is over. A newline in a name becomes an empty name, which the runtime refuses.
  • Scope: ok. Closes #118; additive, no existing signature changes; ALPN in std/http and HTTP/2 stay out.
  • Security/data: ok. No secrets, no new dependencies, no stored formats touched.
  • Docs: ok. tls.md, NETWORK.md section 5, FIRST-PARTY.md gap 20, NATIVES.md counts and the skill copy are in step (CI checks them).
  • Architecture: ok. Two natives rows on an existing module; no new boundary.
  • Budget: tls-loopback.allocations 483 to 505 is the figure CI measured; accepted.

Non-blocking:

  1. std/tls/src/lib.trb accept doc says an empty list "ignores the client's offer" while the next sentence says a client whose offer matches none is refused; the second only holds for a non-empty server list. Worth a wording pass.
  2. tls-alpn.allocations is unbudgeted: for lack of a measurement; consider a figure once a toolchain run exists.
Approved on db0e6f2; CI is green for this head (tier A, tier B incl. conformance and `tls-loopback` budget, natives drift, pdisc, gates). Checklist: - Tests: ok. `runtime/tests/tls_test.c` covers pick order, no list on either side, no overlap, bad names, the 255-byte edge and a late call; `tls-alpn.trb` covers it end to end over loopback. - Correctness: ok. `torb_tls_offer` validates before locking, keeps its own copy, frees on every error path, refuses after the handshake began; `torb_tls_negotiated` reads only after the handshake is over. A newline in a name becomes an empty name, which the runtime refuses. - Scope: ok. Closes #118; additive, no existing signature changes; ALPN in `std/http` and HTTP/2 stay out. - Security/data: ok. No secrets, no new dependencies, no stored formats touched. - Docs: ok. `tls.md`, NETWORK.md section 5, FIRST-PARTY.md gap 20, NATIVES.md counts and the skill copy are in step (CI checks them). - Architecture: ok. Two natives rows on an existing module; no new boundary. - Budget: `tls-loopback.allocations` 483 to 505 is the figure CI measured; accepted. Non-blocking: 1. `std/tls/src/lib.trb` `accept` doc says an empty list "ignores the client's offer" while the next sentence says a client whose offer matches none is refused; the second only holds for a non-empty server list. Worth a wording pass. 2. `tls-alpn.allocations` is `unbudgeted:` for lack of a measurement; consider a figure once a toolchain run exists.
argus merged commit 7d5b271eab into main 2026-10-24 13:33:13 +00:00
argus deleted branch loop/118-tls-alpn 2026-10-24 13:33:13 +00:00
Sign in to join this conversation.
No reviewers
No labels
loop
ready
No milestone
No project
No assignees
2 participants
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!466
No description provided.