# 05 — Network & security
Zero trust, fully air-gapped, everything provisioned before wheels-up.
> **Decision:** [ADR-0005 — WireGuard beneath SSH](adr/ADR-0005-wireguard-beneath-ssh.md).
## Trust model
- **Nothing joins the swarm at runtime.** Every device receives its identity (key pair + certificate) during ground provisioning, signed by the fleet's offline CA. There is no trust-on-first-use, no runtime enrollment endpoint, no exception path.
- **Network position grants nothing.** Being inside the mesh does not authorize anything; every connection authenticates mutually.
- **Two layers of cryptography, by design:**
1. **WireGuard mesh overlay** — every drone holds the public keys of every fleet member (distributed at provisioning). All swarm traffic runs inside these tunnels; a device without a provisioned key cannot even complete a handshake.
2. **Authenticated channels inside the overlay** — the bulk-sync and peer-query channel is SSH with **ed25519** keys and pinned `known_hosts` (no first-use prompts, no runtime key acceptance); every permitted operation is a separate key pair bound to a **forced command**, so a stolen key authorizes exactly one narrow action. Services that speak TLS (MinIO, ground APIs) present per-service certificates from the fleet PKI (mTLS). Compromising the network layer alone still yields nothing readable — and compromising one credential yields one operation, not a shell.
```mermaid
graph TB
subgraph ground [Ground provisioning, before deployment]
CA["offline fleet CA"]
PROV["provisioning station"]
CA -->|"sign device + service certs"| PROV
end
subgraph mesh [In-flight ad-hoc mesh]
A["drone A
wg key + certs"]
B["drone B
wg key + certs"]
C["drone C
wg key + certs"]
A <-->|"WireGuard + mTLS"| B
B <-->|"WireGuard + mTLS"| C
A <-->|"WireGuard + mTLS"| C
end
PROV -->|"per-device identity, peer list"| A
PROV --> B
PROV --> C
```
## Radio reality
- Transport is assumed to be ad-hoc Wi-Fi class (2.4/5 GHz), with channel hopping expected. The platform treats the link as **lossy, variable, and adversarial** — all sync mechanisms already tolerate partitions ([04](04-swarm-sync.md)).
- **RSSI is data.** Per-peer signal strength is captured into `telemetry` like any other sensor — it feeds relative-positioning estimates and post-flight link-quality analysis, and it is free.
- Electromagnetic environment sensing (whatever the radio can observe) is likewise recorded — cheap in flight, valuable in replay.
## Why not SSH alone?
SSH and WireGuard solve different problems. SSH is the **authorization layer** for narrow, point-to-point operations (bulk sync, read-only SQL). WireGuard is the **membership and transport encryption layer** for everything on the mesh, including traffic SSH cannot carry.
| Concern | SSH alone | WireGuard + SSH (this design) |
| --- | --- | --- |
| Pose broadcast (UDP, ~5 Hz, compact frame) | Cannot carry subnet broadcast; would need a separate app protocol | Encrypted inside the mesh overlay; any peer on the tunnel can receive |
| Broadcast confidentiality without WG | Payloads traverse the radio **in cleartext** — any receiver in range reads swarm positions | Only provisioned fleet members complete a handshake; outsiders see noise |
| Lossy ad-hoc links | TCP head-of-line blocking; sessions stall and reconnect on packet loss | UDP transport survives loss and roaming; SSH sessions **over** WG are more stable than SSH directly on Wi-Fi |
| Network membership | Any host that reaches the port gets an SSH banner — a probe surface | Devices without a provisioned key get **no handshake response**; the port is effectively silent |
**Rejected alternative:** encrypt each broadcast frame in application code with a fleet-wide AEAD key. That reinvents a crypto layer the OS already provides, still leaves TCP head-of-line blocking for peer queries, and does not answer the membership question (who is allowed on the mesh at all).
Keep both layers: WireGuard for *who is on the network and whether the wire is readable*; SSH forced commands for *what a peer is allowed to do once connected* ([04](04-swarm-sync.md)).
## Key lifecycle
| Phase | Action |
| --- | --- |
| Fleet build | Offline CA generates device identities; keys injected at provisioning, never transmitted over any network |
| Mission prep | Peer lists and certificates refreshed as part of the fleet release bundle ([11](11-cicd-delivery.md)) |
| Rotation | Standard cadence between missions; emergency rotation = new fleet release |
| Loss of a drone | Its keys are revoked in the next release; mesh peers drop the revoked identity at the next mission load. Data-at-rest exposure is bounded by NVMe encryption (below) |
## Data at rest
- On-board NVMe is encrypted (LUKS) with keys held in the device's secure element / TPM where the hardware provides one; the disk alone is unreadable.
- The ground warehouse applies standard at-rest encryption plus role-based access; it is the single most valuable asset in the system (every flight ever flown).
## Attack surface, deliberately shortened
- The dev/debug plane **does not exist** in production builds ([02](02-architecture.md)) — not firewalled, absent.
- No inbound listeners except the authenticated mesh services; no management SSH in production radio profiles (bench access is wired, at the dock).
- Images are scanned (Trivy) and signed in CI; drones verify signatures against the pinned digests in the fleet release manifest before running anything ([11](11-cicd-delivery.md)).