★ New · build option

The whole engine. None of the desktop.

Every API, geoprocessing routine, analytic, and serving endpoint 3D Map Explorer runs on Windows also runs as a headless Linux service — shipped as .deb and .rpm packages, a Helm chart for Kubernetes, and Docker images. The ArcGIS Maps SDK viewer is not part of this build, which is exactly the point: bring your own renderer. Pair it with MapLibre GL Native on QNX or hardened Linux for vehicle, aircraft, and industrial deployments — or run it as pure back-end services with no map client at all.

DEB · Ubuntu/DebianRPM · RHEL 9 familyHelm · Kubernetes DockersystemdAir-gappedRenderer-agnostic
Not a fork — a build option

One source tree. Two deployment shapes.

This is not a stripped rewrite or a parallel codebase to keep in sync. The desktop application was always a thin native shell talking HTTP to an in-process server — so the headless build simply constructs that same host without the shell. Same engines, same routes, same contracts, built from the same commit.

  • The engine tier is the product — raster, stereo, footprint, SAR, classification, hyperspectral, mensuration, point-cloud and roof engines all compile cross-platform and run unchanged.
  • The identical REST + SSE surface — roughly 90 route modules and the streaming endpoints answer on :5059 exactly as they do on Windows. Existing clients need no changes.
  • The Windows desktop path is untouched — the desktop build still targets Windows, still uses DPAPI, still behaves identically. Adding Linux took nothing away.
  • Honest degradation, never silent failure — the handful of capabilities backed by Windows-only vendor libraries return a documented 503 with an install hint instead of pretending to work.
one tree · two targets
// the desktop build — unchanged
native shell → LocalMapServerHost  (in-process)
               net10.0-windows · DPAPI secrets

// the headless build — same host, no shell
Program.cs   → LocalMapServerHost  (console)
               net10.0 · linux-x64 · AES secrets

● identical engines
● identical REST + SSE routes on :5059
✕ no desktop shell · no ArcGIS Maps SDK
Packaging & deployment

Install it the way your platform expects

Native packages for the two enterprise Linux families, a Helm chart for clusters, and container images for everything else — including an air-gapped variant with the full GDAL stack and pre-seeded extensions baked in.

📦

DEB package

Ubuntu / Debian amd64, self-contained — no .NET runtime to install first. Ships the hardened systemd unit and depends only on stock system libraries.

🎩

RPM package

Built for the RHEL 9 family against Rocky Linux 9, staged from the same self-contained publish, with %config(noreplace) operator config and systemd preset integration.

☸️

Helm chart

A Kubernetes chart for the server workload — ConfigMap-driven settings, a persistent volume for on-disk state, health probes, and ingress with TLS terminated in front of a plain-HTTP pod.

🐳

Docker images

A baseline linux-x64 image plus an air-gapped variant on a pinned full-GDAL base, with the DuckDB spatial extension pre-seeded so first run never reaches for the network.

🔐

Hardened by default

Runs as an unprivileged mapexplorer system user under NoNewPrivileges, ProtectSystem=strict, ProtectHome and PrivateTmp, with a single writable data path.

🗝️

Portable secret envelope

Where Windows uses DPAPI, Linux encrypts the credential stores with AES-GCM keyed by a 32-byte file — mountable as a Kubernetes secret, backed up like any other key.

Packages install binaries under /opt/3dmapexplorer, operator configuration under /etc/3dmapexplorer, and durable data under /var/lib/3dmapexplorer — which package removal never deletes. The service is enabled but deliberately never auto-started, so an operator reviews the environment file before anything binds a port.

Bring your own front end

Serve a web mapping app straight from a folder

Because no viewer ships with this build, the server hosts yours. Drop a compiled web application into a watched folder and it is served at its own route — no rebuild of the server, no bundler step, no reverse proxy to configure. MapLibre, DeckGL, Cesium, TerriaJS, or anything else that compiles to static files.

  • Folder-driven registration — each first-level subfolder of the web-apps root becomes an app with a slug derived from its name; an optional per-app manifest overrides the slug, index file, or SPA behavior.
  • SPA-aware — opt into client-side routing per app so deep links resolve to the index instead of returning a 404.
  • Locked to its root — traversal is stopped by the file provider, manifests cannot aim an index outside the app folder, and directory listing does not exist anywhere in the subsystem.
  • Write surface is opt-in — registering absolute paths over REST stays disabled by default, matching the house rule for every write-capable endpoint.
webapps/ → /apps/{slug}/
# drop a built bundle in, it is served
webapps/
 ├─ fleet-maplibre/    → /apps/fleet-maplibre/
 │   ├─ index.html
 │   ├─ assets/ style.json glyphs/
 │   └─ tmg-app.json    (optional manifest)
 ├─ terrain-cesium/    → /apps/terrain-cesium/
 └─ ops-console/       → /apps/ops-console/

# the management surface
GET    /api/webapps
GET    /api/webapps/capabilities
DELETE /api/webapps/{slug}

● no directory listing · root-contained
The pairing

A back end that does not care what draws the map

The server speaks open standards on the wire — vector tiles, OGC API Features, ArcGIS REST, imagery and elevation endpoints. Any client that reads those can be the renderer, which opens deployments the desktop build was never shaped for.

🚗

Vehicle & platform

Run the services on the platform computer and render with MapLibre GL Native on a QNX or hardened-Linux head unit — a real-time-capable display stack fed by a full geospatial back end.

✈️

Airborne & mission systems

Mission computers that already have their own certified rendering path get imagery, terrain, features, and analytics from a service they can package, sign, and audit like any other.

🏭

Industrial & edge

Ruggedized gateways and control rooms where a Windows workstation is not an option, but OGC data services and on-device analytics still are.

🧰

Pure back-end services

No map client at all — schedule geoprocessing, publish tiles to an existing portal, or feed an analytics pipeline. The REST surface is the product.

🖥️

Thick client, remote engine

Point the Windows desktop application at a remote headless server and it works unchanged — it was always an HTTP client.

🧩

Existing GIS clients

QGIS and ArcGIS Pro connect to the same endpoints as ordinary services, so the server slots into a stack you already run.

What runs where

The Linux capability matrix, stated plainly

Almost the entire processing core is pure C# over cross-platform natives, so it moves without compromise. The exceptions are the capabilities backed by Windows-only vendor libraries — and they are documented, not hidden.

CapabilityOn LinuxNotes
Raster core — GeoTIFF / COG / JP2KFullCross-platform GDAL natives
DuckDB spatial — MVT, FeatureServer, OGC API FeaturesFullPre-seed the extension for air-gapped hosts
Image classification — SAM, MaxLik, RF, SVM, XGBoost, K-meansFullPure C# plus the in-process tree walker
Stereo DSM, footprint & scene fusionFullPure C# plus GDAL
SAR amplitude, dB conversion & speckle filteringFullPure C#
ImageServer — exportImage, identify, mensurationFullAudited: zero Windows-native dependencies
AI detection sidecarFullRun the sidecar container alongside
SNAP / Snaphu / roofer / FFmpeg sidecarsFullAll ship Linux builds; swap the configured paths
Dashboard, job board & collects feedFullVanilla JS, served by the host
Esri mobile geodatabase — .geodatabase / .mmpk503Vendor library is Windows-only; no Linux build exists
LiDAR → I3S scene-layer package export503Encoders are Windows-only until built from source
Tradecraft GDAL formats — NITF / CADRG / CIB / RPF503Enabled by dropping in a full GDAL build — no code change

Capability status reflects the platform's own Linux deployment matrix. Two of the three gaps are unlockable by supplying the relevant native builds at deploy time; the third depends on a proprietary library with no Linux distribution. Credentials encrypted on Windows with DPAPI are re-entered once during migration, after which they are readable on both platforms.

Operations

Configured by environment, like everything else in the rack

Every setting in the configuration file is overridable by environment variable, so one image serves a laptop container, a systemd host, and a cluster deployment without a bespoke build for each.

  • Standard health endpoint — a plain HTTP health route the orchestrator, load balancer, or monitoring agent can probe.
  • Single-writer state, honestly documented — projects, GeoPackages, DuckDB files, and job registries are on-disk single-writer state, so the chart defaults to one replica rather than pretending to scale horizontally.
  • TLS where it belongs — terminate at the ingress and keep the pod plain HTTP, or bind the listener directly for standalone hosts.
  • Optional shared-secret key — gate non-loopback API clients while leaving health and dashboard reachable.
systemd · unprivileged
# install, then review before anything binds
$ sudo apt install ./3dmapexplorer-server_*.deb
$ sudoedit /etc/3dmapexplorer/server.env
$ sudo systemctl start 3dmapexplorer

# the hardening the unit ships with
User=mapexplorer
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/3dmapexplorer

● data survives remove and purge

Same capability. New places to put it.

A desktop when you want the full workstation, a Linux service when you want the engine — packaged for the platform you actually deploy on, rendering with whatever draws your maps.