One address in front of the REST API and sign-in

One address in front of both: the gateway

A client of the hosted provider's library is given one project URL and calls <url>/rest/v1/... and <url>/auth/v1/... on it; the provider's gateway takes each prefix off and hands the request to PostgREST or to sign-in. The fleet does the same:

stado database gateway place <name> --port <port> --public-url <url> --ready-within <seconds>

places Caddy, the release stado-rs/data/declarations/caddy.json pins (installed under ~/.local/share/stado-caddy/<version> and refused unless its archive's sha256 is the pinned one), beside the database as the unit <name>-gateway, reading ~/.stado/databases/<name>/gateway.caddyfile:

path answered by
/rest/v1… the REST API on this host's loopback at rest_port, the prefix removed; 503 {"error":"not_placed"} naming rest place while it is not placed
/auth/v1… sign-in on this host's loopback at auth_port, the prefix removed; 503 naming auth place while it is not placed
/stado/mail-templates/<kind>.html the mail templates auth place wrote; sign-in reads them at http://127.0.0.1:<gateway_port>/stado/mail-templates/
/stado/gateway/health {"database":"<name>","gateway":"ok"}, asked once a second after the unit starts until it answers or --ready-within seconds pass
anything else 404 {"error":"not_found"}

--public-url is the URL clients reach the gateway at, with no query or fragment: a public origin that publishes the port (below), or the gateway's own address for consumers on the fleet. It may carry a path of plain segments (https://host.example/wisent-app), so one public host name serves several databases, each under its own path: the gateway then answers /rest/v1, /auth/v1 and /functions/v1 under that path for outside clients and at the root for the database's own consumers on its host (its functions' SUPABASE_URL). A path under /rest, /auth, /functions or /stado is refused, since the gateway serves its own routes there. The item gains url (that URL, under the name a hosted provider's item gives its project URL, so a consumer that reads <name>-database#url reads it whoever serves the database; an api_url an earlier gateway wrote is taken off), gateway_url (http://<the host's routable address>:<port>), gateway_port and gateway_version; stado database client <name> grants url to the library client. Placing or retiring the REST API or sign-in later writes the configuration again and restarts the unit, so the gateway routes what stands behind it. A placement with the same port and URL is reported reused; --reconfigure writes it again.

To answer from the internet the gateway is published like any Stado surface, as a public origin named <name>-gateway on the database's host:

stado web origin declare <name>-gateway --hostname <host>.<tailnet>.ts.net --target <host> \
  --publication tailscale-funnel --upstream http://127.0.0.1:<port> --path /auth/v1 --path /rest/v1
stado web origin converge <name>-gateway --apply

An origin named for a database's gateway is published only when the product catalog says why: the product named like the database states database_gateway.public.reason in catalog/products.yml. The origin's target must be the item's host and its upstream port the item's gateway_port.

stado database gateway retire <name> stops and forgets <name>-gateway and takes url, gateway_url, gateway_port and gateway_version off the item; the REST API and sign-in stay at their own addresses.

Source: this website