Status: Superseded by ADR-289 — its D1 is D6 there, D4 is D14, D5 is D15, and D2 and D3 are D12 and D13 as narrowed by ADR-200 D1 and D2
Date: September 22, 2026
Target Version: Nikaia 0.0.153
Answers: open-decisions.md’s what does a Nikaia
program have to write to be a microservice, and who decides what is exposed?
Related: ADR-018 (what a handler is given and gives back),
ADR-069 D1 and D2 (http is a package, and the subtraction that
put it there), ADR-121 D1 and D4 (the awaitable readiness this
rests on), ADR-038 §4.5 and D5 (the server’s order, and what an
operating property is), ADR-108 (a path names its root at the
call)
The ask was the ergonomics of python -m http.server: one command line, no
ceremony, functionality reachable in a minute.
The question that page carried forced a choice between the program decides what is exposed and the operator decides, and offered four options to pick from. The owner’s answer is that the choice is false, and it is false for a reason worth writing down: those are not two designs for one product. They are two products, and each is right about itself.
std’s and the protocol is the package’s.ADR-069 D2’s subtraction, carried one item further. That record
itemised what genuinely needs the compiler — a path’s provenance, escaping by
type, a body that is bytes the program never allocated — and pushed the
protocol and the framework out of std. A socket is neither: it is an
operating-system resource of exactly the kind std already owns in fs and
io, and the runtime knows about it already — rt::io’s own words are a
socket is not a file; epoll answers a socket exactly.
And the http package is Nikaia, which cannot make a syscall. So the line
falls where the subtraction already put it: std lends the socket, the package
speaks HTTP.
nikaia serve [dir] — serves the current directory
or the one it is given. It has no program behind it, so everything it does is
the operator’s: the directory, the port, the limits, whether it leaves
localhost. Command-line options, and a configuration file where a deployment
wants one. This is the python -m http.server half of the ask.Why this is the answer and not a compromise. A standard server has no author present at run time, so the only person who can decide anything is the one starting it. An application has an author who decided already, and a tool that overrode them would be taking away the reason to write the application. The earlier framing — exposure is an operating property, so the operator names the routes — is ADR-038 D5 applied where it belongs, which is the first product, and it was wrong to reach into the second.
Both products. python -m http.server binding every interface is half of why it
is a footgun, and the fail-closed default costs one flag to leave.
The flag is not silent. ADR-108’s shape is the precedent: a way
around a check is recorded per site and listed by nikaia --trust. Binding
past localhost is that shape — this record does not build the listing, and it
names it as where such a thing belongs.
.route(…), and there is no @route yet.ADR-018’s own form, and it needs no language change at all:
measured at 0.0.152, the whole chain runs across a package boundary — a type
constructed through its package, a method chain over it, a function-typed
parameter (ADR-102 D1), and the async closure
ADR-192 D1 writes for a run parameter.
No word on the declaration, and no every pub fn is a route. The second is
refused outright and stays refused: pub is a package word
(ADR-047 D2), and reading it as the network may call it would
give one word two meanings and make the dangerous one invisible at the
declaration — the polarity ADR-010 D1 exists for.
And ADR-018’s example is stale in one token. It writes
http::Server::new(); NK1149 answers a type is constructed by its anonymous
constructor since ADR-140 D2. The record is not rewritten, and
what is written is http::Server().
GET and POST, bodies by Content-Length, Connection: close, no chunked
transfer and no TLS. That is ADR-038 §4.5’s own order read as a
scope: a socket layer that keeps registrations, then a minimal HTTP/1.1
server on it, then the parsing moved into Nikaia, then rustls, then HTTP/2.
The parser is written in Rust, in nikaia-std, and moving it into a Nikaia
grammar is a later step of that order — self-hosting the protocol is not the
MVP, and saying so is the difference between a step deferred and a step
forgotten. (That step is taken at 0.0.248: nikaia-std/src/tools/http1.nika
is the grammar and the rules, and http1.rs keeps the bytes.)
The limits are the operator’s in the first product and the program’s in the second: a body cap and a connection cap exist from the first commit, because a server without them is a denial of service rather than a default.
http package stops being a skeleton, and what it gains is the
protocol and the framework — which is what ADR-069 D2 said was
left after the subtraction.std gains a socket, which is the first new operating-system resource
since fs, and it is on Part I 1.3’s list like the
others.std, a protocol in a package, and a command
that is a file server — three pieces, and only the first is in the compiler’s
half of the tree.touches. The ledger
knows — a route onto a function that touches the filesystem is a different
object from one that computes, and no other language’s http.server has that
column. The owner has asked to be asked again, with a fuller write-up,
when the work reaches it. It is deferred on purpose and not dropped.--trust’s listing of a wide bind (D3), which is where
ADR-108’s shape would put it.Nothing of the server. What is built is the ground under it, and that is more than the entry it answers suggested:
rt::io::wait is awaitable, which is the first thing the
HTTP server’s socket layer needs.crates/nikaia/tests/project.rs.The order of work is ADR-038 §4.5’s, and #90 carries it.