Connect to Postgres by parts, and publish loopback on both addresses - #384
Conversation
Two faults that each stop a deployment reaching its own database, found by running the stack on Windows. Bun reads a Postgres URL's path, the database name, as the path of a unix socket, ignoring host and port, so `postgres://user:pass@host:5432/openbot` cannot connect on Windows at all (oven-sh/bun#27713). It looks like a network fault: psql inside the container works, and a plain TCP connection from the same machine works. The address now goes in parts. Passing the parts is not enough on its own. Bun prefers $DATABASE_URL to the options it was given, so the address goes straight back through the parser this avoids. The variable is removed from the environment as it is read; loadConfig has already captured it and the worker reads it into a local before opening a database. That also stops a later bare `new SQL()` connecting somewhere nobody named. A URL with no host, or no database, is refused by name rather than silently resolving to a default. Separately, Compose published loopback ports on 127.0.0.1 only, and `localhost` is not one address: it resolves to ::1 and 127.0.0.1 in an order the platform decides, and Bun and Node both fail outright rather than trying the other. The same configuration therefore worked on one machine and not the next. Every loopback port is published on both now, which is still loopback. Verified locally: with both published, IPv4, IPv6 and `localhost` all connect.
Verified on Windows: 5 pass, 0 failWindows Server 2022, EC2 bare metal, rootless Podman on WSL2, this branch at
Both fixes were exercised, not sidestepped
Compose published both addresses, as intended: The script asserted both changes were present in the checkout before running, so a green result cannot come from a stale tree. Before this branchSame machine, same everything, while Two failures on the way that were my harness, not the codeRecorded so nobody re-diagnoses them: I first ran the server without |
Taking the address apart dropped everything after the '?'. The profile store's serialization tests set ?application_name= and then look for that session in pg_stat_activity, so losing it turned two lock tests into three second timeouts with nothing in the failure to say why. Bun takes these as `connection`. Anything else Postgres accepts on a URL travels the same way, sslmode included, rather than only the parameter that happened to be caught.
Two faults that each stop a deployment reaching its own database. Both found by running the stack on Windows; the second is not Windows-specific.
1. The server cannot reach Postgres on Windows
Bun reads a Postgres URL's path — the database name — as the path of a unix socket, ignores host and port, and fails to open a socket Windows does not have (oven-sh/bun#27713).
It does not look like a parsing bug. On the same machine, at the same moment:
The address now goes in parts.
Passing the parts is not enough on its own. Bun prefers
$DATABASE_URLto the options it was handed, so the address goes straight back through the parser this exists to avoid. Observed, not assumed: the options form connects from a Bun script with no$DATABASE_URLset, and still failed inside the server, which starts with--env-file, until the variable was gone. So it is removed from the environment as it is read.loadConfighas already captured it, and the worker reads it into a local before opening a database. That also stops a later barenew SQL()connecting somewhere nobody named.A URL with no host, or no database, is now refused by name instead of resolving to a default.
2.
localhostis not one addressCompose published its loopback ports on
127.0.0.1only.localhostresolves to::1and127.0.0.1in an order the platform decides, and Bun and Node fail outright rather than trying the other. So the same configuration works on one machine and not the next, for a reason nothing in the error mentions.Every loopback port is published on both addresses now. Both are loopback; nothing became reachable from another host.
Verified locally, one Postgres, three clients:
and Compose reports
127.0.0.1:5432->5432/tcp, [::1]:5432->5432/tcp.Tests
server/tests/db-client-address.test.ts, five cases: the variable is gone after the call, a non-URL is refused, a URL with no host is refused (it parses — the scheme isopenbot:— and would connect nowhere), a URL with no database is refused, and the existing wrong-way-round guard still fires. That last one caught a real gap while writing them: my first attempt at "not a URL" was a stringnew URLhappily accepts.What is still unproven
Fix 1 is the diagnosis of a Windows failure, and the full Windows smoke journey has not yet been run against this branch. That is next, on a bare-metal Windows instance, and I will report the result on this PR rather than merge on the strength of the reasoning.