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:
API access is authenticated (shared secret / API key / mTLS — pick one).
Calls can be load-balanced across multiple Nominatim instances (or a single VIP in front of N backends) when we scale beyond one box.
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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:
Same pattern should land on Ditch_The_Agent/OSRM (sibling routing service).
Current state
ai-server-4080→10.0.0.128:808910.0.0.0/24only (good first gate; not app auth)NOMINATIM_BASE_URL+ optionalNOMINATIM_API_KEYalready forwarded asX-API-Keyif present (gateway not implemented yet)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):
X-API-Key(orAuthorization: Bearer …) on/search,/reverse, etc.401/403Optional 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:
Options to evaluate (pick one in impl):
nginx/caddyupstreams to NnominatimreplicasConstraints:
/statusor cheap/search?q=test&limit=13. Consumer contract
Document for app teams:
Django already supports forwarding
X-API-Key. Mirror the same env names in other services.Acceptance criteria
/search+/reverse.X-API-Key,*_BASE_URL,*_API_KEY).Non-goals (this ticket)
.osrm*graphs with NominatimRelated
/api/address-suggest/backend proxyTwin ticket: Ditch_The_Agent/OSRM#2