managarten/services/mana-geocoding/src/lib/privacy.ts
Till JS 2bbcf14aba chore(geocoding): remove Pelias + close 3 bypass paths to public Nominatim
Pelias was retired from the Mac mini on 2026-04-28; photon-self
(self-hosted Photon on mana-gpu) has been the live primary since then.
This removes the now-dead Pelias adapter, config, tests, and the
services/mana-geocoding/pelias/ stack — the entire compose file, the
geojsonify_place_details.js patch, the setup.sh import script.

Provider chain is now `photon-self → photon → nominatim`. The chain
keeps its `privacy: 'local' | 'public'` split, sensitive-query
blocking, coord quantization, and aggressive caching unchanged.

Three direct calls to nominatim.openstreetmap.org that bypassed
mana-geocoding now route through the wrapper:

- citycorners/add-city + citycorners/cities/[slug]/add use the shared
  searchAddress() client (browser → same-origin proxy → mana-geocoding
  → photon-self).
- memoro mobile drops its OSM reverse-geocoding fallback entirely;
  Expo's on-device reverse-geocoding stays as the sole path. Routing
  through the wrapper would require a memoro-server proxy endpoint —
  a follow-up if Expo's quality proves insufficient.

Other behavioral changes:

- CACHE_PUBLIC_TTL_MS dropped from 7d → 1h. The long TTL was a
  privacy-amplification trick from the Pelias era; with photon-self
  serving the bulk of traffic, a transient cross-LAN blip was pinning
  cached fallback answers for days. 1h gives quick recovery.
- /health/pelias renamed to /health/photon-self; prometheus blackbox
  config + status-page generator updated.
- mana-geocoding container no longer needs `extra_hosts:
  host.docker.internal:host-gateway` (was only there for the
  Pelias-on-host-network era).

113 tests passing. CLAUDE.md rewritten to reflect the post-Pelias
architecture.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:12:26 +02:00

45 lines
1.8 KiB
TypeScript

/**
* Privacy helpers for outbound public-API requests.
*
* The provider chain quantizes coordinates before forwarding to public
* geocoding endpoints (Photon, Nominatim) so the user's precise position
* doesn't end up in a third party's logs.
*
* Quantization rules:
* - **Forward search focus** (`focus.lat/lon`): 2 decimals ≈ 1.1 km
* resolution. Enough to bias results "near you" without revealing
* home/workplace addresses.
* - **Reverse-geocoding coords** (`lat/lon`): 3 decimals ≈ 110 m
* resolution. Trades a small amount of accuracy for the privacy of
* not telling Photon "user is at THIS HOUSE". Reverse geocoding
* against the city block instead of the building is acceptable.
*
* Photon-self and other LAN-local providers always get the original
* full-precision coordinates — quantization only applies on the way
* out to the public internet.
*/
/** ~1.1 km resolution. Enough for "results near me" biasing. */
export const PUBLIC_FOCUS_DECIMALS = 2;
/** ~110 m resolution. Identifies city block, not building. */
export const PUBLIC_REVERSE_DECIMALS = 3;
/**
* Round a coordinate to `decimals` decimal places. Accepts string or
* number to make the call sites clean (the wrapper passes strings
* straight from query params; tests use numbers).
*
* Returns a string so the caller can drop it directly into
* URLSearchParams without re-stringification (which would otherwise
* undo the quantization at toString-time).
*/
export function quantizeCoord(
value: string | number | undefined,
decimals: number
): string | undefined {
if (value === undefined || value === null || value === '') return undefined;
const n = typeof value === 'string' ? parseFloat(value) : value;
if (!Number.isFinite(n)) return undefined;
return n.toFixed(decimals);
}