1f82bc41c0
Closes the long-standing "GUI Disconnect button doesn't actually kill aura"
bug. The previous kill path sent SIGTERM to sudo (our direct child) and
hoped sudo's signal forwarding would propagate to the aura child running
as root; in practice this is unreliable when the parent has no controlling
terminal (which Tauri-spawned children don't), so aura would survive the
"Disconnect" click with the TUN still up and the OS routes still installed.
## Implementation
Adds a `Shutdown` admin-socket request. The aura-cli main loops
(`client::run` and `server::run`) now `tokio::select!` between their normal
work (router.run() / accept loop) and a `tokio::sync::Notify` carried on
the shared `AdminState`. When an admin client posts `{"cmd":"shutdown"}`
the handler calls `state.shutdown.notify_one()`, the select! second arm
fires, the work future is dropped, `OsRouteGuard::Drop` rolls back the
installed system routes, and the process exits with `Ok(())` — clean exit
code 0, kernel reaps the TUN device, no orphan.
The whole round-trip is sub-500 ms in practice (the slow step is the
`route delete` invocations on macOS).
## What changed
* `aura-cli/src/admin.rs`: `Request::Shutdown` variant, `AdminState.shutdown:
Arc<Notify>` field, handler that calls `notify_one()` + returns `Response::ok()`.
* `aura-cli/src/client.rs`: clones `admin_state.shutdown` before spawning the
admin server task, then `tokio::select!`s between `router.run()` and
`shutdown.notified()`. Whichever finishes first ends the function; OsRouteGuard
Drop runs after.
* `aura-cli/src/server.rs`: same pattern around the `MultiServer::accept` loop —
graceful exit on admin Shutdown leaves the accept loop, breaks, and the
router_task is aborted on function return.
* `aura-cli/src/main.rs`: `aura shutdown --admin-socket <path>` subcommand for
CLI control (also useful from launchd/systemd post-stop hooks).
* `aura-gui/src-tauri/src/admin.rs`: new `send_shutdown(path)` helper; factored
out `round_trip()` for the common write-line + read-line pattern. Windows
stub returns "not implemented".
* `aura-gui/src-tauri/src/cli_proc.rs`: `ClientHandle::kill` now tries admin
Shutdown first (3 s poll for graceful exit), then SIGTERM to sudo (2 s),
then SIGKILL as last resort. The admin path needs no sudo because the
socket is already chmod 0666 from v3.4.1.
## Test
New `admin::tests::shutdown_request_fires_notify` unit test: spawns a
notified() waiter, calls `handle_request(Request::Shutdown)`, asserts the
waiter wakes within 200 ms. Combined with the existing 5 admin tests, all 6
pass.
`cargo test --workspace` — all green, `cargo clippy --workspace --all-targets
-- -D warnings` — clean.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>