From ad7e0a5cbeed29f6c198a4e3c3d04c598308472e Mon Sep 17 00:00:00 2001 From: Andriy Oblivantsev Date: Wed, 8 Jul 2026 20:17:34 +0100 Subject: [PATCH] docs: WireGuard vs SSH, Flux GitOps, CONTRIBUTING with TDD-first Document why WireGuard and SSH are complementary layers, add Flux boundaries for the ground segment, shared var/ data paths in docs/06, and a contributor guide with the required test-first workflow. --- AGENTS.md | 11 +++-- CONTRIBUTING.md | 90 +++++++++++++++++++++++++++++++++++++ README.md | 25 +++++++++-- docs/05-network-security.md | 15 +++++++ docs/06-environments.md | 23 ++++++++++ docs/11-cicd-delivery.md | 33 ++++++++++++++ 6 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/AGENTS.md b/AGENTS.md index 70647c1..2f1035f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,11 +12,16 @@ Rules for anyone (human or tooling) contributing to this repository. ## Project layout ``` -docs/ Design documentation (Markdown + Mermaid) -simulator/ Virtual drone fleet — Python data generator (Parquet/DuckDB pipeline) -prototype/ 2D swarm visualization — React + TypeScript (Vite) +docs/ Design documentation (Markdown + Mermaid) +simulator/ Virtual drone fleet — Python data generator (Parquet/DuckDB pipeline) +prototype/ 2D swarm visualization — React + TypeScript (Vite) +infra/ Ansible host prep, Terraform workloads, Flux GitOps overlays +var/t1/ Shared T1 live lake (gitignored runtime data) +var/t3/ Shared T3 warehouse (gitignored runtime data) ``` +See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development workflow and TDD requirements. + ## Conventions - Markdown: ATX headings, fenced code blocks with language tags, pipe tables. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..be58aa2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing + +Thank you for helping improve Swarm House. This repository is a self-contained +design proposal for an on-prem data platform for autonomous drone swarms — all +contributions must follow the rules in [`AGENTS.md`](AGENTS.md). + +## TDD first (required) + +Every change to executable code ships with tests **before** or **alongside** the +implementation — never as an afterthought. + +1. **Red:** write or extend a failing test that captures the behaviour you need. +2. **Green:** implement the smallest change that makes it pass. +3. **Refactor:** clean up without weakening the test. + +The CI pipeline enforces this: + +| Gate | When it runs | +| --- | --- | +| `pytest tests/` | Every push/PR (`verify` job) | +| `RUN pytest tests/` in the simulator Docker build | Every `docker build` — a red test blocks the image | +| Smoke flight + DuckDB assertion | Every push/PR | +| Trivy scan | After verify passes | + +Test locations: + +- `simulator/tests/` — Python unit tests (SQL gate, broadcast codec, writer layout, flight kinematics) +- Prototype: `npm run build` / `tsc --noEmit` before UI changes + +## Local development + +### Fast inner loop (Compose) + +```bash +cd simulator +pip install -r requirements.txt +pytest tests/ -q # run tests first +DRONE_COUNT=5 docker compose up --build # writes to ../var/t1 +``` + +### Full ground miniature (k3d + Terraform + Flux) + +```bash +ansible-playbook infra/ansible/sim-cluster.yml +cd infra/terraform/sim-env && terraform init && terraform apply +cd ../ground && terraform init && terraform apply +cd ../../prototype && npm install && npm run dev # press "go live" +``` + +Shared data directories (gitignored, created automatically): + +| Path | Tier | Contents | +| --- | --- | --- | +| `var/t1/` | T1 live lake | In-flight Parquet from Compose or k3d fleet | +| `var/t3/` | T3 warehouse | Offloaded historical partitions | + +Override with `SWARM_T1_DIR` / `SWARM_WAREHOUSE_DATA` / `SWARM_SIM_DATA` env vars. + +### Prototype only + +```bash +cd prototype && npm install && npm run dev +``` + +## Commit messages + +Conventional Commits only: + +``` +feat: add warehouse offload cron schedule variable +fix: reject COPY statements in the SQL gate +docs: clarify WireGuard vs SSH layering +test: cover broadcast frame roundtrip +``` + +Never include AI tool references, `Co-authored-by` trailers for assistants, or +identifying names of people, companies, or locations. + +## Pull request checklist + +- [ ] Tests added or updated; `pytest tests/ -q` passes locally +- [ ] `docker build` in `simulator/` succeeds (test stage included) +- [ ] Documentation updated if behaviour or boundaries changed +- [ ] English only; no identifying references +- [ ] Conventional commit message + +## Questions + +Open design questions live in [`docs/09-open-questions.md`](docs/09-open-questions.md). +Architecture boundaries are documented in [`docs/06-environments.md`](docs/06-environments.md#iac-boundaries). diff --git a/README.md b/README.md index cdf51eb..b5fb250 100644 --- a/README.md +++ b/README.md @@ -39,18 +39,37 @@ This repository describes how to build, deliver, test, and operate that platform | Component | Purpose | Stack | | --- | --- | --- | | [`simulator/`](simulator/) | Virtual drone fleet: generates realistic telemetry through the same Parquet/DuckDB pipeline a real drone would use; N drones via Docker Compose | Python, pyarrow, DuckDB | -| [`prototype/`](prototype/) | 2D top-down swarm visualization: drones, obstacles, inter-drone links with live transfer metrics | React, TypeScript, Vite, SVG | +| [`prototype/`](prototype/) | 2D top-down swarm visualization: drones, obstacles, inter-drone links with live transfer metrics; **live mode** renders real positions from the k3d fleet | React, TypeScript, Vite, SVG | +| [`infra/ansible/`](infra/ansible/) | Host preparation: drone bench provisioning (keys, forced commands, WireGuard, Compose bundle) and local k3d simulation cluster | Ansible | +| [`infra/terraform/`](infra/terraform/) | Declared workloads on the sim cluster: virtual fleet + observability ([`sim-env`](infra/terraform/sim-env/)), warehouse + T1→T3 offload ([`ground`](infra/terraform/ground/)) | Terraform, kubernetes provider | +| [`infra/gitops/`](infra/gitops/) | Flux overlays for the ground segment — policy labels, dashboard bundles; drones stay on the fleet manifest | Flux, Kustomize | + +Shared runtime data (gitignored): `var/t1/` (live lake), `var/t3/` (warehouse). See [`CONTRIBUTING.md`](CONTRIBUTING.md). ## Quick start ```bash -# Spin up a virtual swarm of 10 drones +# Spin up a virtual swarm of 10 drones (Compose, no cluster needed) cd simulator && DRONE_COUNT=10 docker compose up # Run the visualization cd prototype && npm install && npm run dev ``` +Or run the full miniature of the ground segment — k3d cluster, fleet as +StatefulSet, Grafana, explorer, warehouse with post-flight offload: + +```bash +ansible-playbook infra/ansible/sim-cluster.yml +cd infra/terraform/sim-env && terraform init && terraform apply +cd ../ground && terraform init && terraform apply +# then press "go live" in the prototype header +``` + +See [06 — Environments → IaC boundaries](docs/06-environments.md#iac-boundaries) for why Ansible owns hosts, Terraform owns the ground segment, and neither flies on the drone. + +Contributing (TDD-first workflow): [`CONTRIBUTING.md`](CONTRIBUTING.md). + ## CI & releases -Every push runs the pipeline in [`.github/workflows/release.yaml`](.github/workflows/release.yaml): a compile check, a 10-second virtual smoke flight verified with DuckDB, unit tests for the read-only SQL gate, and a Trivy vulnerability/misconfiguration scan. Pushes to `main` then cut a semantic version from the conventional-commit type (`feat:` → minor, `!`/`BREAKING CHANGE` → major, else patch), tag the commit, and publish a release with a **fleet release manifest** (versioned artifact list with checksums — the delivery unit described in [11 — CI/CD & delivery](docs/11-cicd-delivery.md)) plus a source bundle. +Every push runs the pipeline in [`.github/workflows/release.yaml`](.github/workflows/release.yaml): **pytest unit tests**, a compile check, a 10-second virtual smoke flight verified with DuckDB, and a Trivy vulnerability/misconfiguration scan. The simulator Docker build also runs `pytest` — a failing test blocks the image. Pushes to `main` then cut a semantic version from the conventional-commit type (`feat:` → minor, `!`/`BREAKING CHANGE` → major, else patch), tag the commit, and publish a release with a **fleet release manifest** (versioned artifact list with checksums — the delivery unit described in [11 — CI/CD & delivery](docs/11-cicd-delivery.md)) plus a source bundle. diff --git a/docs/05-network-security.md b/docs/05-network-security.md index 87a7e9c..6272a57 100644 --- a/docs/05-network-security.md +++ b/docs/05-network-security.md @@ -36,6 +36,21 @@ graph TB - **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 | diff --git a/docs/06-environments.md b/docs/06-environments.md index 8cd75e9..3b78df4 100644 --- a/docs/06-environments.md +++ b/docs/06-environments.md @@ -61,6 +61,29 @@ The environment engineers live in daily — and deliberately the first thing to - **T4 data slices**: a thin tool pulls partition subsets from T3 (`flight=X, drone=Y, hour=Z`) for local work — layout-identical, so every query and pipeline runs unmodified. - The same simulator images scale out on the ground k3s farm for CI regression runs: every merge request replays canonical scenarios and asserts on the resulting Parquet output (row counts, coverage, staleness budgets). +## IaC boundaries + +Infrastructure as code follows the same discipline as the data plane: each tool owns the layer it is actually good at, and nothing owns a layer it can't reach. + +| Layer | Tool | Why | +| --- | --- | --- | +| Host preparation (drone bench provisioning, sim cluster creation) | **Ansible** ([`infra/ansible/`](../infra/ansible/)) | Idempotent, agentless, works over the same SSH channel the platform already trusts. Keys, forced commands, WireGuard, Compose bundles — all host-level state. | +| Declared workloads (simulated fleet, ground warehouse) | **Terraform** ([`infra/terraform/`](../infra/terraform/)) | The ground segment is stationary, long-lived, and cluster-shaped — classic Terraform territory. Fleet size, offload schedule, and explorer endpoints are variables, not YAML edits. | +| Ongoing ground configuration | **Flux** ([`infra/gitops/`](../infra/gitops/)) | Policy labels, dashboard bundles, offload knobs — reconciled from git without reprovisioning PVCs. Drones stay on the fleet manifest, not GitOps. | +| On-board runtime | **Compose bundle from the fleet release manifest** | Terraform is deliberately *not* on the drone: there is no API server to reconcile against mid-flight, and the release manifest already gives atomic, versioned, rollback-able delivery ([11](11-cicd-delivery.md)). | + +Shared local data paths (same layout everywhere): **`var/t1/`** (live lake, T1) and **`var/t3/`** (warehouse, T3). Compose, k3d, and DuckDB on the host all read the same Parquet tree. + +Two Terraform modules, two states, one deliberate split: [`sim-env`](../infra/terraform/sim-env/) (the disposable virtual fleet) and [`ground`](../infra/terraform/ground/) (the warehouse that must survive every `destroy` of the fleet). They never share a lifecycle. + +### k3d: the ground segment as an executable miniature + +`ansible-playbook infra/ansible/sim-cluster.yml` creates a local k3d cluster (k3s in Docker — the same distribution as the real ground segment) and `terraform apply` populates it: virtual drones as StatefulSet replicas over a shared Parquet lake, the exporter/Prometheus/Grafana stack, the data-plane explorer, MinIO, and the T1→T3 offload CronJob. The whole diagram at the top of this page runs on one workstation, and the prototype's **live mode** renders real drone positions straight from the explorer's read-only SQL API. + +### Next fidelity step: microVMs + +The k3d fleet shares one kernel and one network namespace tree — good enough to exercise the data plane, not good enough to demo the *peer* plane (real SSH sessions between isolated machines, WireGuard handshakes, packet loss injection). The next step, when that fidelity is needed, is one **Firecracker microVM per drone**: a real kernel, a real network interface, and the actual `authorized_keys` forced-command path between virtual drones — on any KVM-capable workstation, still with no cloud dependency. The process-level simulator stays as the fast inner loop; microVMs become the pre-flight integration rig. + ## Why identical layouts matter (the payoff) | Operation | What it costs with one layout everywhere | diff --git a/docs/11-cicd-delivery.md b/docs/11-cicd-delivery.md index 51de789..0031a29 100644 --- a/docs/11-cicd-delivery.md +++ b/docs/11-cicd-delivery.md @@ -97,6 +97,39 @@ config: 2. Docked drones fetch the manifest, verify digests/signatures ([05](05-network-security.md)), stage the new Compose bundle, and switch on the next boot cycle. 3. **Never mid-flight.** Updates are a dock-only operation by construction — the update endpoint does not exist in the flight radio profile. +## GitOps on the ground segment + +The fleet release manifest handles **drones** (atomic, digest-pinned, dock-only). The **ground k3s segment** uses a different tool: [Flux](https://fluxcd.io/) reconciles long-lived configuration from git — dashboard bundles, Prometheus rules, offload schedules, policy labels — without reprovisioning PVCs or re-running full Terraform applies for every label change. + +```mermaid +graph LR + subgraph git [Git, on-prem] + REPO["service repos"] + GITOPS["infra/gitops/
ground overlay"] + end + subgraph ground_k3s [Ground k3s] + FLUX["Flux controllers"] + WH["warehouse workloads
(Terraform-provisioned)"] + CFG["policy ConfigMaps
dashboard bundles"] + end + subgraph drones [Fleet] + MAN["fleet release manifest"] + DOCK["dock: verify + apply"] + end + REPO --> CI + GITOPS --> FLUX + FLUX --> CFG + MAN --> DOCK +``` + +| Segment | Delivery mechanism | Reconciler in production? | +| --- | --- | --- | +| Drones | Fleet release manifest via registry mirror | **No** — no API server mid-flight; dock applies once | +| Ground k3s (warehouse, dashboards, CI runners) | Terraform for shape + **Flux** for ongoing config | **Yes** — stationary cluster, wired network, git is the source of truth | +| Dev simulation (k3d) | Ansible creates cluster; Terraform applies workloads; Flux CRs installed from [`infra/gitops/`](../infra/gitops/) | Yes (miniature of ground) | + +Boundary: **Terraform provisions** (namespaces, PVCs, Deployments, CronJobs); **Flux reconciles** policy and observability overlays on top. Neither replaces the fleet manifest on the drone. + ## Developer experience - **Dev Containers** define the full toolchain (Python data tooling, DuckDB, compose, linters) — identical on any machine, onboarding in minutes.