navi/backend
2026-06-18 21:09:55 +00:00
..
config Add navi-contacts service (extraction #3) 2026-05-22 10:59:37 -06:00
deploy Add navi-offroute service (extraction #8 — final) (#10) 2026-05-22 23:30:43 -06:00
scripts MVUM Layer 3b: OSM parking as multi-modal Auto transition candidates (#31) 2026-05-26 14:48:35 -06:00
services navi-offroute: HPA tile DB manifest pattern (H6a-pre, unblocks continental rollout) (#53) 2026-06-04 14:23:19 -06:00
shared shared: promote dem.py to shared/ (prep for navi-offroute) (#9) 2026-05-22 22:42:47 -06:00
.gitignore Initial scaffold: navi-backend + navi-traffic (extraction #1) 2026-05-21 22:26:50 -06:00
LICENSE Initial scaffold: navi-backend + navi-traffic (extraction #1) 2026-05-21 22:26:50 -06:00
pyproject.toml feat(offroute): numba A* with anisotropic Tobler, exponentially-inflated cost grid (combined #17+#18) 2026-05-25 15:15:36 +00:00
README.md docs: correct backend README — full 8-service monorepo, navi-traffic retired 2026-06-18 21:09:55 +00:00

navi-backend

Full monorepo for the Navi backend and frontend, extracted from the recon codebase as part of the recon <=> Navi decoupling project.

backend/ contains 8 single-responsibility microservices that together own the /api/* surface that navi.echo6.co depends on; frontend/ is the Vite SPA. All backend services run behind the existing Caddy/Authentik edge.

Note: navi-traffic (the original extraction #1 — TomTom traffic tile proxy) has been retired. Traffic data is now served by the central service (central.echo6.mesh).

Services

# Service Port Description
1 navi-traffic DISABLED — retired, traffic now via central
2 navi-config 8422 Configuration store
3 navi-contacts 8423 Contacts / address book
4 navi-landclass 8424 Land classification lookup
5 navi-places 8425 Places / POI search
6 navi-geo 8426 Geocoding, reverse geocoding, enrichment bundle
7 navi-admin 8427 Fleet admin front door (fan-out to all services)
8 navi-offroute 8428 Off-road / off-network routing

Layout

navi-mono/
├── backend/
│   ├── shared/                  # cross-service helpers, imported by every service
│   │   ├── auth.py              # get_user_id(req), require_auth decorator (Authentik header)
│   │   └── admin_info.py        # build_info_response(), mask_key(), time_dependency()
│   └── services/
│       ├── navi_config/         # :8422
│       ├── navi_contacts/       # :8423
│       ├── navi_landclass/      # :8424
│       ├── navi_places/         # :8425
│       ├── navi_geo/            # :8426
│       ├── navi_admin/          # :8427
│       └── navi_offroute/       # :8428
└── frontend/                    # Vite SPA

Service directories use an underscore (navi_geo) so they are importable Python packages; the service name stays hyphenated (navi-geo) in systemd, nginx, and the admin-info service field.

Setup

Single workspace, single virtualenv:

python -m venv .venv
.venv/bin/pip install -e .

Test

.venv/bin/pytest services/navi_geo/tests/ -v

Run (local) — navi-geo (extraction #6)

.venv/bin/pytest services/navi_geo/tests/ -v

# All paths/URLs are env-overridable (see deploy/env/navi-geo.env.example).
# No secrets — landclass is HTTP-delegated to navi-landclass (:8424).
.venv/bin/gunicorn 'services.navi_geo.app:create_app()' \
    --bind 127.0.0.1:8426 --workers 2

navi-geo serves /api/geocode, /api/reverse?lat=&lon=, and the reverse enrichment bundle /api/reverse/<lat>/<lon> (Central's 9-key contract). All public. The reverse bundle fans out to Photon, the SpatiaLite timezone DB, navi-landclass (HTTP), and the planet-DEM PMTiles — each degrading to null independently, never 5xx.

Run (local) — navi-admin (extraction #7)

.venv/bin/pytest services/navi_admin/tests/ -v

# No secrets — read-only HTTP fan-out over localhost (see
# deploy/env/navi-admin.env.example). Owns no DB.
.venv/bin/gunicorn 'services.navi_admin.app:create_app()' \
    --bind 127.0.0.1:8427 --workers 2

navi-admin is the fleet admin front door: /api/admin/fleet fans out to every navi-* service's localhost /api/admin/<svc>/info + recon's /api/health (merged, never 5xx — failures land in errors[]); /api/admin/recon/info wraps recon's health into the uniform shape; /api/admin/navi-admin/info self-describes. All @require_auth. The per-service admin endpoints stay localhost-only; this is the single edge-exposed admin surface (needs a Caddy @authed_api edit — see deploy/caddy/navi-admin.caddy.notes.md).

Run (local) — navi-offroute (extraction #8)

.venv/bin/pytest services/navi_offroute/tests/ -v

# All paths/URLs env-overridable (deploy/env/navi-offroute.env.example).
# No secrets — PADUS via libpq peer-auth (dbname=padus). DEM via shared/dem.py.
# Needs osmium-tool on the host + scikit-image/rasterio in the venv.
.venv/bin/gunicorn 'services.navi_offroute.app:create_app()' \
    --bind 127.0.0.1:8428 --workers 2 --timeout 130

navi-offroute serves POST /api/offroute (off-network effort-based routing — in-Python least-cost path over a DEM/friction/barriers/trails/MVUM cost grid, stitched to the road network via Valhalla) and GET /api/mvum (Motor Vehicle Use Map road/trail access lookup). Both public. The ^~ /api/offroute nginx block needs a long proxy_read_timeout (130s); routes can take ~2 min.

The admin-info convention (section 4.5)

Every service exposes GET /api/admin/<service-name>/info, gated by require_auth, returning a uniform shape: service, version (git SHA), port, config, env (names + masked values), dependencies (upstream health checks), filesystem, runtime (uptime / request count / last error). No aggregator — a future admin panel fans out to each service in parallel.