Architecture
Dawarich Atlas is built around eight design principles:
- Local-first. Every layer runs on your hardware. No outbound API calls at runtime.
- Open data only. OSM, SRTM, GTFS — all freely downloadable.
- MapLibre GL as the renderer. Vector-first.
- PMTiles as the tile format. Static files, no tile server process.
- Minimal overlap, minimal resource consumption. Each service owns a distinct query type.
- Single compose file. One
compose.yml, bind mounts under./data/, region selected via.env. - Caddy as the edge. Auto-HTTPS-ready, range requests + compression out of the box.
- One Rails codebase + one Go sidecar. The Rails layer is the public surface (API + admin UI + Turbo Streams). A small Go service (
atlas-control) owns the Docker socket so Rails never has to.
Topology
┌────────────────────────┐
│ Browser (MapLibre) │
└───────────┬────────────┘
│
▼
┌──────────────┐
│ Caddy │ TLS + /tiles/* static
└───┬────────┬─┘
│ │
/tiles/* │ │ /*
▼ ▼
┌────────┐ ┌──────────────────┐ ┌──────────────────┐
│ PMTiles│ │ Dawarich Atlas │ ──HTTP→ │ atlas-control │
│ static │ │ (Rails: API + │ │ (Go sidecar, │
│ │ │ admin UI) │ │ docker socket) │
└────────┘ └────────┬─────────┘ └──────────────────┘
│
fan-out (internal Docker network only):
│
┌────────┬─────────────┬────────────┼────────────┬────────┐
▼ ▼ ▼ ▼ ▼ ▼
photon placeholder libpostal valhalla overpass otp
Only Caddy is published on host port 8484. Every other service is reachable only on the internal Docker network.
Layers
| # | Layer | Component | Data source |
|---|---|---|---|
| 1 | Base map tiles | Protomaps PMTiles | OSM planet (Protomaps daily build or self-built via Planetiler) |
| 2 | Tile serving | Caddy static + range + CORS | n/a |
| 3 | Map style | protomaps-themes-base (5 themes) | github.com/protomaps/basemaps |
| 4 | Geocoding | Photon + Placeholder + libpostal | OSM + Who's on First |
| 5 | Routing | Valhalla | OSM PBF + SRTM DEM |
| 6 | Elevation | Bundled with Valhalla | SRTM 1-arcsec |
| 7 | Terrain / hillshade (planned) | Terrain-RGB raster tiles | SRTM → rio-rgbify → PMTiles |
| 8 | POI lookup | Overpass API self-hosted | OSM PBF |
| 9 | Transit | OpenTripPlanner 2 | GTFS + OSM |
Why these engines
| Pick | Reasoning |
|---|---|
| Photon over Nominatim | ~70 GB prebuilt index vs Nominatim's ~1 TB Postgres. Same OSM source, much smaller box. |
| Placeholder | Resolves admin-hierarchy (country / state / city) when Photon's OSM tags are thin. |
| libpostal | Statistical address parser. Normalises long structured queries before they hit Photon. |
| Valhalla over OSRM | Multimodal in one binary, built-in elevation, MIT license, ferry/toll/highway avoidance. |
| Overpass | Tag-aware queries over OSM. Self-updates from minute diffs. |
| OpenTripPlanner 2 | The reference open-source multimodal planner. GTFS + OSM in a single graph. |
| MapLibre GL JS | The open fork of mapbox-gl. Reads PMTiles natively. |
| Caddy | Auto-HTTPS, range requests + compression for *.pmtiles, ergonomic config. |
App layer
Dawarich Atlas's job is to orchestrate: it exposes a clean /api/v1/* surface, fans out to the right engines, normalises shape, and degrades gracefully when an upstream is down. The internal client classes are thin:
PhotonClient,ValhallaClient,OverpassClient,OtpClient— direct HTTP wrappers.SearchOrchestrator,ReverseOrchestrator,BatchReverseGeocoder— composition (Photon + Placeholder + cache + grid-snap).ControlPlaneClient— talks to theatlas-controlGo sidecar that owns docker socket access.
The map UI is Hotwire (Turbo + Stimulus) with a MapLibre Stimulus controller. Admin operations stream over Action Cable / Turbo Streams.
The atlas-control Go sidecar
Atlas never touches the Docker socket directly. A separate Go service — atlas-control — owns that responsibility and exposes a small HTTP API the Rails layer calls into. The split exists for three reasons:
- Privilege boundary. The Rails container ships without
/var/run/docker.sockmounted. Onlyatlas-controlhas the socket. If the Rails app is ever compromised, the attacker still can't spawn or stop containers directly. - Operational surface.
atlas-controlis the thing that boots / stops profiles (docker compose --profile geocoding up -d), tails service logs, downloads region PBFs, fetches PMTiles basemaps, and reports status back over a streaming endpoint. - Independent release cycle. The sidecar ships as its own container image. Bumping the sidecar doesn't require redeploying Atlas, and vice versa.
What flows through the sidecar:
| Triggered from Rails | Sidecar action |
|---|---|
POST /admin/services/:name | docker compose --profile <X> up -d <name> or … down <name> |
GET /admin/services/:name/logs | tail container logs, stream over HTTP |
POST /admin/apply | apply region change + start projected service set |
POST /admin/regions | swap regional bind-mount data + signal dependent services |
POST /admin/tiles/download | pull a PMTiles file (Protomaps daily, custom URL, etc.) into data/tiles/ |
GET /admin/tiles | report state of the local basemap file (exists, size, freshness) |
The sidecar source lives at atlas-control/ in the main repo. It uses the Docker Engine API directly (no shelling out to docker compose); a small embedded YAML parser reads the same compose.yml Caddy + Atlas already use.