Skip to content

Decide the means the core speaks HTTP with: the protocol crate, not a client (#292) - #296

Merged
iderex merged 1 commit into
mainfrom
the-means-the-core-speaks-http-with-292-signed
Sep 4, 2026
Merged

Decide the means the core speaks HTTP with: the protocol crate, not a client (#292)#296
iderex merged 1 commit into
mainfrom
the-means-the-core-speaks-http-with-292-signed

Conversation

@iderex

@iderex iderex commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Closes #292

What changed

The core's HTTP means is decided and recorded. docs/decisions/0292-the-means-the-core-speaks-http-with.md decides that the core writes and reads HTTP/1.1 through ureq-proto, taken with default-features = false and the client feature alone, driven over a socket the core opens and the TLS stream 0243 already decided. It is admitted under 0103's clause for a dependency a landed record already requires, the records being 0011, which measures that the standard library reaches TCP and stops, and 0027, which requires a transport.

The manifest carries the entry with the clause that admits it and the condition that retires it written beside it, as 0103 requires. The lockfile carries the seven packages the entry resolves to. The index in docs/decisions/README.md names the record.

The record takes a protocol rather than a client, and it says why as records rather than as taste: every bound, every connection and every destination that 0027, 0069 and 0102 decide is a value src/server/transport.rs already holds, and a client would hold them instead, on a clock 0102's injected source cannot reach, with a proxy read from the environment that 0069's set does not admit, and with a cancelled body dropped rather than read to 0027's bound.

What failure it prevents

The one 0103 was written against, one layer up. The socket #27 writes would otherwise be written against the first package that compiles, and the first package that compiles for HTTP in this ecosystem is a client that arrives holding opinions about timeouts, pools, redirects and proxies which four landed records here have already decided differently. None of those reversals reddens a gate: the invariants register reads src/ and judges no dependency, so a tree that moved its bounds into a client keeps a green invariants leg while 0102's promise stops being provable. The record is the only place that reversal is refused, which is why it is written before the code.

Evidence

The tree every reading is against:

git rev-parse origin/main
d9de3c7fb18237d4e3456c6504a01819d82c55d5

The record's counts were first taken on 2026-09-03 in scratch crates outside this tree. I re-took every one of them today with --locked in those same crates, with the toolchain rust-toolchain.toml pins (cargo 1.98.0 (797e8a9bc 2026-08-05)), and every number reproduces to the package. The scratch crate declaring ureq-proto = "0.6" and nothing else, and the one declaring it with default-features = false and features = ["client"], resolve to the same seven packages on all seven triples:

for t in aarch64-linux-android armv7-linux-androideabi aarch64-apple-ios aarch64-apple-tvos aarch64-apple-darwin x86_64-pc-windows-msvc x86_64-unknown-linux-gnu; do
  cargo tree -e normal --target $t --prefix none --locked | sed 's/ (\*)$//;s/ (proc-macro)$//' | sort -u | grep -v '^probe-' | wc -l
done
7 7 7 7 7 7 7         ureq-proto alone; the client-only listing is identical per triple under cmp
29 29 17 17 17 13 13  0243's baseline, rustls 0.23 and rustls-platform-verifier 0.7, reproducing that record's counts
35 35 24 24 24 20 20  ureq 3 with rustls-no-provider and platform-verifier

comm -13 baseline.TRIPLE ureq-proto.TRIPLE | wc -l     5 5 6 6 6 6 6
comm -13 baseline.TRIPLE ureq.TRIPLE | wc -l           8 8 9 9 9 9 9

comm -13 ureq-proto.x86_64-unknown-linux-gnu ureq.x86_64-unknown-linux-gnu
once_cell v1.21.4
openssl-probe v0.2.1
percent-encoding v2.3.2
rustls v0.23.43
rustls-native-certs v0.8.4
rustls-pki-types v1.15.1
rustls-platform-verifier v0.7.0
rustls-webpki v0.103.15
subtle v2.6.1
untrusted v0.9.0
ureq v3.4.0
utf8-zero v0.8.1
zeroize v1.9.0

The licence of every package the entry brings, read rather than assumed:

cargo metadata --format-version 1 --locked | jq -r '.packages[] | "\(.name) v\(.version)\t\(.license)"' | sort
base64 v0.23.1      MIT OR Apache-2.0
bytes v1.12.1       MIT
http v1.5.0         MIT OR Apache-2.0
httparse v1.10.1    MIT OR Apache-2.0
itoa v1.0.18        MIT OR Apache-2.0
log v0.4.34         MIT OR Apache-2.0
ureq-proto v0.6.1   MIT OR Apache-2.0

The four behaviours 0103 refuses, re-read over the vendored sources of all seven packages:

grep -rnE 'TcpStream|UdpSocket|ToSocketAddrs' */src ; echo "exit=$?"
exit=1
grep -rnE 'std::fs|OpenOptions|File::(open|create)' */src
bytes/src/bytes.rs:240:    /// let file = File::open("upload_bundle.tar.gz")?;
grep -rnE 'thread::spawn|thread::Builder' */src
bytes/src/bytes.rs:1643:            let t1 = thread::spawn(move || {
bytes/src/bytes.rs:1648:            let t2 = thread::spawn(move || {
bytes/src/bytes_mut.rs:246:    /// let th = thread::spawn(move || {
bytes/src/bytes_mut.rs:2015:            let t1 = thread::spawn(move || {
bytes/src/bytes_mut.rs:2020:            let t2 = thread::spawn(move || {
grep -n 'cfg(all(test, loom))' bytes/src/bytes.rs bytes/src/bytes_mut.rs
bytes/src/bytes.rs:1627:#[cfg(all(test, loom))]
bytes/src/bytes_mut.rs:1997:#[cfg(all(test, loom))]
ls */build.rs
httparse/build.rs

The readings the record makes of the client shape were re-run against the registry sources at the versions the record names (ureq-3.4.0, reqwest-0.13.4, isahc-2.0.1, attohttpc-0.31.0, minreq-3.0.0, hyper-1.11.1): the pub(crate) struct CurrentTime at timings.rs:177 with Instant::now as its default at 215-218, proxy: Proxy::try_from_env() at config.rs:872, no impl Drop anywhere in ureq/src, the eleven methods of pool.rs, the thread in reqwest's blocking client at 1414 and in isahc's agent at 144, no platform.verifier string in attohttpc, and minreq's static CONFIG and its rustls-platform-verifier at 0.6.2. Every line number in the record is the one the source prints today.

The gate's legs, run on this tree, which is byte for byte the commit pushed (nothing was unstaged when they ran):

cargo build --locked --all-targets
Finished `dev` profile [unoptimized + debuginfo] target(s)
cargo test --locked
test result: ok. 577 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 2 passed ...    test result: ok. 5 passed ...    test result: ok. 5 passed ...
test result: ok. 3 passed ...    test result: ok. 11 passed ...   test result: ok. 28 passed ...
test result: ok. 5 passed ...    test result: ok. 125 passed ...  test result: ok. 1 passed ...

bash .github/lint/lint.sh check                          exit=0  Every lint the groups above carry was refused, outside the register printed with it.
bash .github/format/format.sh check                      exit=0  Every tracked source file above is written the way the formatter would write it.
bash .github/invariants/invariants.sh check              exit=0  Every rule above was applied to its subject and refused nothing.
bash .github/decision-records/decision-records.sh check  exit=0  Every narrowing these records declare is named from both ends.
bash .github/statements/statements.sh check              exit=0
bash .github/doc-paths/doc-paths.sh check                exit=0  Every path these documents name resolves against the tracked set.
bash .github/fixture-scrub/fixture-scrub.sh check        exit=0
bash .github/dependencies/dependencies.sh check          exit=0  Cargo.toml resolves to the committed lockfile, and the 17 package(s) it declares carry no advisory this database knows.
bash .github/targets/targets.sh check                    exit=0  The library compiles for 7 target triple(s): aarch64-linux-android armv7-linux-androideabi aarch64-apple-ios aarch64-apple-tvos aarch64-apple-darwin x86_64-pc-windows-msvc x86_64-unknown-linux-gnu

The means

Markdown for the record, because a decision record is what docs/decisions/0001-decision-records.md fixes as the shape, and a manifest entry with its lockfile for the dependency, because that is the one place 0103 says an admitted dependency and its clause live. Nothing else is available for either, and no language, runtime or tool arrives with this change that the tree did not already carry: the seven packages are pure Rust, one of them runs rustc --version at build time and none compiles C.

What this does not cover

The thread-detector leg. It runs the suite for x86_64-unknown-linux-gnu under -Zsanitizer=thread and there is no cc linker for that target on this machine, so the local run refused with linker cc not found before a single test was collected. That verdict is the runner's alone and is not claimed here. The coverage leg is the same: not measured locally, the runner's number is the one to read.

The transport itself. Nothing here opens a socket, and the record says so of itself. #27 is where the read-write loop over rustls::StreamOwned<ClientConnection, TcpStream> is written, and this record is what that socket is written against. #27's own text names #292 as what it waits on and does not yet name the record by number; that line belongs on #27 and is not written from here.

The entry 0243 needs. rustls and rustls-platform-verifier are not added by this change, and #291 still stands in front of them, because every graph that includes them carries a C build the target leg cannot compile today. The entry landed here is pure Rust and does not reach that question, which the targets leg above shows: all seven triples compile with it.

Nothing runs on any triple but this machine's. The seven-triple result is a compile and never a run, which the targets leg prints beside its own verdict.

Nothing here reads the clause line beside the manifest entry. 0103 says of itself that nothing in this repository does, and the dependencies leg prints the same bound, so whether the clause written there is the right clause is a reading for whoever reviews this and for nobody else.

Who has read it

Nobody besides me has read this change. There is no second reader on this board tonight, and the evidence above stands in place of one: every count in the record was re-taken with --locked and reproduces, every source reading was re-run at the version named, and every gate leg that can run on this machine has run on this tree.

The pull request this replaces

#295 carried this same commit without the Signed-off-by trailer the DCO sign-off check requires, and adding the trailer rewrites the commit. A pushed branch is never rewritten here, so the corrected commit arrives under a new branch name and #295 is closed with this reason written into its body. The tree content is identical: the diff of the two heads is empty.

… client (#292)

Record 0292 decides that the core writes and reads HTTP/1.1 through
`ureq-proto`, taken with `default-features = false` and the `client`
feature alone, driven over a socket the core opens and the TLS stream 0243
decided. The manifest carries the entry with the clause of 0103 that admits
it and the condition that retires it, the lockfile carries the seven
packages it resolves to, and the index in `docs/decisions/README.md` names
the record.

What goes wrong without it: the socket #27 writes is written against the
first package that compiles, and the first package that compiles for HTTP
in this ecosystem is a client. A client holds 0027's bounds as its own
settings on a clock 0102's injected source cannot reach, adds a proxy read
from the environment to the destinations 0069 fixed, and drops a cancelled
body instead of reading it to 0027's bound. None of those reversals reddens
a gate here, because the invariants register reads `src/` and judges no
dependency, so the record is the only place the reversal is refused.

I re-took every count in the record with `--locked` in the scratch crates
the readings came from and they reproduce to the package, and I re-ran
every source reading against the vendored and registry sources at the
versions named. Build, test, lint, format, invariants, decision-records,
statements, doc-paths, fixture-scrub, dependencies and all seven target
triples pass on this tree; the thread-detector leg cannot link on this
machine and is the runner's verdict alone.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 3979f8a into main Sep 4, 2026
27 checks passed
@iderex
iderex deleted the the-means-the-core-speaks-http-with-292-signed branch September 4, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decide the means the core speaks HTTP with, and record what it costs

1 participant