fix(geo): read the Nominatim override the way the schema validates it

Follow-up to #2379.

NOMINATIM_URL arrives from a compose file or a ConfigMap with whatever
whitespace was typed around it. The schema validates the trimmed string and
calls a blank one unset, the derivation did neither, so " " passed startup as
"use the default" and then became the base URL that cannot be fetched, and a
padded value lost every geocoding call the same way. It trims now, which is
also what the places client next door does with its own override.

The chart could not pass the variable at all: the ConfigMap is an allowlist and
an unknown key is dropped without a word, which is exactly the locked-down
cluster the override exists for.

The docs said what to set and not what it costs. They now say that it replaces
the public service with no fallback, that the endpoint has to speak the
Nominatim API, and that the one-per-second spacing stays either way, so nobody
stands up their own instance expecting it to run faster.

Test IDs follow the file again.
This commit is contained in:
Maurice
2026-09-16 11:08:07 +02:00
parent c0195557d4
commit 61a8a2765b
8 changed files with 65 additions and 24 deletions
+4 -1
View File
@@ -265,7 +265,10 @@ export function deriveIntegrations(raw: RawEnv) {
return {
unsplashAccessKey: raw.UNSPLASH_ACCESS_KEY?.trim(),
transitApiBase: stripTrailingSlashes(raw.TRANSIT_API_URL || 'https://api.transitous.org'),
nominatimUrl: stripTrailingSlashes(raw.NOMINATIM_URL || 'https://nominatim.openstreetmap.org'),
// Trimmed before the default fires: the schema validates the trimmed value and
// treats a blank one as unset, so a padded or whitespace-only value would
// otherwise pass startup and then be the string that cannot be fetched.
nominatimUrl: stripTrailingSlashes(raw.NOMINATIM_URL?.trim() || 'https://nominatim.openstreetmap.org'),
overpassUrl: raw.OVERPASS_URL,
// Longer than the `[timeout:20]` the query itself carries, or we abort an answer the
// mirror was still allowed to be working on. See OVERPASS_QUERY_TIMEOUT_S.
+6
View File
@@ -163,6 +163,12 @@ export interface NominatimOptions {
* The abort signal is constructed after the throttle wait on purpose: building it
* first would spend up to 1.1s of the caller's budget queueing rather than
* fetching, which for the 2.5s identity lookup is nearly half of it.
*
* The base is `NOMINATIM_URL` where an operator set one and the public service
* otherwise, read live rather than frozen at import so a test (and a restart-free
* change) is seen on the next call. Neither the cursor nor the User-Agent asks
* which one it got: the spacing is what keeps an operator's installs inside the
* policy, and that holds for a gateway in front of the public service too.
*/
export async function nominatimFetch(
path: 'search' | 'reverse' | 'lookup',