commit 3e18dcf2055188a06a98262e8439e481e83975df
parent 52b4f527012273d89595572605c099c000e4052e
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date: Sun, 31 May 2026 17:41:05 -0500
build: one-command install.sh / uninstall.sh + README install section
install.sh orchestrates the full setup in safe order: cargo release build →
sign Hydra.app → load LaunchAgent → build+install driver (the sudo/coreaudiod
step is last and prompts). --no-driver stops before the virtual mic. Symlinks
a `hydra` launcher to /usr/local/bin. uninstall.sh reverses it. README now
leads with ./install.sh and the TUI keymap.
Verified the non-sudo stages run clean end to end: release binaries built,
Hydra.app signs, universal Hydra.driver builds. The sudo stages (agent load,
driver install) are intentionally left for the user to trigger.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat:
| M | README.md | | | 29 | +++++++++++++++++++++++------ |
| A | install.sh | | | 61 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | uninstall.sh | | | 32 | ++++++++++++++++++++++++++++++++ |
3 files changed, 116 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
@@ -23,17 +23,34 @@ Clients talk to it over `~/Library/Application Support/hydra/hydrad.sock`.
## Status
-**Phase 0 — skeleton.** Workspace builds; daemon answers `Ping`/`GetState`; TUI renders the
-Navi-themed shell and shows live connection status. CoreAudio engine + driver land in P1–P3.
+Working: per-app audio **capture → monitor** (verified on real hardware), **combine**
+multiple apps into one route, live gain/mute, peak meters, route **persistence** across
+restarts, a `query` subcommand + SketchyBar widget, and a buildable+signed virtual-audio
+**driver** that makes "Hydra" selectable as an input in other apps. See `TESTING.md` for
+what's verified vs. what needs your one-time approval.
-## Run it
+## Install
```sh
-cargo run -p hydrad # terminal 1: start the daemon
-cargo run -p hydra # terminal 2: the TUI (press q to quit, r to refresh)
-cargo test # unit tests (model + wire-format round-trips + theme parsing)
+./install.sh # build everything; prompts before the sudo driver step
+./install.sh --no-driver # capture→speakers only (no sudo, no virtual mic yet)
+./uninstall.sh # remove it all
```
+Then launch the control panel with `hydra`, and pick **Hydra** as the microphone in any
+app to receive the routed audio.
+
+## Dev loop
+
+```sh
+cargo run -p hydrad # terminal 1: the daemon (or run the signed bundle for capture TCC)
+cargo run -p hydra # terminal 2: the TUI
+cargo test # unit tests
+```
+
+TUI keys: `↑↓` select · `⏎` monitor · `␣` mark · `c` combine · `o` output · `a` all apps ·
+`⇥` switch pane · `m` mute · `+/-` gain · `d` stop · `q` quit.
+
Swap the theme: copy `themes/navi.toml` to `~/Library/Application Support/hydra/theme.toml`.
## License
diff --git a/install.sh b/install.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+# install.sh — one-command Hydra setup.
+#
+# Builds and installs everything in the right order, pausing before the steps that
+# need sudo or restart audio so nothing happens behind your back:
+#
+# 1. build the Rust workspace (release)
+# 2. build + sign Hydra.app (the daemon, with the audio-capture entitlement)
+# 3. load the daemon as a per-user LaunchAgent (routing persists across logins)
+# 4. build + install the Hydra virtual-audio driver ← sudo + ~1s audio glitch
+#
+# After this, "Hydra" is selectable as an input device in other apps, and the `hydra`
+# TUI drives routing. Re-run any time; it's idempotent.
+#
+# Usage:
+# ./install.sh # full install (prompts before the sudo driver step)
+# ./install.sh --no-driver # everything except the driver (capture→speakers only)
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")" && pwd)"
+WITH_DRIVER=1
+[ "${1:-}" = "--no-driver" ] && WITH_DRIVER=0
+
+say() { printf '\n\033[1;36m▸ %s\033[0m\n' "$1"; }
+
+say "1/4 Building the Rust workspace (release)"
+cargo build --manifest-path "$ROOT/Cargo.toml" --release --workspace
+
+say "2/4 Building + signing Hydra.app"
+"$ROOT/scripts/bundle.sh" release
+
+say "3/4 Loading the daemon as a LaunchAgent"
+"$ROOT/scripts/install-agent.sh"
+
+# Install a `hydra` launcher on PATH for convenience (best-effort).
+if [ -w /usr/local/bin ] || sudo -n true 2>/dev/null; then
+ say "Installing the 'hydra' command to /usr/local/bin"
+ sudo ln -sf "$ROOT/target/release/hydra" /usr/local/bin/hydra 2>/dev/null \
+ || ln -sf "$ROOT/target/release/hydra" /usr/local/bin/hydra 2>/dev/null \
+ || echo " (skipped — add $ROOT/target/release to PATH yourself)"
+fi
+
+if [ "$WITH_DRIVER" = "1" ]; then
+ say "4/4 Building the Hydra virtual-audio driver"
+ "$ROOT/scripts/build-driver.sh"
+ say "Installing the driver (needs sudo; restarts coreaudiod, ~1s audio glitch)"
+ "$ROOT/scripts/install-driver.sh"
+else
+ say "4/4 Skipped driver install (--no-driver). Run ./scripts/install-driver.sh later."
+fi
+
+cat <<DONE
+
+✓ Hydra installed.
+
+ Launch the control panel: hydra (or: cargo run -p hydra)
+ In another app, pick "Hydra" as the microphone/input to receive routed audio.
+
+ Daemon logs: /tmp/hydrad.err.log
+ Uninstall: ./uninstall.sh
+DONE
diff --git a/uninstall.sh b/uninstall.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# uninstall.sh — remove everything install.sh set up. Reverses in safe order.
+set -euo pipefail
+
+LABEL="com.ganten.hydrad"
+HAL="/Library/Audio/Plug-Ins/HAL/Hydra.driver"
+
+say() { printf '\n\033[1;36m▸ %s\033[0m\n' "$1"; }
+
+say "Stopping + unloading the daemon LaunchAgent"
+launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
+rm -f "$HOME/Library/LaunchAgents/$LABEL.plist"
+
+say "Removing the 'hydra' command"
+sudo rm -f /usr/local/bin/hydra 2>/dev/null || rm -f /usr/local/bin/hydra 2>/dev/null || true
+
+if [ -d "$HAL" ]; then
+ say "Removing the driver (sudo; restarts coreaudiod, ~1s audio glitch)"
+ sudo rm -rf "$HAL"
+ sudo launchctl kickstart -k system/com.apple.audio.coreaudiod
+fi
+
+say "Removing the control socket"
+rm -f "$HOME/Library/Application Support/hydra/hydrad.sock"
+
+cat <<DONE
+
+✓ Hydra uninstalled.
+ Kept: your config + theme at ~/Library/Application Support/hydra/
+ the device manifest at /Library/Application Support/hydra/ (harmless)
+ Delete those by hand if you want a clean slate.
+DONE