From 4ede1f3fd2475b4a6afaafd0445c6af53e103de2 Mon Sep 17 00:00:00 2001 From: RayRose Date: Sun, 16 Aug 2026 21:07:58 -0500 Subject: [PATCH] Split the operator TUI out of the detector's supply chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `flyingsquirrel-watch` shares no code with the detector — it reads the JSON event log with its own permissive structs, deliberately, so a schema change can't break it. But as a `[[bin]]` in the root package its dependency tree was welded to the safety-critical one, and that had real costs: * `ratatui -> lru 0.12` put two unsoundness advisories (RUSTSEC-2026-0002, -0253) into the detector's `cargo audit`. Both were suppressed with the rationale "ratatui's widget cache, NOT the detector." True, and unenforced — nothing in the build made it so. * `ratatui -> instability -> darling` was the SOLE reason the declared MSRV was 1.88 rather than 1.85. * A terminal UI rode along in every ARM cross-build, the Docker image, and `fuzz/Cargo.lock` — the NMEA/MAVLink fuzz harness was receiving Dependabot PRs for a TUI library, which is how the coupling surfaced. Move it to `watch/` as a detached workspace with its own lockfile, the same pattern (and for the same reason) as `fuzz/`. A workspace MEMBER would not do: members share one lockfile, so `lru` would stay in the detector's audit scope and the exercise would be pointless. Result: 34 crates leave the detector (288 -> 254). `lru`, `ratatui`, `crossterm`, `darling`, `instability` are gone from both the detector's and the fuzz harness's lockfiles, so the two `lru` ignores are DELETED from ci.yml rather than argued. They now live in a `watch` job scoped to the crate that actually pulls them — the boundary is structural. MSRV stays 1.88. I re-derived it instead of assuming it dropped: with ratatui gone the binding constraint moved to `nmea -> serde_with -> time 0.3.47`, verified by a reachability walk over `cargo metadata --filter-platform` excluding dev-deps. That is a core functional dependency rather than a dashboard, which is the defensible place for it. Also in this pass: * Dependency bumps that were open as separate PRs, applied together so the tree resolves once: toml 0.8 -> 1.1 and thiserror 1.0 -> 2.0 (both major), tokio-stream, async-trait. Full suite green on both. * deploy/Dockerfile: the builder image had been bumped 1.88 -> 1.97 while still carrying an "MSRV = 1.88, keep in sync" comment. The image tracks a current toolchain on purpose; proving the MSRV is the CI `msrv` job's duty. Split the two so a base-image bump can't silently invalidate the claim again. * Delete docs/sitl_driver.py — a deprecated prototype, superseded by deploy/sitl/sitl_harness.py, guarded to refuse to run, referenced by nothing. Git history keeps it. * README: document the dashboard (a shipped binary that had no docs at all) and correct the now-stale MSRV rationale. Verified: detector fmt + clippy(-D warnings, --all-targets --all-features) + 182 lib + 35 integration across all 14 suites + 10 proptests; watch crate fmt + clippy + 4 tests. Co-Authored-By: Claude Opus 5 --- .github/dependabot.yml | 10 + .github/workflows/ci.yml | 68 +- .github/workflows/release.yml | 13 +- .gitignore | 5 + Cargo.lock | 405 +------- Cargo.toml | 41 +- README.md | 26 +- deploy/Dockerfile | 14 +- docs/sitl_driver.py | 181 ---- fuzz/Cargo.lock | 410 +------- watch/Cargo.lock | 918 ++++++++++++++++++ watch/Cargo.toml | 61 ++ .../src/main.rs | 5 +- 13 files changed, 1175 insertions(+), 982 deletions(-) delete mode 100644 docs/sitl_driver.py create mode 100644 watch/Cargo.lock create mode 100644 watch/Cargo.toml rename src/bin/flyingsquirrel-watch.rs => watch/src/main.rs (99%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 46b2c68..cdc8612 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -47,6 +47,16 @@ updates: interval: weekly open-pull-requests-limit: 3 + # The operator dashboard, also a detached workspace (see watch/Cargo.toml). + # It carries the TUI stack — ratatui and its `lru`/`paste` advisories — which + # is exactly why it is not in the detector's tree. Keeping it updated here is + # how the eventual ratatui release that drops those advisories reaches us. + - package-ecosystem: cargo + directory: "/watch" + schedule: + interval: weekly + open-pull-requests-limit: 3 + # Docker base images in the shipped detector image (deploy/Dockerfile). - package-ecosystem: docker directory: "/deploy" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58bd362..5ee9c9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,24 +153,13 @@ jobs: run: cargo install cargo-audit --locked - name: Scan dependency tree for RUSTSEC advisories # `--deny warnings` fails on any advisory, including informational - # "unmaintained"/"unsound" notices. Five TRANSITIVE advisories are + # "unmaintained"/"unsound" notices. THREE transitive advisories are # explicitly accepted — none is reachable in the runtime flight path, # and none has a semver-compatible patched version available without a # major dependency migration: # * RUSTSEC-2024-0436 paste 1.0.15 — UNMAINTAINED. A build-time - # proc-macro pulled by BOTH ratatui and nalgebra/simba; abandoned - # upstream with no replacement. No runtime code, no vulnerability. - # * RUSTSEC-2026-0002 lru 0.12.5 — unsound `IterMut`. Pulled ONLY by - # ratatui's TUI widget cache (the flyingsquirrel-watch monitor), NOT - # the safety-critical detector. ratatui 0.29 pins `lru = 0.12`, so no - # semver-compatible patched version is available. - # * RUSTSEC-2026-0253 lru 0.12.5 — unsound `LruCache::pop()` (potential - # use-after-free ONLY if a caller-supplied Drop/Hash panics mid-pop; - # not triggerable by wire input). Same crate, same sole path as - # RUSTSEC-2026-0002 above: ratatui's internal widget cache in the - # flyingsquirrel-watch monitor TUI, NOT the detector. Patched only in - # lru >=0.18.2; ratatui 0.29 pins `lru = 0.12`, so no semver-compatible - # patched version exists. Revisit when ratatui is next upgraded. + # proc-macro pulled by nalgebra/simba; abandoned upstream with no + # replacement. No runtime code, no vulnerability. # * RUSTSEC-2026-0194 + RUSTSEC-2026-0195 quick-xml 0.36.2 — two DoS # advisories (quadratic duplicate-attribute scan; unbounded # namespace-declaration allocation). quick-xml is a BUILD-dependency @@ -182,8 +171,57 @@ jobs: # migration deferred to its own change. # NB: RUSTSEC-2026-0190 (anyhow unsound downcast_mut) is deliberately # NOT ignored — it was fixed by upgrading anyhow to 1.0.103 in Cargo.lock. + # + # REMOVED, not suppressed: RUSTSEC-2026-0002 and RUSTSEC-2026-0253 (both + # `lru 0.12.5`, unsound) used to be ignored here on the grounds that lru + # reached us only through ratatui's widget cache in the operator TUI and + # never through the detector. That was true but unenforced — the TUI was + # a bin target in this same package, so `lru` sat in the detector's + # lockfile, its ARM cross-builds, its Docker image, and even + # `fuzz/Cargo.lock`. Moving the dashboard to the detached `watch/` crate + # made the claim structural: `lru` is no longer in this tree at all, so + # the advisories are gone rather than argued. `watch/` runs its own audit + # (see the `watch` job) where those two are ignored with that same + # rationale — but now it is the honest scope. # Real vulnerabilities and any NEW advisory still fail this gate. - run: cargo audit --deny warnings --ignore RUSTSEC-2024-0436 --ignore RUSTSEC-2026-0002 --ignore RUSTSEC-2026-0194 --ignore RUSTSEC-2026-0195 --ignore RUSTSEC-2026-0253 + run: cargo audit --deny warnings --ignore RUSTSEC-2024-0436 --ignore RUSTSEC-2026-0194 --ignore RUSTSEC-2026-0195 + + # ---- Operator dashboard: detached crate, so it needs its own gate ---- + watch: + name: watch TUI (detached crate) + runs-on: ubuntu-latest + # `watch/` has its own workspace and lockfile, so NOTHING in the jobs above + # compiles it — `cargo test` at the root cannot see it. Without this job the + # dashboard would rot silently, which is the standing cost of detaching a + # crate and the reason `fuzz/` carries an equivalent build gate. + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable + with: + toolchain: stable + components: clippy + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: + workspaces: watch + - name: clippy (deny warnings) + run: cargo clippy --all-targets --manifest-path watch/Cargo.toml -- -D warnings + - name: fmt + run: cargo fmt --manifest-path watch/Cargo.toml -- --check + - name: test + run: cargo test --all-targets --manifest-path watch/Cargo.toml + - name: Install cargo-audit + run: cargo install cargo-audit --locked + - name: Scan the dashboard's own dependency tree + # The two `lru 0.12.5` unsoundness advisories (RUSTSEC-2026-0002 IterMut, + # RUSTSEC-2026-0253 pop) live HERE now, which is the honest scope: lru + # arrives solely via ratatui's widget cache, and ratatui 0.29 pins + # `lru = 0.12` so no semver-compatible patched version exists. This is a + # log-reading dashboard, not the flight-path detector — the same + # rationale the root gate used to carry, except now the build enforces + # the boundary instead of a comment asserting it. + # `paste` (RUSTSEC-2024-0436, unmaintained build-time proc-macro) also + # reaches ratatui. Revisit all three when ratatui next releases. + run: cargo audit --deny warnings --file watch/Cargo.lock --ignore RUSTSEC-2024-0436 --ignore RUSTSEC-2026-0002 --ignore RUSTSEC-2026-0253 # ---- Docker image build (validates deploy/Dockerfile) ---- docker: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f04d473..080a4a9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,6 +85,12 @@ jobs: esac - name: Build release binaries (hw-i2c + journald) run: cargo build --release --locked --features hw-i2c,journald --target ${{ matrix.target }} + - name: Build the operator dashboard (detached crate) + # `watch/` has its own workspace and lockfile, so the build above does + # not produce it — that separation is what keeps the TUI's dependency + # tree (ratatui -> lru) out of the detector. It still ships in the same + # tarball, so it needs its own explicit build here. + run: cargo build --release --locked --manifest-path watch/Cargo.toml --target ${{ matrix.target }} - name: Stage artifacts + SHA-256 manifest run: | set -euo pipefail @@ -93,9 +99,10 @@ jobs: mkdir -p "$OUT" # Ship the main detector binary and the watch TUI. mavsim is a test # fixture (it emits ATTACK trajectories), deliberately NOT shipped. - for bin in flyingsquirrel flyingsquirrel-watch; do - cp "target/$TGT/release/$bin" "$OUT/" - done + # NB two different target dirs: `watch/` is a DETACHED workspace, so + # cargo puts its output under `watch/target/`, not the root one. + cp "target/$TGT/release/flyingsquirrel" "$OUT/" + cp "watch/target/$TGT/release/flyingsquirrel-watch" "$OUT/" # SHA-256 manifest — the flyingsquirrel binary's hash here MUST match # the `sha256=` field in its startup ATTESTATION log line on the # target device. That's the operator's integrity cross-check. diff --git a/.gitignore b/.gitignore index 43eb7ef..d615e20 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,11 @@ __pycache__/ # inputs, build tree, and crash artifacts are not. A crash artifact worth # keeping gets promoted into a regression test, not committed here. /fuzz/target/ +# The operator dashboard is a detached workspace, so it builds into its own +# target dir rather than the root one. Its Cargo.lock IS committed (detached +# workspaces get no lockfile from the root; a reproducible dashboard build +# needs its own). +/watch/target/ /fuzz/artifacts/ /fuzz/coverage/ diff --git a/Cargo.lock b/Cargo.lock index b7dc2ac..4f4d520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -121,13 +115,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.3", ] [[package]] @@ -202,27 +196,12 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - [[package]] name = "cc" version = "1.2.62" @@ -303,20 +282,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -348,31 +313,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73" -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.11.1", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crypto-common" version = "0.1.7" @@ -383,40 +323,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.117", -] - [[package]] name = "deranged" version = "0.5.8" @@ -437,12 +343,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - [[package]] name = "embedded-hal" version = "1.0.0" @@ -497,7 +397,6 @@ dependencies = [ "bytes", "chrono", "clap", - "crossterm", "embedded-hal", "futures", "linux-embedded-hal", @@ -507,13 +406,12 @@ dependencies = [ "proptest", "rand 0.8.6", "rand_chacha 0.3.1", - "ratatui", "rstest", "serde", "serde_json", "sha2", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tokio-serial", "tokio-stream", @@ -714,8 +612,6 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", "foldhash", ] @@ -789,12 +685,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "indexmap" version = "1.9.3" @@ -818,28 +708,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "instability" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" -dependencies = [ - "darling", - "indoc", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "io-kit-sys" version = "0.4.1" @@ -856,15 +724,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.18" @@ -925,42 +784,18 @@ dependencies = [ "sysfs_gpio", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "mach2" version = "0.4.3" @@ -1264,29 +1099,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - [[package]] name = "paste" version = "1.0.15" @@ -1336,7 +1148,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", + "toml_edit", ] [[package]] @@ -1471,42 +1283,12 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags 2.11.1", - "cassowary", - "compact_str", - "crossterm", - "indoc", - "instability", - "itertools", - "lru", - "paste", - "strum", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - [[package]] name = "rawpointer" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.1", -] - [[package]] name = "regex" version = "1.12.3" @@ -1581,19 +1363,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.11.1", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", -] - [[package]] name = "rustix" version = "1.1.4" @@ -1603,7 +1372,7 @@ dependencies = [ "bitflags 2.11.1", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -1625,12 +1394,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "safe_arch" version = "0.7.4" @@ -1697,11 +1460,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -1765,27 +1528,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -1848,12 +1590,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.11.1" @@ -1861,32 +1597,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" +name = "syn" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.117", + "unicode-ident", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1895,9 +1620,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -1922,7 +1647,7 @@ dependencies = [ "fastrand", "getrandom 0.4.2", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -2049,9 +1774,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -2073,23 +1798,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ - "serde", + "indexmap 2.14.0", + "serde_core", "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] @@ -2101,20 +1820,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.14.0", - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_write", - "winnow 0.7.15", -] - [[package]] name = "toml_edit" version = "0.25.11+spec-1.1.0" @@ -2122,25 +1827,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap 2.14.0", - "toml_datetime 1.1.1+spec-1.1.0", + "toml_datetime", "toml_parser", - "winnow 1.0.3", + "winnow", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_writer" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tracing" @@ -2254,35 +1959,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-segmentation" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -2592,15 +2268,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index 95c7958..5d9eba2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,13 +2,21 @@ name = "flyingsquirrel" version = "0.1.0" edition = "2021" -# MSRV = 1.88, the REAL floor empirically determined by building this exact -# locked dependency tree in Docker (Phase V). The binding constraints: -# * darling 0.23 + instability 0.3.12 (transitive via ratatui) need rustc 1.88 -# * clap_lex 1.1 needs the edition2024 cargo feature (rustc 1.85) -# 1.88 wins. The CI `msrv` job and deploy/Dockerfile pin 1.88 so the claim is -# continuously verified and can never drift from reality (audit I-09). The -# earlier 1.75/1.83/1.85 values were unverified guesses that did not build. +# MSRV = 1.88, the REAL floor of this exact locked tree — re-derived (not +# assumed) after the TUI was split out into `watch/`. The binding constraint +# CHANGED with that split, so the reasoning is worth keeping current: +# * WAS: darling 0.23 + instability 0.3.12, transitive via ratatui. +# * NOW: `nmea 0.7 -> serde_with -> time 0.3.47`, which declares 1.88. +# Reachable on the Linux deploy targets, so it binds the shipped artifact. +# * Runners-up: hashbrown/deranged/toml_* at 1.85; clap_lex 1.1 needs the +# edition2024 cargo feature (1.85). +# So removing ratatui did NOT lower the floor — it only moved it onto a CORE +# functional dependency (the NMEA parser) instead of an operator dashboard, +# which is the defensible place for it. Verified with a reachability walk over +# `cargo metadata --filter-platform x86_64-unknown-linux-gnu`, excluding +# dev-deps. The CI `msrv` job builds `--all-features` at this version so the +# claim can never drift from reality (audit I-09); earlier 1.75/1.83/1.85 +# values were unverified guesses that did not build. rust-version = "1.88" description = "GPS/IMU drift cross-correlator: detects coordinate spoofing by comparing GPS-reported position against IMU-dead-reckoned position." license = "MIT OR Apache-2.0" @@ -18,9 +26,11 @@ homepage = "https://github.com/Ray-Rose/flyingsquirrel" readme = "README.md" keywords = ["gps", "spoofing", "drone", "mavlink", "security"] categories = ["aerospace", "embedded"] -# The fuzz crate is a detached workspace (nightly + libFuzzer only); it is a -# development harness, not part of the published package. -exclude = ["fuzz/"] +# Both are detached workspaces with their own lockfiles, excluded from the +# published package: `fuzz/` is a nightly+libFuzzer development harness, and +# `watch/` is the operator dashboard — deliberately kept out of the detector's +# dependency tree (see watch/Cargo.toml for the full rationale). +exclude = ["fuzz/", "watch/"] [lib] name = "flyingsquirrel" @@ -34,9 +44,8 @@ path = "src/bin/flyingsquirrel.rs" name = "mavsim" path = "src/bin/mavsim.rs" -[[bin]] -name = "flyingsquirrel-watch" -path = "src/bin/flyingsquirrel-watch.rs" +# NB: `flyingsquirrel-watch` is NOT a bin target here. It lives in `watch/` as +# a detached crate so its TUI dependency tree stays out of the detector's. [dependencies] tokio = { version = "1.40", features = ["rt-multi-thread", "macros", "sync", "time", "signal", "io-util", "fs", "net"] } @@ -57,7 +66,7 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "json", "fmt"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -thiserror = "1.0" +thiserror = "2.0" clap = { version = "4.5", features = ["derive"] } rand = "0.8" rand_chacha = "0.3" @@ -65,10 +74,8 @@ mavlink = { version = "0.13", default-features = false, features = ["common", "s tokio-serial = "5.4" tokio-util = { version = "0.7", features = ["codec"] } bytes = "1" -toml = "0.8" +toml = "1.1" sha2 = "0.10" -ratatui = "0.29" -crossterm = "0.28" # Linux-only I2C IMU driver. Pulled in only when explicitly requested by # enabling the `hw-i2c` feature, and a target_os = "linux" cfg-gate keeps diff --git a/README.md b/README.md index d3c7d1a..ff77bdc 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,27 @@ docker build -f deploy/Dockerfile -t flyingsquirrel:latest . bash deploy/docker-run.sh ``` +### 3b. Operator dashboard (optional) + +`flyingsquirrel-watch` is a terminal dashboard over the JSON event log — live +`--tail` for flight ops, `--replay` for post-incident review. It is a **separate +crate** in `watch/`, built on its own: + +```bash +cd watch && cargo build --release +./target/release/flyingsquirrel-watch /var/log/flyingsquirrel/events.jsonl +``` + +It is deliberately not part of the detector's build. The dashboard shares no +code with the detector — it reads the event log with its own permissive structs, +so a schema change can't break it — but as a `[[bin]]` in the root package its +dependency tree was welded to the safety-critical one. That coupling put +`ratatui → lru` (two unsoundness advisories) into the detector's `cargo audit` +scope, its ARM cross-builds, its Docker image, and even the fuzz harness's +lockfile. Splitting it out removed 34 crates from the detector and let those two +advisory suppressions be **deleted rather than justified**. Release tarballs +still ship both binaries. + ### 4. Run ```toml @@ -460,9 +481,10 @@ workflows (`sitl.yml` for ArduPilot, `sitl-px4.yml` for PX4) are documented in |---|---| | `fmt` | `cargo fmt --all --check` — formatting is enforced, not suggested | | `test` | `clippy --all-targets --all-features -D warnings`, then `cargo test` with **default AND all features** + doc tests. The all-features run compiles the Linux-only `hw-i2c` / `journald` paths so they can't silently rot | -| `msrv` | builds against the declared MSRV (Rust 1.88, `--locked`) so `rust-version` can never drift from reality. 1.88 is the real floor (set by `darling`/`instability` via `ratatui`), determined empirically by building in Docker; this job exists because earlier unverified MSRV claims silently didn't build | +| `msrv` | builds against the declared MSRV (Rust 1.88, `--locked`) so `rust-version` can never drift from reality. 1.88 is the real floor, re-derived after the TUI split — it is now set by `nmea → serde_with → time`, a core functional dependency, where it used to be set by `ratatui`. This job exists because earlier unverified MSRV claims silently didn't build | | `cross` | cross-compiles release binaries for `aarch64-unknown-linux-gnu` and `armv7-unknown-linux-gnueabihf` (the real deploy targets) with `hw-i2c,journald`, and verifies the ELF architecture | -| `audit` | `cargo audit` — fails on any RUSTSEC advisory in the dependency tree | +| `audit` | `cargo audit` — fails on any RUSTSEC advisory in the dependency tree. Three transitive advisories are accepted with written rationale (`paste` unmaintained; two `quick-xml` build-time DoS); the two `lru` ones are gone entirely since the TUI split | +| `watch` | fmt + clippy + tests + a **separate** `cargo audit` for the detached `watch/` dashboard crate. Nothing in the jobs above compiles it, so without this gate it would rot silently | | `docker` | builds `deploy/Dockerfile` to validate the container image | The all-features test job exists specifically to catch the class of bug where a diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 6b98bf4..32c2bb3 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -10,11 +10,15 @@ # host network for MAVLink UDP (or expose 14551/udp explicitly) # -------- builder -------- -# Rust 1.88 is the MSRV, empirically determined by building this exact locked -# dependency tree (Phase V, in Docker): darling 0.23 + instability 0.3.12 -# (transitive via ratatui) require rustc 1.88; clap_lex 1.1 requires the -# edition2024 feature (1.85). 1.88 is the binding constraint. Keep this in -# sync with Cargo.toml `rust-version` and the CI `msrv` job. +# This image tracks a CURRENT toolchain, deliberately — it is NOT the MSRV +# gate. Building the shipped artifact with an up-to-date compiler is what we +# want (toolchain security fixes, better codegen); proving the declared +# `rust-version` still builds is a separate concern owned by the CI `msrv` job, +# which pins exactly 1.88 with `--locked`. Keeping both duties on this one +# FROM line is what let Dependabot's 1.88 -> 1.97 bump silently invalidate an +# "MSRV = 1.88, keep in sync" comment that used to live here. +# (The MSRV itself is 1.88, now bound by `nmea -> serde_with -> time`; see the +# reasoning in Cargo.toml.) # Digest-pinned for a reproducible, supply-chain-hardened build. The tag is kept # for humans; Docker resolves by digest. Dependabot (.github/dependabot.yml, # docker ecosystem) opens a PR when the base image updates, so the pin stays diff --git a/docs/sitl_driver.py b/docs/sitl_driver.py deleted file mode 100644 index 74ca58d..0000000 --- a/docs/sitl_driver.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python3 -""" -⚠️ DEPRECATED (Phase V prototype) — DO NOT USE FOR NEW RUNS. - -Superseded by the maintained, CI-gated harness: - • deploy/sitl/sitl_harness.py — the relay + flight choreographer - • deploy/sitl/run-sitl-validation.sh — one-command Linux / CI driver - • deploy/sitl/run-sitl-validation.ps1 — one-command Windows driver - -This early driver injects the spoof via SIM_GPS1_POS_X/Y in *metres* (a -body-frame antenna-offset param), which DIVERGES from the maintained harness: -that one defaults to wire-level relay meaconing (--spoof-mode relay) and, for -its EKF route, uses SIM_GPS_GLITCH_* in *degrees* — not SIM_GPS1_POS in metres. -It also lacks the force-arm / wait-airborne choreography that the RTL read-back -confirmation (ActionAcked) depends on, so a run here will under-report. It is -kept only as a historical reference and is guarded to refuse to run without ---force-deprecated. See docs/sitl.md for the current procedure. - ---- - -SITL closed-loop driver for FlyingSquirrel validation (Phase V). - -Runs INSIDE the ArduPilot SITL container (uses the vendored pymavlink at -/ardupilot/modules/mavlink). Drives a full closed-loop test: - - 1. Connect to SITL's MAVLink (tcp:127.0.0.1:5760). - 2. Wait for EKF + GPS ready, then arm + takeoff to altitude. - 3. Fan out every inbound MAVLink message to a UDP endpoint - (flyingsquirrel's --mav-bind) so the detector sees real GPS_RAW_INT / - GLOBAL_POSITION_INT / HEARTBEAT / SCALED_IMU traffic. - 4. After a clean-flight observation window, inject a GPS position glitch - via SITL's SIM_GPS1_POS_X/Y parameters (the autopilot's OWN simulated - GPS is spoofed — the realistic attack, not a faked wire signal). - 5. Watch the autopilot's flight-mode response and report it. - -This is the empirical answer to the four open questions in docs/sitl.md: -does PARAM_SET/GPS-glitch apply mid-flight, does the AP go RTL or LAND, does -our verify pass on real timing, does the EKF lane-switch. - -Usage (inside the container): - PYTHONPATH=/ardupilot/modules/mavlink python3 /work/sitl_driver.py \ - --sitl tcp:127.0.0.1:5760 --fanout udpout:FS_HOST:14550 \ - --takeoff-alt 30 --clean-secs 20 --glitch-m 300 --watch-secs 40 - -The --fanout target is where flyingsquirrel listens (its --mav-bind). We use -udpout so SITL-side acts as the client connecting to the detector's bound -UDP socket. -""" -import argparse -import sys -import time - -from pymavlink import mavutil - - -def log(msg): - print("[driver] {}".format(msg), flush=True) - - -def main(): - ap = argparse.ArgumentParser() - ap.add_argument("--sitl", default="tcp:127.0.0.1:5760") - ap.add_argument("--fanout", default=None, - help="udpout:HOST:PORT to forward all MAVLink to (flyingsquirrel)") - ap.add_argument("--takeoff-alt", type=float, default=30.0) - ap.add_argument("--clean-secs", type=float, default=20.0) - ap.add_argument("--glitch-m", type=float, default=300.0) - ap.add_argument("--watch-secs", type=float, default=40.0) - ap.add_argument("--force-deprecated", action="store_true", - help="run this deprecated prototype anyway (see banner / docs/sitl.md)") - args = ap.parse_args() - - if not args.force_deprecated: - log("DEPRECATED: this Phase-V prototype is superseded by " - "deploy/sitl/sitl_harness.py + run-sitl-validation.{sh,ps1}. Its " - "SIM_GPS1_POS-in-metres injection diverges from the maintained " - "harness and it lacks the force-arm choreography that ActionAcked " - "needs. Re-run with --force-deprecated only if you specifically need " - "this historical path; otherwise see docs/sitl.md.") - return 2 - - log("connecting to SITL at {}".format(args.sitl)) - m = mavutil.mavlink_connection(args.sitl, retries=10) - m.wait_heartbeat(timeout=60) - log("heartbeat from sys {} comp {}".format(m.target_system, m.target_component)) - - fan = None - if args.fanout: - # input=False → we are the originator side (udpout client). - fan = mavutil.mavlink_connection(args.fanout, input=False) - log("fanning MAVLink out to {}".format(args.fanout)) - - def forward(msg): - # Forward RAW bytes, not a re-encode — preserves the original CRC, - # sequence, and signing exactly as ArduPilot emitted them, so - # flyingsquirrel sees a byte-faithful stream. - if fan is None: - return - try: - buf = msg.get_msgbuf() - if buf: - fan.write(buf) - except Exception: - pass # best-effort forwarding - - def pump(duration, also_forward=True): - """Receive for `duration` secs, optionally forwarding to fanout. - Returns the last HEARTBEAT custom_mode seen.""" - end = time.time() + duration - last_mode = None - while time.time() < end: - msg = m.recv_match(blocking=True, timeout=1.0) - if msg is None: - continue - if also_forward: - forward(msg) - t = msg.get_type() - if t == "HEARTBEAT": - last_mode = msg.custom_mode - return last_mode - - # --- Wait for EKF/GPS ready --- - log("waiting for GPS 3D fix + EKF...") - deadline = time.time() + 90 - got_fix = False - while time.time() < deadline: - msg = m.recv_match(type="GPS_RAW_INT", blocking=True, timeout=2.0) - if msg is not None: - forward(msg) - if msg and msg.fix_type >= 3 and msg.satellites_visible >= 6: - got_fix = True - log("GPS ready: fix_type={} sats={}".format(msg.fix_type, msg.satellites_visible)) - break - if not got_fix: - log("FAIL: no GPS 3D fix within 90s") - return 2 - - # --- Set mode GUIDED, arm, takeoff --- - log("setting GUIDED") - m.set_mode("GUIDED") - pump(2) - log("arming") - m.arducopter_arm() - m.motors_armed_wait() - log("armed; takeoff to {} m".format(args.takeoff_alt)) - m.mav.command_long_send( - m.target_system, m.target_component, - mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, - 0, 0, 0, 0, 0, 0, 0, args.takeoff_alt) - - # Climb out. - pump(12) - - # --- Clean observation window (detector should stay NORMAL) --- - log("clean flight window: {} s (detector should stay Normal)".format(args.clean_secs)) - pump(args.clean_secs) - - # --- Inject GPS glitch via SITL sim params --- - # SIM_GPS1_POS_X/Y are body-frame offsets (m) added to the simulated GPS. - # A 300 m east step is far above any jump threshold. - log("INJECTING GPS GLITCH: SIM_GPS1_POS_Y = {} m".format(args.glitch_m)) - for name in ("SIM_GPS1_POS_Y", "SIM_GPS_POS_Y"): # param name varies by AP version - m.mav.param_set_send( - m.target_system, m.target_component, - name.encode("ascii"), - float(args.glitch_m), - mavutil.mavlink.MAV_PARAM_TYPE_REAL32) - log("watching autopilot mode response for {} s".format(args.watch_secs)) - last_mode = pump(args.watch_secs) - - # ArduCopter custom_mode: 0=STABILIZE 3=AUTO 4=GUIDED 5=LOITER 6=RTL 9=LAND 21=SMART_RTL - mode_names = {0: "STABILIZE", 3: "AUTO", 4: "GUIDED", 5: "LOITER", - 6: "RTL", 9: "LAND", 16: "POSHOLD", 21: "SMART_RTL"} - log("final autopilot custom_mode = {} ({})".format( - last_mode, mode_names.get(last_mode, "?"))) - log("DONE") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 2f1b067..7c83155 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android_system_properties" version = "0.1.6" @@ -190,21 +184,6 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - [[package]] name = "cc" version = "1.4.3" @@ -287,20 +266,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "compact_str" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -332,31 +297,6 @@ version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46db9f663dfb869b80fcf59e32d7a80fc6c464a4f6328f3f06a00f5e36d05f8c" -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.13.1", - "crossterm_winapi", - "mio", - "parking_lot", - "rustix", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crypto-common" version = "0.1.7" @@ -367,40 +307,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "darling" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88490bf1b990d87eaaa7ac8aa887f629a08e7359765b4911faf63c3763347d23" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084e274f91c482280130e1e34e0b8d6e66776a060d7b6de7b84289ca778868c4" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 3.0.3", -] - -[[package]] -name = "darling_macro" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5792fa0d41cd2325ce0ffa64f0a340eaebd4971a3a0c5e1ffd2cc488a355e" -dependencies = [ - "darling_core", - "quote", - "syn 3.0.3", -] - [[package]] name = "deranged" version = "0.5.8" @@ -431,12 +337,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "either" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" - [[package]] name = "equivalent" version = "1.0.2" @@ -468,18 +368,16 @@ dependencies = [ "bytes", "chrono", "clap", - "crossterm", "futures", "mavlink", "nalgebra", "nmea", "rand", "rand_chacha", - "ratatui", "serde", "serde_json", "sha2", - "thiserror 1.0.69", + "thiserror 2.0.20", "tokio", "tokio-serial", "tokio-stream", @@ -503,12 +401,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "futures" version = "0.3.34" @@ -644,17 +536,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - [[package]] name = "hashbrown" version = "0.17.1" @@ -707,12 +588,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "indexmap" version = "1.9.3" @@ -736,28 +611,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "instability" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8" -dependencies = [ - "darling", - "indoc", - "proc-macro2", - "quote", - "syn 3.0.3", -] - [[package]] name = "io-kit-sys" version = "0.4.1" @@ -774,15 +627,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.18" @@ -838,36 +682,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "mach2" version = "0.4.3" @@ -1121,29 +941,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - [[package]] name = "paste" version = "1.0.15" @@ -1234,42 +1031,12 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags 2.13.1", - "cassowary", - "compact_str", - "crossterm", - "indoc", - "instability", - "itertools", - "lru", - "paste", - "strum", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - [[package]] name = "rawpointer" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.1", -] - [[package]] name = "regex-automata" version = "0.4.18" @@ -1287,31 +1054,12 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - [[package]] name = "rustversion" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "safe_arch" version = "0.7.4" @@ -1372,11 +1120,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -1440,27 +1188,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -1512,40 +1239,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.119", -] - [[package]] name = "syn" version = "1.0.109" @@ -1727,44 +1426,42 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ - "serde", + "indexmap 2.14.0", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "indexmap 2.14.0", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_writer" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tracing" @@ -1861,35 +1558,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - [[package]] name = "utf8parse" version = "0.2.2" @@ -1969,28 +1637,6 @@ dependencies = [ "safe_arch", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.62.2" @@ -2059,15 +1705,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -2143,12 +1780,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.15" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" [[package]] name = "zerocopy" diff --git a/watch/Cargo.lock b/watch/Cargo.lock new file mode 100644 index 0000000..a18953f --- /dev/null +++ b/watch/Cargo.lock @@ -0,0 +1,918 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "compact_str" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd622ebbb56a5b2ccb651b32b911cdeb2a9b4b11776b2473bf26a26a286244e" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix 0.38.44", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags", + "crossterm_winapi", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix 1.1.4", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "darling" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88490bf1b990d87eaaa7ac8aa887f629a08e7359765b4911faf63c3763347d23" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084e274f91c482280130e1e34e0b8d6e66776a060d7b6de7b84289ca778868c4" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.3", +] + +[[package]] +name = "darling_macro" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5792fa0d41cd2325ce0ffa64f0a340eaebd4971a3a0c5e1ffd2cc488a355e" +dependencies = [ + "darling_core", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "flyingsquirrel-watch" +version = "0.1.0" +dependencies = [ + "clap", + "crossterm 0.29.0", + "ratatui", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "instability" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mio" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm 0.28.1", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.119", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/watch/Cargo.toml b/watch/Cargo.toml new file mode 100644 index 0000000..b1e1aa3 --- /dev/null +++ b/watch/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "flyingsquirrel-watch" +version = "0.1.0" +edition = "2021" +# Independently determined: this crate's binding constraint is ratatui +# (-> instability -> darling 0.23, which needs 1.88). It happens to match the +# detector's floor today, but the two are now decoupled — that is the point. +# A future ratatui bump that raises the requirement fails HERE instead of +# silently raising the detector's published MSRV. +rust-version = "1.88" +description = "Terminal dashboard for FlyingSquirrel JSON event logs." +license = "MIT OR Apache-2.0" +authors = ["Ray Rose"] +repository = "https://github.com/Ray-Rose/flyingsquirrel" +homepage = "https://github.com/Ray-Rose/flyingsquirrel" +keywords = ["gps", "spoofing", "drone", "tui", "monitoring"] +categories = ["aerospace", "command-line-utilities"] + +# DETACHED from the detector's workspace, deliberately — this is the whole +# point of the crate existing separately rather than as a third `[[bin]]`. +# +# The dashboard is an operator convenience that reads the detector's JSON event +# log. It shares NO code with the detector (it deserializes the log with its +# own loose structs so a schema change cannot break it), yet as a bin target in +# the root package its dependency tree was welded to the safety-critical one: +# +# * `ratatui` -> `lru 0.12` carried RUSTSEC-2026-0002 and RUSTSEC-2026-0253 +# into the detector's `cargo audit`. Both were accepted with the rationale +# "ratatui's widget cache, NOT the detector" — true, but nothing in the +# build enforced it. Now the separation is structural, and those two +# ignores are gone from `ci.yml` rather than argued. +# * `ratatui` -> `instability` -> `darling 0.23` was the SOLE reason the +# detector's MSRV was 1.88 rather than 1.85. +# * A terminal UI shipped inside every ARM cross-build, the Docker image, +# and even `fuzz/Cargo.lock` — a fuzz harness for NMEA/MAVLink decoding +# was receiving Dependabot PRs for a TUI library. +# +# A detached workspace (its own `Cargo.lock`), not a workspace member: members +# share one lockfile, which would leave `lru` in the detector's audit scope and +# defeat the purpose. Same pattern, same reason, as `fuzz/`. +[workspace] + +[[bin]] +name = "flyingsquirrel-watch" +path = "src/main.rs" + +[dependencies] +clap = { version = "4.5", features = ["derive"] } +ratatui = "0.29" +crossterm = "0.29" +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +[dev-dependencies] +# The log-tailing tests write real files; this came from the root package's +# dev-dependencies before the split. +tempfile = "3" + +[profile.release] +lto = "thin" +codegen-units = 1 diff --git a/src/bin/flyingsquirrel-watch.rs b/watch/src/main.rs similarity index 99% rename from src/bin/flyingsquirrel-watch.rs rename to watch/src/main.rs index a0b521a..4a5c135 100644 --- a/src/bin/flyingsquirrel-watch.rs +++ b/watch/src/main.rs @@ -30,8 +30,9 @@ //! r resume auto-scroll //! c clear visible buffer (does not affect source file) //! -//! Build: -//! cargo build --release --bin flyingsquirrel-watch +//! Build (this is a DETACHED crate — build it from `watch/`, not the repo +//! root; see `watch/Cargo.toml` for why it is separate from the detector): +//! cd watch && cargo build --release use clap::Parser; use crossterm::event::{self, Event as CtEvent, KeyCode, KeyEvent, KeyModifiers};