Offline-First Design
Every feature works without internet. The system operates on a local network created by the server hardware.
7 min readPhilosophy
Offline-first is GroundWave's foundational constraint, not a feature bolted on after the fact. Every architectural decision is evaluated against a single question: does this work with zero internet connectivity?
This means:
- No cloud dependencies. There are no SaaS APIs, external CDNs, or third-party services in the critical path. The application does not phone home, validate licenses, or pull runtime configuration from remote servers.
- No external CDNs. The compiled frontend bundle, all fonts, and all static assets are served from the local Nginx container. Nothing is loaded from
cdn.jsdelivr.net,unpkg.com, or any other external host. - Everything runs locally in Docker containers. The web server, application, database, and tile server are all self-contained. A fresh deployment from the release archive requires no outbound network access whatsoever.
- Internet extends; it does not enable. When connectivity exists, optional features like online map style fallback and server federation become available as enhancements. Their absence never degrades core functionality.
This philosophy is why GroundWave is suitable for search and rescue, disaster response, military operations, and other environments where network infrastructure cannot be assumed.
Server-Side
The server half of the offline-first design is straightforward: all backend services are containerized and run entirely locally. No component makes outbound network calls during normal operation.
Map Tiles from Local Storage
Map tiles are served by TileServer GL from pre-downloaded .mbtiles or
.pmtiles archive files stored on a Docker volume. The setup wizard provides
a tile downloader that fetches tiles from public sources during initial setup — but once
downloaded, no internet is required to serve them. A single archive covering a country
or region typically ranges from a few hundred megabytes to several gigabytes.
Multiple tilesets can be managed and switched at runtime from the admin dashboard. The active tileset persists across server restarts.
Local PostgreSQL Database
All operational data — positions, messages, markers, files, overlays — is stored in a local PostgreSQL instance with PostGIS. The database runs in its own container and is mounted on a persistent Docker volume. Data survives container restarts and upgrades.
No Required Outbound Calls
The application server makes no outbound HTTP or DNS requests during normal operation. The only exceptions are opt-in features:
- Online map tile fallback (disabled automatically when local tiles are available)
- Server federation outbound connections (requires explicit peer configuration by an admin)
- Tile downloader during initial setup (one-time, then no longer needed)
Client-Side Resilience
The client is a single-page React application delivered from the server. Once loaded, it implements multiple layers of local caching so that a temporary loss of connectivity to the GroundWave server — for example, a user moving to the edge of Wi-Fi range — does not immediately destroy the operational picture.
Service Worker Tile Caching
A registered service worker intercepts all tile fetch requests and applies a CacheFirst strategy: if the requested tile is in the cache, it is returned immediately without a network round trip. If not, it is fetched from the server and simultaneously stored in the cache for future use.
Cache configuration:
- Cache name:
groundwave-tiles-v1 - Maximum entries: 5,000 tiles
- Expiry: 30 days from last access
- Eviction: Least-recently-used when the entry limit is reached
In practice this means that map areas a user has already panned across will continue to render correctly even if the connection to the server drops. For field units that operate in a fixed area, the relevant tiles will be cached after the first session and remain available indefinitely.
Service worker tile caching applies to both locally-served tiles from
groundwave-tiles and to online tile sources used as fallback. Tiles from
any source that have been fetched previously are served from the cache first.
IndexedDB Data Cache
In addition to tile caching, the client maintains a structured local data cache in
IndexedDB using the idb library. This cache stores the full operational
dataset that has been fetched from the server, organized into seven object stores:
| Store | Contents |
|---|---|
markers |
All map markers (points, lines, polygons) with categories and coordinates |
channels |
Chat channel list and metadata |
messages |
Recent chat messages per channel (bounded history) |
roster |
Connected user list and last-known positions |
meta |
Cache timestamps, version, and sync state |
queue |
Pending operations to be replayed on reconnect |
All data hooks follow a cache-first, write-through pattern. When the client loads data (e.g., the marker list), it immediately renders from the local IndexedDB cache while simultaneously fetching fresh data from the server in the background. When the server responds, the cache is updated and the UI is refreshed. This eliminates loading spinners on subsequent visits and makes the application feel instant.
When the client receives a real-time update via Socket.IO (e.g., a new marker was created by another user), the update is applied to both the in-memory React state and the IndexedDB cache simultaneously, keeping the persistent cache in sync with the live operational picture.
Offline Operation Queue
When the client detects that it has lost its Socket.IO connection to the server, it
enters offline mode. Any operations the user performs — sending a chat message, creating
a marker, updating or deleting existing features — are not dropped.
Instead they are serialized and appended to the queue object store in
IndexedDB.
The queue is a strict FIFO (first-in, first-out) structure. Each entry records:
- The operation type (e.g.,
chat:send,marker:create) - The full payload that would have been sent to the server
- A timestamp for ordering and potential expiry
When the Socket.IO connection is re-established, the client automatically drains the queue in order, replaying each operation against the server. The user does not need to manually retry. From their perspective, the action they took while offline was applied — it just takes a moment to sync after connectivity returns.
If the server has been offline for an extended period and another user has made conflicting changes (e.g., deleted a marker that you also edited offline), the replay may produce an error for that operation. Errors during queue replay are logged and skipped — the remaining queued operations continue to process.
Offline UI Indicators
The client continuously monitors the state of its Socket.IO connection and displays status clearly to the user:
- Offline banner: When disconnected, an amber banner appears at the top of the interface reading "Offline" with a count of pending operations in the queue (e.g., "Offline — 3 operations pending"). This is persistent and visible regardless of which panel or map view is active.
- Tile source indicator: A small indicator in the map controls shows whether map tiles are being served from the local GroundWave tile server or from an online fallback source.
- Cached data remains accessible: Markers, chat history, and the user roster continue to display from the IndexedDB cache. The map continues to render from the service worker cache. The user retains a complete operational picture of the last-known state.
When connectivity is restored, the amber banner disappears, the queue drains automatically, and real-time updates resume. No user action is required.
Online Tile Fallback
When no local tile set has been downloaded and configured, the client automatically falls back to free public tile providers. No API key is required for the fallback sources. Available fallback styles include:
- Street (OpenStreetMap): Standard street map from the OSM tile CDN
- Satellite (ESRI World Imagery): Aerial/satellite imagery from ESRI's public tile service
- Topo (OpenTopoMap): Topographic map with contour lines
- Dark (CartoDB Dark Matter): Dark base map suited for low-light environments
The map style picker in the UI labels online sources so operators always know whether they are viewing tiles from the local server or from the internet. When local tiles are available, the online fallback sources are still accessible as alternatives — useful when the local tileset covers a different geographic area than the current operation.
Once a client has browsed the map using online fallback tiles, those tiles are cached by the service worker. If internet connectivity is then lost, previously viewed areas will still render correctly — even from online tile sources.