-
Notifications
You must be signed in to change notification settings - Fork 2
Daemon Lifecycle and Autostart
Covers what
budi-daemondoes on the laptop after it is installed: how it starts, how it survives going offline, how it stays in sync with the CLI, and how the platform-native service contract works on each OS.
This page is the operational sibling of JSONL Tailing as Live Ingestion (ADR-0089). That ADR pins the what of live ingestion; this page documents the how of the process that runs it.
A single Rust binary (budi-daemon) that runs as a per-user service on each engineer's laptop. One process, port 7878, loopback-only. It owns:
- A single SQLite database in WAL mode (the canonical local store)
- A filesystem tailer that watches the directories each
Provider::watch_roots()returns - An HTTP server (axum) that the CLI, the Cursor extension, and the Claude Code statusline call into
- Background workers for pricing refresh and (when configured) cloud sync
Everything the daemon does, it does locally. The only outbound traffic is the LiteLLM pricing refresh and the optional cloud sync — both governed by ADRs (Model Pricing – Embedded Baseline and Runtime Refresh, Cloud Data Contract and Privacy Boundary).
Three platforms, three native mechanisms, one CLI surface to install and remove them:
| Platform | Mechanism | Service file |
|---|---|---|
| macOS | launchd LaunchAgent | ~/Library/LaunchAgents/dev.getbudi.budi-daemon.plist |
| Linux | systemd user service | ~/.config/systemd/user/budi-daemon.service |
| Windows | Task Scheduler |
BudiDaemon task (created via schtasks) |
The CLI surface is consistent across platforms:
-
budi init— creates the data dir, starts the daemon, installs autostart, wires recommended integrations idempotently -
budi autostart status— reports service state -
budi autostart install— installs the platform service -
budi autostart uninstall— removes it -
budi uninstall— full removal: service, Claude/Cursor integrations, managed legacy-proxy residue -
budi doctor— reports service-installation status alongside its other checks
Crash recovery is the platform's job: launchd restarts on exit, systemd has Restart=on-failure, Task Scheduler retries on failure. Budi does not implement its own supervisor.
The CLI and the daemon must come from the same build. The contract is enforced on the first CLI command after upgrade:
- The first command after upgrade detects the daemon's version, and auto-restarts a stale daemon when needed.
- If automatic restart fails, the CLI prints the platform-specific manual command (
pkill -f budi-daemonon Unix,taskkill /IM budi-daemon.exe /Fon Windows) and exits non-zero. -
budi initwarns when multiplebudibinaries are found onPATH— version mismatch breaks daemon restarts.
The "only one copy on PATH" invariant is the most common source of failed upgrades. Do not mix Homebrew (/opt/homebrew/bin) with ~/.local/bin/.
When budi init or a release-binary swap starts a new daemon, it must replace any existing listener on port 7878. The takeover path is platform-specific:
-
macOS / Linux:
lsofto identify the owning PID,psto confirm it's abudi-daemon,killto sendSIGTERM. The new process binds once the port is free. -
Windows: PowerShell
Get-NetTCPConnection+taskkillfor the same flow.
Unsupported or minimal environments (some hardened CI runners, locked-down dev containers) may skip the automatic takeover. In that case, stop the old daemon manually and re-run budi init. The daemon logs explicitly what it tried; check the launchd / systemd journal first.
The daemon survives three flavors of offline; in all three, the live JSONL tailer keeps running and the CLI / statusline keep reading from local SQLite:
| State | What stops | What keeps working |
|---|---|---|
| No network | Cloud sync worker, pricing-refresh worker | Tailer, CLI, statusline, every analytics route |
| Cloud auth failure (401) | Cloud sync only | Tailer, CLI, statusline, pricing refresh |
| Schema-version mismatch with cloud (422) | Cloud sync (parked until upgrade) | Everything else |
Cloud sync re-attempts with exponential backoff (1 s → 2 s → … → 5 min cap) on 429 / 5xx. Pricing refresh retries on its 24 h cadence; failure does not block ingest because the embedded LiteLLM baseline keeps serving lookups.
budi init is the canonical install entry point. It is idempotent — re-running merges with existing state instead of clobbering it.
Three install routes:
-
Homebrew (macOS / Linux):
brew install siropkin/budi/budi && budi init. The tap lives at siropkin/homebrew-budi. -
Install script:
./scripts/install.sh— builds release, installs to~/.local/bin/. -
Cargo-bin fallback (when AV blocks the install scripts):
cargo install --path crates/budi-cli --bin budi --force --locked cargo install --path crates/budi-daemon --bin budi-daemon --force --locked budi --version && budi init
budi init --no-integrations skips the recommended-integrations step (Claude Code statusline, Cursor extension) for CI runs, containers, and hand-rolled setups.
The daemon owns SQLite exclusively. The CLI never opens the DB directly — every CLI query goes through the daemon HTTP API. Two consequences:
- A daemon that won't start blocks the CLI. The fix is
budi doctor(which has its own DB open path for diagnostics) →budi autostart status→ manual restart. - Schema drift is repaired via
budi db check --fix. New schema versions migrate forward on daemon startup; the daemon refuses to start against a DB version it doesn't recognize and emits a structured error.
| Symptom | Cause | Fix |
|---|---|---|
budi --version works but budi stats returns "daemon unreachable" |
Daemon not running or stuck |
budi autostart status; budi init to reinstall the service |
budi init warns about multiple binaries |
Mixed Homebrew + Cargo install | Pick one prefix; budi uninstall from the other |
| First CLI after upgrade hangs | Daemon version mismatch and auto-restart failed | Manual kill (pkill -f budi-daemon / taskkill /IM budi-daemon.exe /F), then budi init
|
Anti-virus blocks scripts/install.sh
|
Common on Windows, occasionally on macOS | Cargo-bin fallback above |
| Port 7878 occupied by a non-budi process | Some other dev tool bound to it | Either reconfigure the other tool or kill it; budi has no port override yet |
-
crates/budi-core/src/autostart.rs— platform-native service install / uninstall / status -
crates/budi-cli/src/commands/init.rs—budi initorchestration -
crates/budi-cli/src/commands/doctor.rs— service-installation reporting + first-run nudge -
crates/budi-cli/src/commands/uninstall.rs— full-removal flow -
crates/budi-daemon/src/main.rs— HTTP server + worker wiring -
SOUL.md— Daemon autostart & Platforms sections — canonical reference
- The live ingestion contract (what feeds the tailer, attribution rules) → JSONL Tailing as Live Ingestion (ADR-0089)
- Adding a new agent / provider → Provider Plugin Contract
- The cloud sync loop (watermark, idempotency, retry) → Cloud Sync Mechanics
- Release engineering (versioning, schema-version bumps, tap publishing) → Release and Versioning
budi · Issues · Releases · app.getbudi.dev · getbudi.dev
Start here
ADRs — Data & privacy
ADRs — Ingestion
ADRs — Pricing
- Model Pricing – Embedded Baseline and Runtime Refresh
- Custom Team Pricing and Effective Cost
- Codex Cost Model – Marginal-Token Counting
ADRs — Provider contracts
Operational references
- Daemon Lifecycle and Autostart
- Provider Plugin Contract
- Cloud Sync Mechanics
- Statusline Integration
- Operations and Observability
- Release and Versioning
Ecosystem