Daemon (watchfired)
The Watchfire daemon is the backend brain — managing projects, spawning agents, handling git workflows, and serving clients over gRPC.
Daemon (watchfired)
The daemon is the backend brain of Watchfire. It manages multiple projects simultaneously, watches for file changes, spawns coding agents in sandboxed PTYs with terminal emulation, handles git worktree workflows, and serves state to thin clients over gRPC.
Lifecycle
| Aspect | Behavior |
|---|---|
| Development | Run watchfired --foreground for hot reload (tray still active) |
| Production | Runs in background, started automatically by CLI/TUI/GUI if not running |
| Persistence | Stays running when thin clients close |
| Shutdown | Ctrl+C (foreground), CLI command, or system tray quit |
The daemon starts automatically when you run any watchfire command. It persists in the background even after the CLI/TUI exits, so agent sessions keep running.
Network
| Aspect | Decision |
|---|---|
| Protocol | gRPC + gRPC-Web (multiplexed on same port) |
| Port | Dynamic allocation (OS assigns free port) |
| Discovery | Connection info written to ~/.watchfire/daemon.yaml |
| Clients | CLI/TUI use native gRPC, GUI uses gRPC-Web |
Multi-Project Management
| Aspect | Behavior |
|---|---|
| Projects index | ~/.watchfire/projects.yaml lists all registered projects |
| Registration | Projects added via CLI (watchfire init) or GUI |
| Concurrency | One active task per project, multiple projects in parallel |
| Client tracking | Tracks which clients are watching which projects |
| Task cancellation | Task stops only when ALL clients for that project disconnect |
File Watching
The daemon uses fsnotify to watch for changes:
| Aspect | Behavior |
|---|---|
| Mechanism | fsnotify with debouncing |
| Robustness | Handles create-then-rename pattern (common with AI tools) |
| Per-project | .watchfire/project.yaml, .watchfire/tasks/*.yaml |
| Polling fallback | 5s polling as safety net for missed watcher events |
| Re-watch on chain | Re-watches directories created during earlier phases |
Task Lifecycle
The daemon manages the reactive task lifecycle:
1. Client calls StartTask(task_id)
2. Daemon creates git worktree for task
3. Daemon spawns coding agent in sandboxed PTY (inside worktree)
4. Daemon streams screen buffer to subscribed clients
5. Agent updates task file when done (status: done)
6. Daemon detects via fsnotify OR polling fallback (5s)
7. Daemon kills agent (if still running)
8. Daemon processes git rules (merge, delete worktree)
9. Daemon starts next task (if queued and merge succeeded)
Session Logging
The daemon captures two types of logs for each agent session, stored in ~/.watchfire/logs/<project_id>/:
| Format | Filename | Source |
|---|---|---|
| PTY scrollback | <task_number>-<session>-<timestamp>.log | Raw terminal output captured during the session |
| JSONL transcript | <task_number>-<session>-<timestamp>.jsonl | Structured Claude Code conversation transcript (preferred) |
Transcript Auto-Discovery
On agent exit, the daemon automatically discovers the Claude Code JSONL transcript from ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl. It matches the transcript's customTitle field against the --name session name used to launch the agent, then copies the file to the logs directory.
Log Viewer
The ReadLog RPC serves session logs to clients (TUI and GUI). It prefers the .jsonl transcript when available, formatting it as a readable User/Assistant conversation with tool call summaries. If no JSONL transcript exists, it falls back to the .log file (raw PTY scrollback).
Phase Completion Signals
The daemon detects phase completion via signal files:
| Phase | Signal File | Daemon Response |
|---|---|---|
| Task | Task YAML status: done | Stop agent, merge worktree, start next |
| Refine | .watchfire/refine_done.yaml | Stop agent, start next phase |
| Generate | .watchfire/generate_done.yaml | Check for new tasks or end wildfire |
| Generate Definition | .watchfire/definition_done.yaml | Stop agent (single-shot) |
| Generate Tasks | .watchfire/tasks_done.yaml | Stop agent (single-shot) |
System Tray
The daemon runs a system tray icon with a menu on macOS and Linux:
| Menu Item | Content |
|---|---|
| Status header | "Watchfire Daemon" |
| Version | Displays the current version below the header for easy identification |
| Port | "Running on port: port" |
| Running agents | List with project name |
| Open GUI | Launches Electron GUI |
| Quit | Shuts down daemon |
Desktop Notifications
The daemon sends desktop notifications when agents complete tasks or encounter errors, via the internal/daemon/notify package.
| Platform | Backend |
|---|---|
| macOS | Native UNUserNotificationCenter via CGo (displays Watchfire icon) |
| Linux | github.com/gen2brain/beeep |
| Other | No-op (logs to stderr) |
Notifications are triggered by the tray package calling notify.Send(title, message). The Watchfire icon is embedded in the notify package.
On macOS, notifications are safely handled when running outside the .app bundle (e.g., during development or when launched directly). The daemon checks for app bundle availability before sending notifications to avoid crashes. This was fixed in v0.4.0 — previously, a notification outside the .app bundle could cause a daemon crash (exit code 2) due to an unhandled NSInternalInconsistencyException.
Health Check
The daemon exposes a Ping RPC — a lightweight health check that clients can use to verify the daemon is alive and accepting connections. Unlike GetStatus, which returns detailed daemon information, Ping is a simple empty-to-empty call designed for fast connection verification.
Startup Reliability
The daemon guarantees that ~/.watchfire/daemon.yaml (the connection discovery file) is only written after the gRPC server is fully ready and accepting connections. This eliminates race conditions where clients would read the file and attempt to connect before the port was open.
CLI and GUI both verify port readiness before proceeding after launching the daemon, so commands like watchfire agent start will not fail with "connection refused" errors even when starting the daemon for the first time.
Daemon Commands
# Start the daemon (no-op if already running)
watchfire daemon start
# Show daemon status
watchfire daemon status
# Stop the daemon
watchfire daemon stopSecrets & Setup Instructions
Give AI agents access to external services, API keys, and environment configuration without hardcoding credentials in task prompts.
CLI / TUI
The Watchfire CLI provides scriptable commands while the TUI offers an interactive split-view interface for managing tasks and monitoring agents.