# route-proxy — the mesh's public ingress The shipping form of the reference reverse proxy (novox/hq ADR 0007, novox/hq 08-connectivity §3 "Exposure"). It is the `traefik` replacement: the module that **provides `route`**. ## What it is **A route is a grant.** A module that must be reachable declares `requires: ["route"]` and contributes the name it wants and the port it listens on; this module provides `route`, receives every consumer that asked as a file, and forwards by the `Host` header of each request to `http://:` — where `at` is where the mesh says that consumer's machine is (empty means this machine, reached over loopback). The control plane does not write the proxy's configuration. It delivers the facts — the contributions file at `receives.route` — and the proxy turns them into whatever it runs. That boundary is why swapping this proxy for another costs nothing to the modules that publish through it. | field | value | why | |---|---|---| | `provides` | `route` (scope `mesh`) | a module on any node may require it | | `serves.route` | `{}` | a route hands back a name, not a credential — the consumer already knows the name it asked for | | `receives.route` | `…/routes/mesh.json` | where the mesh writes every contribution; the proxy reads it as `$ROUTES` and re-reads on change | | `listens` | `80` and `443`, both `from: anywhere` | the one machine with a public opening; the firewall opens these for free (ADR 0045) | No broker account, no own-secrets, no provisioner: the proxy neither mints a credential nor emits an event. It only reads the file the mesh writes. (Contrast `redis`, which mints passwords, and `cloudflare-dns`, which emits record events.) ## How it ships the Go proxy The proxy is a Go program, unlike the TypeScript tool-runtime modules. The canonical source is **not vendored here** — it lives in the mesh-controller repository at `examples/route-proxy`, the contract written as something that runs. This module ships only the packaging: a multi-stage [`Dockerfile`](Dockerfile) whose build context is the mesh-controller repository root and which compiles `./examples/route-proxy` into `mesh-route-proxy`. The committed `module.json` carries the placeholder digest every mesh-built image does (`@sha256:0000…`); the mesh pins the real digest at publish. In the lab, `mesh-lab/scripts/build-route-proxy-image.sh` builds this into the local daemon as `mesh-route-proxy:development`, and a scenario stocks it and serves it by digest. ## Certificates, and the endpoint (resolves 04-ISSUES/004) TLS is opt-in and terminates here for public names, via ACME (autocert). A proxy with no `TLS_LISTEN` serves plain HTTP; with it, it obtains a publicly-trusted certificate for exactly the names the mesh routes here (`HostPolicy` = only routed names, so a scan cannot spend the account's issuance quota) and answers the HTTP-01 challenge on `:80` at the name being certified. **`ACME_DIRECTORY` defaults to Let's Encrypt *staging*, not production.** This closes novox/hq 04-ISSUES/004 (certificate issuance targets production): the issuer is a per-node choice, and the safe endpoint is the default. 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 somebody remembering to opt out of it, 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 at the rate limit. **A node that serves real public traffic states so**, by overriding `ACME_DIRECTORY` to the production directory `https://acme-v02.api.letsencrypt.org/directory` for this module on that node. It is a node property, and the node facing the public internet is the one that opts in. | env | default | meaning | |---|---|---| | `ROUTES` | `/routes/mesh.json` | the contributions file the mesh writes (`receives.route`) | | `LISTEN` | `:80` | HTTP, and the ACME HTTP-01 challenge | | `TLS_LISTEN` | `:443` | HTTPS; unset to serve plain HTTP only | | `ACME_CACHE` | `/acme` | where issued certificates persist; required when `TLS_LISTEN` is set, so a restart does not re-order | | `ACME_DIRECTORY` | Let's Encrypt **staging** | the issuer; a public-serving node overrides it to production | | `ACME_CA_BUNDLE` | *(unset)* | a file of roots to trust for the issuer's own API — set only for a private/lab authority whose API certificate the world does not yet trust |