Both servers bound `0.0.0.0`. That was harmless while the only deployment was
docker-compose.yml, which publishes these ports on 127.0.0.1 and makes the
container's own bind irrelevant. As ordinary processes on a rented machine
there is no such wrapper, and `0.0.0.0` is a listener on the internet — in
front of an issuer that sets cookies without `Secure` and mints sign-in codes,
because it expects something else to be terminating TLS.
So the default is `127.0.0.1` and `HOST` is there for the deployment that
genuinely needs every interface. compose now sets `HOST: 0.0.0.0` explicitly,
which is not a workaround: inside a container, binding loopback is what would
make the published port unreachable. The right answer differs by deployment,
which is why it is a variable rather than a constant.
Both now log the address they bound, not the one they hoped for.
BREAKING CHANGE: api and auth no longer listen on every interface by default.
A deployment that relied on that must set HOST=0.0.0.0.
Moving the issuer's state into Postgres removed the last thing that tied
either app to one hosting provider. What was left was a deployment tool
describing resources that no longer existed — so this replaces it with
`wrangler`, which is what actually deploys a Worker, and adds a second way
to run each app that involves no provider at all.
Each app now has a `wrangler.jsonc` with an environment per stage, and a
`Dockerfile` beside it. The handler is the same one in both cases; what
differs is only where its settings come from. Two of them gained a second
spelling so that nothing has to branch on the runtime: Postgres arrives as
a pooled binding or as `DATABASE_URL`, and the route to the issuer is a
service binding or `AUTH_INTERNAL_URL`.
That last one is new, and it is a split the binding was already making
without saying so. `AUTH_ISSUER_URL` has to be the issuer's public name,
because it is compared literally against every token's `iss` claim — but
the public name is often not routable from inside a deployment. So the
name and the route are two settings now rather than one that cannot be
both.
DNS moves out of code and into `docs/dns.md`, which lists every hostname
and what it is for. Six records that change roughly never did not need a
tool, and the table outlives whatever is answering the names — which is
the point, since some of them will stop being Workers. The sandbox
hostnames are hyphenated rather than nested for the same reason: a
certificate covering `*.nestri.io` covers one label and not two, so
`api-sandbox.nestri.io` can become an ordinary origin later without a
certificate having to be ordered for it first.
Also drops `EMAIL_DEV_LOG` from committed configuration into `.dev.vars`,
which `wrangler deploy` cannot upload. Printing a live sign-in code to a
log should not be one forgotten override away from production.