The stack — and the three rules that picked it
Someone asked what the stack is. Fair question. But the list of tools is the boring half of the answer. The interesting half is why these and not the obvious alternatives — because for a ground control station, the constraints do the choosing.
Pick the tool first and you'll fight the constraints forever. So: three rules. Every dependency answers to all three.
License-clean, enforced by CI
This is a defense-and-commercial project. "We'll sort the licenses later" is how you end up unable to ship. So there's an allowlist (licenses.allowed.json) and a check that fails the build if any dependency falls outside it. GPL, AGPL, BSL, SSPL, Elastic-2.0 — out. Permissive only: MIT, Apache-2.0, BSD, ISC.
This rule has teeth, and it's vetoed real tools:
- Mapbox GL JS v2+ — went BSL. Out. Use MapLibre (BSD-3), the community fork from the last open version.
- Ultralytics YOLO (v5/v8) — AGPL. Out. A permissively-licensed detector instead, if and when vision lands.
- Some popular open-weight models — not a software-license problem, an acceptable-use one: certain models' terms restrict defense use, which disqualifies them even when the code license looks fine. Any model would have to clear acceptable-use review first — and that's a decision for later, not a dependency I'm carrying now.
- DJI SDK, Neo4j, libx264/x265 — restrictive or GPL. Out.
And the clean-room rule rides alongside it: every protocol gets re-authored from the public spec. No lifting code from QGroundControl, Mission Planner, or ArduPilot (all GPL). Patterns are ideas; ideas aren't copyrightable. Implementations are not ideas.
It has to run air-gapped
A tactical GCS can't assume the internet exists. No CDN fonts, no map tiles phoned home from someone's cloud, no telemetry beacon. Everything ships in the bundle.
That single rule is why the map is MapLibre GL JS + PMTiles + Protomaps, not Google Maps. Google Maps is a great product and completely disqualified here: it needs a billed API key, it phones home, and its ToS won't survive an air-gapped deployment. MapLibre renders vector tiles locally; PMTiles is a single-file tile archive you can drop in the repo; Protomaps is an OSM-derived basemap you self-host. The whole map works on a laptop in a field with no signal.
Same rule killed CDN icon fonts. The asset markers (drone, tank, ship, submarine, satellite) are Tabler Icons (MIT) inlined as SVG — no font fetch, themeable via currentColor, attributed in NOTICE. Even the cockpit background — scanlines, glow — is pure CSS, no images.
Make the dangerous parts impossible by type
This is the one I care about most, and it's why the whole thing is TypeScript, strict, no any — plus noUncheckedIndexedAccess, exactOptionalPropertyTypes, verbatimModuleSyntax.
The point isn't "types are nice." The point is that in this domain there are operations you must not be able to do by accident — and the strongest place to stop them is the compiler, not a runtime check you hope runs. The engagement command can't be constructed without an authorization token: it's in the type. A ground vehicle can't show a "take off" button: controls render from declared capabilities, and a UGV doesn't declare flight. Add a new command and the exhaustive switch won't compile until every site handles it.
Screenshots and deeper technical detail
The live console screenshots, the rest of the stack, and the deferred-tools list are behind the wall. Same password as the other GCS posts — or request access below.
The takeaway, if there is one: don't ask "what's the best tool." Ask "what can't this system be allowed to do," write that down as a rule with teeth, and let the rules pick the tools. The stack falls out almost for free.
docs/architecture/tech-stack.md.