# Containerisation **Status**: forward-looking design (Rust). Final shape will surface during build-system bring-up; treat the choices below as the current intent, not commitments. ## 1. Deployment shape `autopilot` is a single Rust binary. Two delivery options are considered: | Option | Form | Pros | Cons | |---|---|---|---| | **A — native systemd unit** | bare binary deployed to `/usr/local/bin/autopilot` + a `.service` unit | minimum overhead on Jetson; closest to airframe constraints; trivial flight-gate integration | per-host installation discipline; less reproducible across nodes | | **B — single container image** | `azaion/autopilot:-arm64` | consistent across environments; matches the suite's existing OTA model (Watchtower) | container runtime adds startup latency and one more failure surface on the airframe | The decision is **Option A** for the on-airframe deployment (lowest overhead, closest to the autopilot's real-time constraints), and **Option B** for development / CI / emulated-hardware testing (reproducibility wins). The same Rust binary is built once and packaged into both. ## 2. Target hardware | Item | Value | |---|---| | Edge device | NVIDIA Jetson Orin Nano Super 8 GB | | Architecture | aarch64 | | OS | Ubuntu 22.04 (JetPack-bundled) — locked JetPack version + power mode | | Camera | ViewPro A40 (RTSP + UDP control) | | Autopilot | ArduPilot or PX4 over MAVLink v2 (UDP or serial) | ## 3. Native deployment (Option A — production) **Layout:** ```text /usr/local/bin/autopilot Rust binary /etc/azaion/autopilot/config.toml runtime config /etc/systemd/system/autopilot.service systemd unit /var/lib/autopilot/ persistent state (mapobjects_store) /run/azaion/in-flight flight-gate marker (per ../_docs/00_top_level_architecture.md) ``` **systemd unit highlights:** - `Type=notify` — autopilot signals readiness once Tier 1, gimbal, and MAVLink links are healthy. - `Restart=on-failure`, `RestartSec=2s`, `StartLimitBurst=5` — bounded restart (so a hard-broken binary doesn't loop forever). - `MemoryMax=` — enforces the on-airframe memory budget (~6 GB; Tier-1 YOLO container holds ~2 GB). - `LimitNOFILE`, `LimitNPROC` set explicitly. - `ExecStartPre=/bin/sh -c 'mkdir -p /run/azaion && touch /run/azaion/in-flight'` — asserts the suite-wide flight-gate so `model-sync.service` does not pull a new model mid-flight. - `ExecStopPost=/bin/rm -f /run/azaion/in-flight` — clears the flight-gate on shutdown. **Runtime config** (`/etc/azaion/autopilot/config.toml`) is the single source for non-secret configuration: RTSP URL, gimbal endpoint, MAVLink connection URI, missions API endpoint, Ground Station endpoint, VLM IPC socket path, `vlm_enabled` flag, log level. Secrets (if any — TBD per `../_docs/02_missions.md` auth model) come from the systemd `EnvironmentFile=` pointing at a permission-restricted file. ## 4. Container image (Option B — dev / CI / emulation) **Base image:** `nvcr.io/nvidia/l4t-base:` for production-equivalent NVDEC + TensorRT plumbing; `ubuntu:22.04` for emulation (no GPU acceleration). **Image layout:** ```text /usr/local/bin/autopilot Rust binary (built outside the image) /etc/azaion/autopilot/config.toml runtime config (mounted at runtime) /var/lib/autopilot/ persistent state (volume-mounted) ``` **Image is non-root.** Default `USER` is `autopilot:autopilot`; `/var/lib/autopilot/` is owned by that user. **Compose example** (development): ```yaml services: autopilot: image: azaion/autopilot:dev-arm64 restart: unless-stopped environment: AUTOPILOT_CONFIG: /etc/azaion/autopilot/config.toml volumes: - ./config/autopilot.toml:/etc/azaion/autopilot/config.toml:ro - autopilot-state:/var/lib/autopilot - /run/azaion:/run/azaion devices: - /dev/ttyUSB0:/dev/ttyUSB0 # MAVLink serial (if used) network_mode: host # RTSP / UDP gimbal / Ground Station modem all on host volumes: autopilot-state: {} ``` `network_mode: host` is intentional on Jetson: RTSP, gimbal UDP, MAVLink UDP, and the modem-link to the Ground Station all share the airframe's network namespace. ## 5. External dependencies on the airframe `autopilot` itself is the only autopilot-owned process. The on-airframe tier also runs (separately): - **`../detections`** — Tier 1 YOLO service. Container delivered from its own pipeline. Bi-directional gRPC endpoint consumed by `detection_client`. - **NanoLLM / VILA1.5-3B** (optional) — local IPC peer of `vlm_client`. Separate container or process; not embedded in the autopilot binary. Surfaces a Unix-domain socket; peer-credential check is mandatory when supported. - **GPS-Denied service** — separate edge service, owned by `gps-denied-onboard`; consumed indirectly through the shared edge data path (per `../_docs/11_gps_denied.md`). - **`model-sync.service`** — suite-wide rclone-driven model puller. Reads `/run/azaion/in-flight` to defer model swaps during flight (per `../_docs/00_top_level_architecture.md`). ## 6. Configuration surface All configuration is declarative (`config.toml`); there is no compile-time configuration of endpoints, addresses, or feature switches **except** the `vlm_client` build-time feature flag (see `architecture.md §7.6 Local VLM confirmation > Optionality model`). | Concern | Mechanism | |---|---| | RTSP / gimbal / MAVLink endpoints | `config.toml` | | `missions` API endpoint + auth | `config.toml` (auth pulled from `EnvironmentFile=`) | | Ground Station endpoint | `config.toml` | | VLM IPC socket path | `config.toml` | | `vlm_enabled` runtime flag | `config.toml` | | `vlm_client` build-time feature | `cargo --features vlm` at build | | Log level + format | `RUST_LOG` env (`tracing-subscriber` honours it) | | Mission ID for the current flight | CLI arg (per-flight, not per-host) | ## 7. Health endpoint `autopilot` exposes a single HTTP health endpoint (port and bind address from `config.toml`; default `127.0.0.1:8080`). It aggregates per-component readiness: ```json { "status": "green | yellow | red", "components": { "frame_ingest": "green", "detection_client": "green", "movement_detector": "green", "semantic_analyzer": "green", "vlm_client": "disabled", "scan_controller": "green", "mapobjects_store": "green", "gimbal_controller": "green", "operator_bridge": "yellow", "mission_executor": "green", "mavlink_layer": "green", "mission_client": "green", "telemetry_stream": "green" }, "last_state_change": "2026-05-17T12:00:00Z" } ``` `yellow` is degraded-but-running; `red` is unrecoverable for at least one essential component. The aggregator surfaces details on each transition through `tracing` (see `observability.md`). ## 8. Out of scope here - Provisioning the Jetson host itself (Ansible / Kickstart / disk imaging) — owned by airframe ops. - Build pipeline (cross-compile, signing, registry push) — see `ci_cd_pipeline.md`. - Observability stack (tracing exporter, log shipper, metrics scraper) — see `observability.md`. - Mission delivery to the airframe — owned by `missions` API.