· Yogesh Khedar· Engineering· 4 min read

The AutrikOS Tech Stack — Few Languages, Chosen Deliberately

Flutter, Go, C++, Kubernetes and Docker Compose — the decisions behind AutrikOS, including why we moved away from MAVSDK and did not adopt BlueOS.

Flutter, Go, C++, Kubernetes and Docker Compose — the decisions behind AutrikOS, including why we moved away from MAVSDK and did not adopt BlueOS.

Every technology choice here was made by a small team that has to maintain all of it. That constraint produces different answers than a large organisation would reach, and it is worth being explicit about them.

Right tool per layer

One team, few languages, and a reason for each.

  • Flutter, for the console. One codebase that runs in the browser, on a Mac or Windows desktop and on an Android tablet in the field. For a product that has to be a web page in the control room and an app on a device with no browser, that is the whole argument. The desktop builds are the same code as the web build, with a gamepad and a few file dialogs added.
  • Go, for the backend and for the aircraft. The cloud services and Skyclient, the onboard client, share a language and a library of protocol code. Static binaries, straightforward concurrency, and cross-compilation to whatever companion computer the aircraft happens to carry: a Raspberry Pi, a Jetson, an industrial x86 box.
  • C++, where the vendors are. Camera and sensor SDKs and the DJI payload SDK live in C++, so that is where we meet them: a small bridge process per SDK, talking to the Go client over a local socket, rather than wrapping every SDK in a foreign language.
  • Protobuf over MQTT, and REST. The contract between console, cloud and aircraft is one set of protobuf messages over MQTT for everything live (telemetry, commands, events) and a REST API for everything stored. One schema repository generates the Go and Dart code, so the three sides cannot drift.

The onboard client as the device page shows it: system metrics, what is on disk, what was synced

The onboard client as the device page shows it: system metrics, what is on disk, what was synced.

Kubernetes in the cloud, Compose at the edge

Cloud and on-premise deployments have different operational needs. Using the right orchestration for each beats forcing one everywhere.

  • Kubernetes where it earns it. In the cloud, where rollout, scaling and self-healing justify the weight it brings. Releases go out as a train every two weeks, staged on a dev stack first and gated by an automated test suite against the real services.
  • Docker Compose at the edge. On-premise and field deployments where a cluster would be more infrastructure than the workload. The offline build of the console runs against a local broker and a local platform on one laptop at the site, with no internet.
  • Identical containers. The same images run in both, so on-premise is not a second-class variant that lags the cloud.

Why we moved away from MAVSDK

MAVSDK is a reasonable library and we used it. The difficulty was language fit: the practical bindings are Python and C++, while our onboard and backend work is Go, and bridging that boundary cost more than the library saved. We ended up with a Go MAVLink implementation of our own, and the same code now powers mavgo-cli, the command-line tool for probing, proxying and logging any MAVLink vehicle.

Rust is the candidate we watch for a future port of Skyclient: static binaries, strong guarantees at the boundaries where we currently rely on discipline. That is a direction, not a shipped decision.

Why not BlueOS

BlueOS is flexible, open source and good. Adopting it would still have cost a great deal of time, and the flexibility that makes it attractive to builders is exactly what we did not want to hand to operators.

We want an opinionated client: one that has already made the decisions, discovers what is on the aircraft, budgets the bandwidth between the streams and ships one timestamped log of everything, so an operator gets a system that works rather than a platform to configure. That is a product position more than a technical one, and it is the honest reason.

What the choices cost us

Three languages still means three toolchains, and a small team feels every one. Flutter’s web build is heavier than a hand-written page would be. Go’s lack of generics until recently made the protocol code more repetitive than we liked. And an opinionated client means saying no to integrations that a plugin system would have allowed. We would make the same choices again, and we say so with the trade-offs on the table.

Curious about a decision here? We are happy to go deeper on any of these, including the ones we would make differently now.