ADR-069: http Leaves std and Becomes a Package, So It Can Ripen at Its Own Speed

Status: Superseded by ADR-289 — its D1-D5 are D1-D5 there; D6 is not carried Date: September 14, 2026 Target Version: Nikaia 0.0.8 Supersedes: — nothing. It settles a question the specification answered twice, differently, and that no record had ever put Related: ADR-018 (the request is the handler’s first argument, and what a handler returns), ADR-058 (a response body may be bytes the program never allocated; D7’s Untrusted path), ADR-038 D1, D6 and §4.5 (the server is Nikaia’s own, HTTP/1.1 first, and it is not built), ADR-002 D1 (a manifest key reaches Cargo verbatim), ADR-047 D2 (the same path is the same package), ADR-053 D2 (what a build calls each package)


1. Context

The specification says both things.

The tooling chapter heads a section std::http and promises “a production-ready HTTP/1.1 and HTTP/2 server and client”, and the README’s code example opens with

use std::http

The first chapter uses the same name as its worked example of a foreign package — it is the passage that teaches how one package reaches another at all:

use http

fn handle(r: http::Request) -> rh::Response { … }

with http = { path = "../http" } in [dependencies]. And ADR-046 D2 reaches for use http::server as its example of “a path that is not std’s”.

So the same name is the standard-library example in one chapter and the not-the-standard-library example in another. Nobody decided this, and nobody noticed, for a reason that is itself the finding: neither exists. While there is no way to name a package by a version (ADR-002 D1 §5 declines to answer it deliberately), “in std” and “a package” look identical from here — either way you cannot have it.

What did happen in the meantime is that nine decisions across two records elaborated a surface on top of the unasked question. That is the cost this record stops: each of them reads as settled, and the thing they rest on was never put.

2. Decisions

D1 — http is a package, not a module of std, and it lives in examples/ until it is ripe.

It is reached the way the first chapter already teaches, by a path:

[dependencies]
http = { path = "../http" }

A path dependency of this shape is not a promise about the future: it is the arm project.rs already resolves, recursively, refusing two names for one path (ADR-047 D2) and naming each package once per build (ADR-053 D2). What the corpus exercises today is the type = "rust" arm, in examples/foreign-runtime/; the Nikaia arm is the one crates/nikaia/tests/project.rs covers.

examples/ rather than a repository of its own, because a package nobody can fetch and nobody can build is not a package, it is a directory. In examples/ it is compiled by the same test that compiles every other example, and it grows against a compiler that is moving.

D2 — Subtract what needs the compiler, and nothing HTTP-shaped is left in std.

The argument for a server in a standard library is that parts of it cannot live outside one. That argument is worth taking seriously and it does not survive being itemised. Three things in the design genuinely need the compiler, and all three are already in std under their own names and for their own reasons:

What is left after the subtraction is the protocol — request parsing, status lines, headers, HTTP/2, TLS — and the framework: a route table, a handler shape, listen. Neither needs a compiler, both are large, and both age. A standard library that carries them ties the language’s release to a protocol’s.

D3 — The direction is chosen by which mistake stays cheap.

Both answers could be wrong, and being wrong costs different amounts.

Moving a package into std later is an addition: programs that named it as a dependency keep working, and the manifest line becomes redundant rather than invalid. Moving a module out of std is a break: every use std::http in every program stops compiling, and there is no deprecation that makes it not a break.

So the asymmetry decides it even for a reader who thinks a server belongs in a standard library. Start outside, where a change of mind is additive.

D4 — It ripens at its own speed, and that is the decision rather than a consequence.

A version of std is a version of the language: the ledger it ships is read by the compiler, and its entries are checked against the compiler’s own inference (crates/nikaia/tests/contracts.rs). A protocol implementation has a different clock — it wants to be wrong in public, change its surface twice, and settle — and a surface that changes twice inside std is two breaks in the language.

Outside, the same two changes are two versions of a package, and a program that wants the old one keeps it. That is the whole of why it moves, and the measure of ripeness is therefore not “is it finished” but “has its surface stopped moving”.

D5 — This is not blocked by how a package is named by a version.

It looked as though it would be, and it is worth writing down that it is not: a path dependency needs no registry, no namespace, and no distribution format. The notes page’s entry on naming a package by a version stays open, and this record does not wait for it.

What the open question does decide is the day http leaves examples/ — which is D4’s ripeness by another name, and a later decision than this one.

D6 — The records that describe the server keep every word.

ADR-018 and ADR-058 decide what a handler receives, what it may return, how bytes reach a socket, what may be kept between requests, and what happens to a path a request names. None of that is about where the code lives, and none of it is reopened here. A record is written once and is not edited when the code moves (README.md), so those records keep saying std; what changes is the specification, which is maintained, and this record is the reason a reader finds for the difference.

3. Consequences

The specification stops contradicting itself, which is the only thing that gets worse while nothing is built.

examples/fortunes.nika has somewhere to live. It does not compile today and says so, waiting on the request surface; it is now waiting on a package rather than on a chapter of std.

The concurrency half of the language still has no program. That is a separate gap, found while this one was being looked at: no .nika file in the tree uses spawn, Locked, Shared or SharedMut — those features live in Rust test snippets only. A server is the honest program for it, which is why it is worth building this one rather than a contrived queue.

The entry on the notes page closes — what is std::http — and the question it should have asked, is http in std at all, is answered rather than deferred.

4. What is built

The specification and the README. The tooling chapter’s section and the README’s example now spell http as a package and name this record; the first chapter needed no change, because it was already right.

Not the package. There is no examples/http/, and std::http never existed to remove (ADR-038 §4.5) — this record moves a promise, not code. D1’s path arm is built and tested; what is missing is the thing on the other end of the path.

This site is open source. Improve this page.