Skip to main content

Contributing Guide

ADOS Mission Control and ADOS Drone Agent are open-source under GPLv3. Contributions are welcome. This guide covers the dev setup for both repos, the patterns for adding common feature types, and the pull request process.

Dev environment setup

Mission Control (GCS)

1

Clone the repo

2

Install dependencies

Requires Node.js 20+ and npm 10+.
3

Start the dev server

Opens at http://localhost:4000. Turbopack provides fast hot module replacement.
4

Launch demo mode

Open the app in your browser. The welcome modal offers a “Try Demo” button that starts 7 simulated drones. No hardware needed.

SITL testing

To test with a real ArduPilot simulator:
1

Build ArduPilot from source

Follow the ArduPilot build docs. The SITL tool expects the ArduPilot repo at ~/.ardupilot.
2

Launch SITL

This starts ArduPilot SITL with full physics simulation and a TCP-to-WebSocket bridge. Mission Control connects to ws://localhost:5760.
3

Connect from Mission Control

In Mission Control, click Connect > WebSocket and enter ws://localhost:5760. You now have a real autopilot with simulated GPS, IMU, and battery.
The agent is a Rust and Python hybrid. The long-running services are Rust binaries in crates/; Python carries AI and vision, the plugin runtime, setup, HAL detection, and the residual web API. A full dev setup needs both a Rust toolchain and Python.
1

Clone the repo

2

Create a Python virtual environment

Requires Python 3.11+. (uv works too if you prefer it.)
3

Build the Rust services

Requires a recent stable Rust toolchain. The Cargo workspace lives in crates/.
4

Run the CLI

5

Run the terminal status page

The read-only terminal page shows setup URLs and agent status over SSH.

Adding a configure panel (Mission Control)

Configure panels are the most common contribution. Each panel lets the user adjust a group of flight controller parameters.
1

Create the component

Add a new file in src/components/configure/:
2

Register the panel

Add the panel to the navigation in src/components/configure/DroneConfigureTab.tsx:
3

Test with SITL

Launch SITL, connect, and verify the panel loads, reads parameters, and writes them back.
The usePanelParams hook handles all protocol details. It works with MAVLink native parameters (ArduPilot, PX4) and MSP virtual parameters (Betaflight) without any changes to your panel code. When you need to handle a new MAVLink message type:
1

Add constants

In src/lib/protocol/mavlink-crc-extra.ts, add the message ID, CRC_EXTRA, and payload length:
2

Add the decoder

In src/lib/protocol/mavlink-adapter.ts, add a case to handleMessage():
3

Add the callback to DroneProtocol

In src/lib/protocol/drone-protocol.ts:
4

Subscribe in DroneManager

In src/stores/drone-manager.ts inside bridgeTelemetry():

Adding a Zustand store (Mission Control)

1

Create the store

2

Use selectors in components

Always select specific fields. Never destructure the entire store.
If the store needs to persist across page reloads, use the persist middleware with a version number:

Adding a board profile (Drone Agent)

1

Create the YAML profile

model_patterns are matched against /proc/device-tree/model and /proc/cpuinfo for auto-detection.
2

Test detection

On the target board, run:
This prints setup, service, board, and runtime status for inspection.

Adding an agent service (Drone Agent)

New long-running or safety-critical services are written as Rust crates under crates/ and run a binary from /opt/ados/bin/. An ancillary Python-backed service is still fine for ecosystem-bound work (AI, drivers, setup glue); the steps below show the Python case. Either way, the unit is registered in the supervisor catalog.
1

Create the service module

Add a new file in src/ados/services/my_service/:
2

Create the systemd unit

Add data/systemd/ados-my-service.service:
3

Register in the supervisor

Add the unit to the SERVICE_REGISTRY in crates/ados-supervisor/src/registry.rs with its category (core, hardware, on-demand), its profile gate, and any ground-station role gate. The supervisor drives the unit through systemctl; it does not spawn the process itself.

Pull request guidelines

Before submitting

  • Run tsc --noEmit for Mission Control (zero errors required)
  • Run python -m py_compile on any modified Python files
  • Run cargo fmt --check, cargo clippy, and cargo test for any Rust crate changes
  • Check for em dashes in any user-facing strings (there should be none)
  • Test with demo mode or SITL for Mission Control changes
  • Bump the version in src/ados/__init__.py for agent changes

PR format

Branch naming

  • feature/short-description for new features
  • fix/short-description for bug fixes
  • docs/short-description for documentation

Review process

  1. Open a PR against main
  2. Automated checks run (TypeScript build, linting)
  3. A maintainer reviews the code
  4. Once approved, the maintainer merges
For large features, open a draft PR early with a description of what you plan to build. This helps avoid wasted effort if the design needs changes.

Code style

Mission Control: Follow the existing patterns. Zustand for state, usePanelParams for configure panels, selectors for subscriptions. Tailwind for styling. No CSS modules. Drone Agent (Python): Use structlog for logging, asyncio for async code, Pydantic for config models. Type hints on all function signatures. black for formatting. Drone Agent (Rust): Format with cargo fmt, lint with cargo clippy, and keep the IPC wire contracts defined in the ados-protocol crate as the single source of truth.

Getting help

  • Discord for questions, architecture discussion, and PR review
  • GitHub Issues for bug reports and feature requests with a clear use case