Hook binary distribution
The aiot hook binary is distributed via GitHub Releases. Each release includes eight platform-specific binaries (a launcher + a runtime for each of four targets) and a SHA256SUMS-hook checksum file.
The split into a launcher and a runtime exists so that macOS Background Task Management (BTM) attributes background processes to our signature rather than Bun’s. Without the launcher, Activity Monitor’s “App Background Activity” shows “Jarred Sumner” (Bun’s author) because BTM reads the code-signing identity of the executable backing each LaunchAgent. The launcher is a ~300 KB Rust binary that execvs the ~50–80 MB Bun-compiled runtime next to it; the user never interacts with the runtime directly.
| Binary | Size | Installed as | Purpose |
|---|---|---|---|
aiot-<target> |
~300 KB | ~/.local/bin/aiot |
Rust launcher — execs the runtime |
aiot-runtime-<target> |
~50–80 MB | ~/.local/bin/aiot-runtime |
Bun-compiled CLI (all commands) |
Installation is a two-step process handled by two separate installers:
| Step | Installer | What it does |
|---|---|---|
| 1. Binary acquisition | scripts/install.sh (shell script) |
Downloads both binaries, verifies checksums, and places them on your PATH |
| 2. Service setup | aiot install (CLI subcommand) |
Writes launchd/systemd service files, starts the background daemons, and auto-wires hooks into detected agent harnesses |
Step 1 gets the binaries onto your machine. Step 2 wires them into your system services and your coding agent’s hook configuration. Both are needed for a working install.
Step 1 — Binary acquisition
Option A: Install script (recommended)
curl -fsSL https://raw.githubusercontent.com/yorch/ai-agents-observability/main/scripts/install.sh | bashOr to install a specific version or to a system-wide directory:
curl -fsSL ... | bash -s -- --version v1.0.0curl -fsSL ... | bash -s -- --prefix /usr/local/bin # system-wide (requires sudo)The default prefix is ~/.local/bin — a user-writable directory that avoids sudo entirely. If it’s not on your PATH, the script prints the export PATH= line to add to your shell profile. The subsequent aiot install command never needs sudo: it writes to user-owned directories only (~/Library/LaunchAgents/, ~/.config/systemd/user/, ~/.claude/, etc.).
Upgrading from a previous install? If you previously installed to
/usr/local/bin(the old default), re-running the script will install to~/.local/bininstead. The old binary will remain at/usr/local/binuntil you remove it (sudo rm /usr/local/bin/aiot /usr/local/bin/aiot-runtime). Or pass--prefix /usr/local/binto keep the old location.
What the script does, in order:
- Parses args —
--version <tag>,--prefix <dir>(default~/.local/bin); validates that values are present and don’t start with-. - Detects platform —
uname -s+uname -m→ one ofdarwin-arm64,darwin-x64,linux-x64,linux-arm64. Exits 1 on unsupported platforms. - Resolves version — if no
--version, queries the GitHub API for the latest release tag. - Downloads the binary — prefers
gh release downloadifghis installed and authenticated, otherwise falls back tocurl. Shows a progress bar for the 50–80 MB download. - Fetches checksums — downloads
SHA256SUMS-hookfrom the same release. A 404 (asset not published for older releases) warns and continues; any other HTTP error or network failure aborts — the binary is not installed without verification. - Verifies checksum — uses
sha256sumon Linux orshasum -a 256on macOS (strips CRLF from the checksums file first). Aborts on mismatch or if neither tool is available. - Detects upgrades — if an existing binary is at the install path, runs
--version(with a 5s timeout) and reports the old version. - Installs —
mvinto the prefix, usingsudoonly if the prefix is not writable. - Quarantine notice — on macOS, if the binary has the
com.apple.quarantinexattr, prints thexattr -dcommand to remove it.
Option B: Manual download
- Go to the releases page.
- Download both binaries for your platform:
| Launcher | Runtime | Platform |
|---|---|---|
aiot-darwin-arm64 |
aiot-runtime-darwin-arm64 |
macOS (Apple Silicon) |
aiot-darwin-x64 |
aiot-runtime-darwin-x64 |
macOS (Intel) |
aiot-linux-x64 |
aiot-runtime-linux-x64 |
Linux (x86-64) |
aiot-linux-arm64 |
aiot-runtime-linux-arm64 |
Linux (ARM64) |
- Download
SHA256SUMS-hookfrom the same release.
Option C: Via the GitHub CLI
TAG=v1.0.0 # replace with the tag you wantgh release download "${TAG}" --repo yorch/ai-agents-observability \ --pattern "aiot-darwin-arm64" \ --pattern "aiot-runtime-darwin-arm64" \ --pattern "SHA256SUMS-hook"Verify (manual download)
sha256sum -c SHA256SUMS-hook --ignore-missingBoth binaries should report OK.
Install manually (manual download)
Install both binaries to the same directory — the launcher finds the runtime by looking for aiot-runtime next to itself. The examples below use ~/.local/bin (the default, no sudo needed). Use /usr/local/bin for a system-wide install (requires sudo).
Mac:
mkdir -p ~/.local/binchmod +x aiot-darwin-arm64 aiot-runtime-darwin-arm64mv aiot-darwin-arm64 ~/.local/bin/aiotmv aiot-runtime-darwin-arm64 ~/.local/bin/aiot-runtimeIf the binary is unsigned (no Apple signing secrets were configured at build time), remove the quarantine attribute from both:
xattr -d com.apple.quarantine ~/.local/bin/aiot ~/.local/bin/aiot-runtimeSigned binaries (codesigned + notarized) do not need this step — Gatekeeper will accept them.
Linux:
mkdir -p ~/.local/binchmod +x aiot-linux-x64 aiot-runtime-linux-x64mv aiot-linux-x64 ~/.local/bin/aiotmv aiot-runtime-linux-x64 ~/.local/bin/aiot-runtimeStep 2 — Service setup and hook wiring
Once the binary is on your PATH, run:
# Persist these first when the platform is not running on localhost.aiot config set web-url https://observability.example.comaiot config set ingest-url https://ingest.example.com
aiot login # GitHub device-code OAuth flowaiot install # writes launchd/systemd services + auto-wires detected agentsaiot status # verify everything is healthyWhat aiot install does, in order:
- Guards against uncompiled use — if
process.execPathis the Bun runtime (not the compiled binary), refuses to write service files unless--forceis passed. This prevents generating services that point at the wrong executable. - Writes service files:
- macOS:
~/Library/LaunchAgents/com.brnby.aiot.{flusher,shipper}.plist(launchd) - Linux:
~/.config/systemd/user/aiot-{flusher,shipper}.service(systemd user units)
- macOS:
- Handles upgrades — if service files already exist, unloads/disables them first, then rewrites and reloads. This makes
installidempotent — re-running it after a binary upgrade restarts the daemons cleanly. - Starts the services (default,
--start): runslaunchctl load/systemctl --user enable --now. If any start step fails, exits 1 with a clear error. Use--no-startto write files without starting (prints the commands instead). - Auto-detects and wires agent harnesses — scans for installed agents (Claude Code, Codex, Gemini CLI, Copilot CLI, Pi, OMP, opencode) and automatically writes hook configuration into each detected agent’s config. In interactive mode, shows a checkbox list of detected agents; use
--yesto wire all without prompting. For shared config files, creates a.aiot-backupbefore first modification, preserves user-defined hooks, and strips only aiot-owned entries on re-install (idempotent). Agents that are not detected get their snippet printed for manual setup.
| Flag | Description |
|---|---|
--no-start |
Write service files but don’t load/enable them (prints the commands instead) |
--force |
Write service files even when running uncompiled (from the Bun runtime, not the binary) |
--yes |
Wire all detected agents without prompting |
--agent <name> |
Wire only this agent (repeatable); skips detection and prompting |
--no-auto |
Skip auto-wiring entirely; print snippets for all agents (legacy behavior) |
--dry-run |
Show what would be wired without modifying any files |
After login, historical sessions can be previewed without uploading:
aiot import --agent codex --dry-run# Also supported: claude-code, opencode, pi, ompSee apps/hook/README.md for the full CLI reference.
Air-gapped distribution
For air-gapped environments, download both binaries and SHA256SUMS-hook on a connected machine, transfer via your approved mechanism, verify checksums on the target, and install manually as described in Step 1 Option B above. Then run Step 2 (aiot install) on the target machine.
Updating
# Option A: re-run the install script (detects the upgrade, replaces both binaries)curl -fsSL https://raw.githubusercontent.com/yorch/ai-agents-observability/main/scripts/install.sh | bash
# Option B: manualgh release download v1.1.0 --repo yorch/ai-agents-observability \ --pattern "aiot-darwin-arm64" \ --pattern "aiot-runtime-darwin-arm64" \ --pattern "SHA256SUMS-hook"sha256sum -c SHA256SUMS-hook --ignore-missingchmod +x aiot-darwin-arm64 aiot-runtime-darwin-arm64mv aiot-darwin-arm64 ~/.local/bin/aiotmv aiot-runtime-darwin-arm64 ~/.local/bin/aiot-runtimeAfter replacing the binaries, re-run aiot install to restart the daemons with the new executable:
aiot install # unloads old services, rewrites files, reloadsaiot status # verify the daemons picked up the new binaryThe hook binary is stateless across versions — the local SQLite queue, identity, and service files are preserved.