Commit Graph
4 Commits
Author SHA1 Message Date
jschoubben b48572bdfc A null body limit is refused, not read as no limit; the gate on the wrong machine is refused
Review of the registry hand-over. The proxy's bodyLimit treated an absent key and a JSON
null alike, so a `max-request-body: null` was served unlimited here while the adapter
skipped it and the catalogue refused it — one provider carrying what the others refuse.
Presence is now checked before the value is read.

The catalogue-backed test asserted the gate pulling the store in beside it as the feature.
It was the fault: a node-scoped requirement with one candidate installs that candidate, so
a gate assigned to a machine without the store raised a second, empty one there behind the
real credentials and the public name. The store's seat is one per mesh now (mesh-catalog),
and the test asserts the refusal by name. Delete is asserted only behind the lock.

hq ADR 0082/0104, the registry hand-over.
2026-09-23 23:35:29 +02:00
jschoubben 01d57b629f A route says the largest body its proxy may carry, and both proxies honour it
The registry's public name is served by the predecessor with a twenty-gigabyte buffering
middleware, because a registry takes image layers in single requests of gigabytes and a
proxy's default turns every push into a 413 the registry never sees. A route contribution
had no way to say so, so the mesh could not take the name over without losing what made it
usable.

The contribution now carries `max-request-body`, a whole positive number of bytes, and the
catalogue holds every route to an agreed vocabulary — label or name, port, and the limit —
refusing a key no proxy reads (a field that parses cleanly and does nothing is a promise
nobody keeps) and a route with no port (unreachable by the proxy it just asked for, found at
parse time rather than in a proxy's log). The mesh's own proxy reads the limit as written,
refuses a body past it as 413 rather than the 502 the transport would have reported, and
skips a route whose limit it cannot read rather than carrying what the module said not to.

The registry's hand-over itself is read from the catalogue beside this checkout: the store
still resolves with no proxy, the gate beside it pulls the store in, contributes the
predecessor's name on the port the node gave it, and locks only the door that faces the
world.

hq ADR 0082/0104, the registry hand-over.
2026-09-23 23:19:12 +02:00
jschoubben f04d00b411 The proxy can obtain a public certificate, and asks staging by default
Work breakdown 1.4. The mesh's own authority certifies internal names
and always did; a name reachable from outside needs one the world
already trusts, and there was no ACME anywhere in this repository.

Uses acme/autocert from x/crypto, which was already a dependency — one
indirect addition (x/net, for idna) and no new direct one.

Three things worth more than the feature:

**Staging is the default** (novox/hq 04-ISSUES/004). Production issuance
is rate-limited per domain and per account and does not replenish
quickly. Defaulting to production would leave the safe path depending on
remembering to opt out, on exactly the work most likely to iterate. A
staging certificate is trusted by no browser, so the mistake announces
itself on the first request rather than a fortnight later.

**A certificate is only asked for on a name the mesh routes here.**
Without that policy, anything that can reach the port and send a name
triggers an order for it — a scan becomes a stream of failed orders
against the account's rate limit, and the proxy looks healthy
throughout. What it may certify is what it was told to serve.

**A private issuer is trusted by naming a file, never by skipping
verification.** Skip would still apply on the day this points at a
public issuer, and nothing would say so.

TLS is opt-in: without TLS_LISTEN the proxy serves plain HTTP exactly as
before, which is what an internal-only mesh wants. With it and no cache,
it refuses rather than defaulting — every restart would otherwise order
new certificates, silently, until the rate limit says it does not.
2026-08-31 19:35:34 +02:00
jschoubben d0c0ee8dab A route is a grant, and a provider is told where its consumer is
novox/hq 08-connectivity §3, built. The mirror of a database grant: there the
consumer supplies a name and receives credentials; here it supplies a target
and receives a name. Nothing new in the vocabulary — a route is a provision
like any other.

One field was missing and it is the one that matters for anything reaching
back: a contribution now carries where the mesh says that machine is. A reverse
proxy is told to send traffic to a consumer and has to open a connection, so
without it every provider implementing a provision would have to know how the
mesh names machines — a convention leaking into every module.

The proxy itself is an example, not part of the control plane: the contract is
the file, not this program. It replaces its table whole rather than merging,
because the file is the whole truth about who has a route and merging would
keep serving a name whose module was unassigned — the stale-route fault
08-connectivity lists as open, reintroduced one level down. A name it does not
serve is refused by saying which it does: a route withdrawn and a name that
never existed are different things.
2026-08-31 02:43:19 +02:00