Architecture
How MappView is deployed and how the codebase is organized. Two views, same system.
Delivery
MappView ships three ways from one Bun workspaces monorepo. The viewer is a React app served as static files by nginx (Docker) or installed as a Tauri v2 desktop app for Windows, macOS and Linux. The management console and the share/admin API deploy as containers, and a Jupyter anywidget embeds the viewer in notebooks. Distribution is closed-source: Docker images plus signed desktop installers, no app stores and no update servers.
Runtime topology
flowchart LR
subgraph clients["Clients"]
Browser["Browser · PWA"]
Desktop["Desktop · Tauri v2"]
Jupyter["Jupyter · anywidget"]
Console["Management console"]
end
subgraph viewer["Viewer stack · nginx"]
Static["Static React app"]
Sidecar["FastAPI sidecar · /sidecar
Whitebox · conversion · raster · SQL"]
Static --> Sidecar
end
subgraph control["Control plane"]
Admin["Management API · Bun
SQLite share.db · Clerk auth"]
RustFS["RustFS · S3 object store
layers · photos · share blobs"]
Plugins["Plugin registry CDN
registry.json"]
end
subgraph ai["AI relay"]
Edge["Edge proxy · Bun
model routing · rate limits"]
EdgeRedis["Redis"]
end
subgraph geonode["GeoNode stack · hosted-layers profile"]
Django["GeoNode Django + bridge API"]
PostGIS["PostGIS"]
GeoServer["GeoServer"]
Celery["Celery + Redis"]
end
Browser --> Static
Desktop --> LocalSidecar["Local sidecar · 127.0.0.1:8765"]
Jupyter --> Static
Console --> Admin
Admin --> RustFS
Admin --> Django
Admin --> Edge
Edge --> EdgeRedis
Django --> PostGIS
Django --> GeoServer
Django --> Celery
Browser -->|"hosted layers WMS/WFS/tiles"| GeoServer
Trust boundaries
- The desktop app talks to its local sidecar over
127.0.0.1only; heavier Python processing never leaves the machine unless a remote processing service is configured. - The viewer container reaches the sidecar same-origin at
/sidecar— no CORS surface. SetMAPPVIEW_DISABLE_SIDECAR=1to serve static files only. - The management API fronts object storage with signed, time-limited layer URLs (
LAYER_ACCESS_TTL_SECONDS) and per-tenant storage quotas. - The edge AI proxy authenticates viewers with a token, allowlists models, and rate-limits per client; provider keys never reach the browser.
Workspace layout
flowchart TD
subgraph apps["apps/*"]
WebDesktop["web-desktop · shell, Tauri I/O, composition"]
ManagementApp["management console"]
Docs["docs · this site"]
end
subgraph packages["packages/*"]
Core["@mappview/core · domain types, project schema, runtime, sessions"]
MapPkg["@mappview/map · MapLibre lifecycle, layer sync, globe"]
UiPkg["@mappview/ui · shadcn-style primitives"]
Proc["@mappview/processing · client-side algorithms"]
PluginsPkg["@mappview/plugins · plugin API, built-in plugins"]
end
subgraph services["services/*"]
EdgeSvc["edge · AI proxy"]
MgmtSvc["management · share/admin API"]
Bridge["geonode-bridge · Django app"]
end
subgraph backend["backend/mappview_server"]
FastAPI["FastAPI sidecar · Whitebox, conversion, raster, /sql, /vector"]
end
WebDesktop --> Core
WebDesktop --> MapPkg
WebDesktop --> UiPkg
WebDesktop --> PluginsPkg
PluginsPkg --> Core
Proc --> Core
MapPkg --> Core
MgmtSvc --> Bridge
State flow
sequenceDiagram
participant User
participant UI as Panels / Add Data
participant Session as ProjectSession
participant Doc as Document store + Travels
participant Sync as MapController.syncLayers
participant ML as MapLibre · deck.gl · Cesium
User->>UI: Add data / style / reorder
UI->>UI: Parse or convert (DuckDB-WASM Spatial)
UI->>Session: Document command (createGeoJsonLayer…)
Session->>Doc: Record change (undoable)
Doc-->>Sync: MappViewLayer records
Sync->>ML: Add/update/remove sources and layers
ML-->>User: Rendered map
Engines
- Vector conversion: DuckDB-WASM Spatial in-browser; GeoPandas/Shapely on the sidecar or the same
vector_ops.pymodules in Pyodide — one source of truth, three runtimes. - Spatial SQL: DuckDB, PostGIS (PGlite in-browser) and Apache Sedona (SedonaDB on the sidecar, CereusDB WASM in-browser), with automatic sidecar-first fallback.
- Rendering: MapLibre GL JS primary; deck.gl for raster, point-cloud and 3D overlays; CesiumJS globe as a per-pane view mode behind a lazy ~4.8 MB chunk and an optional Ion token.
- Contracts: Effect v4 owns programs, schemas and typed errors; TanStack Query owns async lifecycle; TanStack Store holds runtime state. The persisted artifact is the engine-agnostic
.mappview.jsonproject (format 0.2.0).