API access auth + load-balanced front door for Nominatim #1

Open
opened 2026-08-08 03:23:07 -07:00 by westfarn · 1 comment
Owner

Why

monica_site (and later other LAN apps) call Nominatim only through a Django backend proxy today (/api/address-suggest/http://10.0.0.128:8089). That keeps the browser off Nominatim, but the Nominatim service itself is still a single host + open LAN HTTP endpoint — no app-level auth, no multi-instance front door.

We want to extend this stack so:

  1. API access is authenticated (shared secret / API key / mTLS — pick one).
  2. Calls can be load-balanced across multiple Nominatim instances (or a single VIP in front of N backends) when we scale beyond one box.

Same pattern should land on Ditch_The_Agent/OSRM (sibling routing service).

Current state

  • Host: ai-server-408010.0.0.128:8089
  • UFW: LAN 10.0.0.0/24 only (good first gate; not app auth)
  • Nominatim itself: no native API keys
  • Consumers: Django NOMINATIM_BASE_URL + optional NOMINATIM_API_KEY already forwarded as X-API-Key if present (gateway not implemented yet)
  • Provisioning: server-infra#16

Proposed direction

1. API access layer (in front of Nominatim)

Add a thin edge (prefer Caddy or nginx sidecar in this compose stack, or Traefik if we standardize later):

  • Require X-API-Key (or Authorization: Bearer …) on /search, /reverse, etc.
  • Reject missing/wrong key with 401/403
  • Keep UFW LAN-only as defense in depth
  • Document key issuance + rotation in README; store secrets in server-infra / env files (never commit)

Optional later: mTLS between app hosts and the edge if we harden beyond shared secrets.

2. Load balancing

Design the public URL consumers use as a stable VIP / LB hostname, not a single container IP:

NOMINATIM_BASE_URL=http://nominatim.lan.internal:8089
# or http://10.0.0.128:8089 behind local LB that fans out

Options to evaluate (pick one in impl):

Option Notes
Compose nginx/caddy upstreams to N nominatim replicas Same host first; easy
Keepalived / DNS round-robin across hosts Multi-node later
Existing homelab LB (if any) Prefer reuse

Constraints:

  • Nominatim DB is per instance (heavy). LB ≠ shared DB unless we deliberately run one DB + N API workers (usually not how mediagis image is packaged).
  • Realistic v1: auth edge + single backend; v2: N full stacks (each with own import) behind LB for read fan-out / HA.
  • Health checks: /status or cheap /search?q=test&limit=1

3. Consumer contract

Document for app teams:

NOMINATIM_BASE_URL=http://<lb-or-vip>:8089
NOMINATIM_API_KEY=<secret>

Django already supports forwarding X-API-Key. Mirror the same env names in other services.

Acceptance criteria

  • Edge/proxy in this repo (compose) that authenticates API requests (key or equivalent).
  • Unauthenticated requests from LAN fail; valid key succeeds for /search + /reverse.
  • README documents auth header, key rotation, and that Nominatim itself has no native keys.
  • Stable consumer URL designed for LB (hostname/VIP), not hard-coded sole container assumption.
  • Health-check endpoint documented for LB backends.
  • Skeleton/config for ≥2 upstreams (even if only one active in v1).
  • Coordinate with OSRM twin issue so header/env naming stays consistent (X-API-Key, *_BASE_URL, *_API_KEY).
  • Note server-infra follow-up for UFW / multi-host / secrets once design is chosen.

Non-goals (this ticket)

  • Public internet exposure of Nominatim
  • Replacing Lob CASS / postal certification
  • Sharing OSRM .osrm* graphs with Nominatim

Related

  • Twin: OSRM API access + load balancing (open in Ditch_The_Agent/OSRM)
  • server-infra#16 — Nominatim on ai-server-4080
  • monica_site — /api/address-suggest/ backend proxy
## Why `monica_site` (and later other LAN apps) call Nominatim only through a **Django backend proxy** today (`/api/address-suggest/` → `http://10.0.0.128:8089`). That keeps the browser off Nominatim, but the Nominatim service itself is still a **single host + open LAN HTTP** endpoint — no app-level auth, no multi-instance front door. We want to extend this stack so: 1. **API access is authenticated** (shared secret / API key / mTLS — pick one). 2. **Calls can be load-balanced** across multiple Nominatim instances (or a single VIP in front of N backends) when we scale beyond one box. Same pattern should land on [Ditch_The_Agent/OSRM](https://git.aimloperations.com/Ditch_The_Agent/OSRM) (sibling routing service). ## Current state - Host: `ai-server-4080` → `10.0.0.128:8089` - UFW: LAN `10.0.0.0/24` only (good first gate; not app auth) - Nominatim itself: **no native API keys** - Consumers: Django `NOMINATIM_BASE_URL` + optional `NOMINATIM_API_KEY` already forwarded as `X-API-Key` if present (gateway not implemented yet) - Provisioning: [server-infra#16](https://git.aimloperations.com/ai_ml_operations/server-infra/issues/16) ## Proposed direction ### 1. API access layer (in front of Nominatim) Add a thin edge (prefer **Caddy** or **nginx** sidecar in this compose stack, or Traefik if we standardize later): - Require `X-API-Key` (or `Authorization: Bearer …`) on `/search`, `/reverse`, etc. - Reject missing/wrong key with `401`/`403` - Keep UFW LAN-only as defense in depth - Document key issuance + rotation in README; store secrets in server-infra / env files (never commit) Optional later: mTLS between app hosts and the edge if we harden beyond shared secrets. ### 2. Load balancing Design the public URL consumers use as a **stable VIP / LB hostname**, not a single container IP: ```text NOMINATIM_BASE_URL=http://nominatim.lan.internal:8089 # or http://10.0.0.128:8089 behind local LB that fans out ``` Options to evaluate (pick one in impl): | Option | Notes | |--------|--------| | Compose `nginx`/`caddy` upstreams to N `nominatim` replicas | Same host first; easy | | Keepalived / DNS round-robin across hosts | Multi-node later | | Existing homelab LB (if any) | Prefer reuse | Constraints: - Nominatim DB is **per instance** (heavy). LB ≠ shared DB unless we deliberately run one DB + N API workers (usually not how mediagis image is packaged). - Realistic v1: **auth edge + single backend**; v2: **N full stacks** (each with own import) behind LB for read fan-out / HA. - Health checks: `/status` or cheap `/search?q=test&limit=1` ### 3. Consumer contract Document for app teams: ```bash NOMINATIM_BASE_URL=http://<lb-or-vip>:8089 NOMINATIM_API_KEY=<secret> ``` Django already supports forwarding `X-API-Key`. Mirror the same env names in other services. ## Acceptance criteria - [ ] Edge/proxy in this repo (compose) that authenticates API requests (key or equivalent). - [ ] Unauthenticated requests from LAN fail; valid key succeeds for `/search` + `/reverse`. - [ ] README documents auth header, key rotation, and that Nominatim itself has no native keys. - [ ] Stable consumer URL designed for LB (hostname/VIP), not hard-coded sole container assumption. - [ ] Health-check endpoint documented for LB backends. - [ ] Skeleton/config for ≥2 upstreams (even if only one active in v1). - [ ] Coordinate with OSRM twin issue so header/env naming stays consistent (`X-API-Key`, `*_BASE_URL`, `*_API_KEY`). - [ ] Note server-infra follow-up for UFW / multi-host / secrets once design is chosen. ## Non-goals (this ticket) - Public internet exposure of Nominatim - Replacing Lob CASS / postal certification - Sharing OSRM `.osrm*` graphs with Nominatim ## Related - Twin: OSRM API access + load balancing (open in Ditch_The_Agent/OSRM) - server-infra#16 — Nominatim on ai-server-4080 - monica_site — `/api/address-suggest/` backend proxy
Author
Owner

Twin ticket: Ditch_The_Agent/OSRM#2

Twin ticket: https://git.aimloperations.com/Ditch_The_Agent/OSRM/issues/2
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ai_ml_operations/Nominatim#1