Releases: jdx/pitchfork
v2.0.0: Namespaces, Port Management, and File Watching
Pitchfork v2.0.0 is a major release that introduces namespace-qualified daemon IDs, port conflict management, and several important bug fixes. The namespace change is breaking -- daemon IDs are now in namespace/name format -- but short IDs continue to work when unambiguous, and legacy log directories are automatically migrated.
Highlights
- Namespaces: Daemons across different projects no longer collide. Each project gets a namespace (derived from directory name or explicit config), and daemon IDs become
namespace/name. Short names still work when you're inside the project directory. - Port conflict detection: Daemons can declare expected ports and opt into auto-bump, so pitchfork will find an available port if the configured one is already in use.
- File watching actually works: The
watchconfig field was previously broken -- patterns were lost after daemon startup. This release rewrites the watcher to persist patterns in state and dynamically pick up new daemons.
Added
-
Port conflict detection and auto-bump: Declare
expected_portandauto_bump_portin your daemon config, and pitchfork will check for conflicts before starting and automatically increment ports if needed. Resolved ports are injected asPORT,PORT0,PORT1, etc. environment variables. New CLI flags--expected-portand--auto-bump-portare available onrun,start, andconfig add. A network view (presspin the TUI) shows all listening processes. (#259) - @benjaminwestern[daemons.api] run = "npm run dev" expected_port = [3000] auto_bump_port = true
Fixed
- File watching for daemon auto-restart: The
watchfield inpitchfork.tomlwas documented but non-functional. Watch patterns and base directories are now persisted in daemon state and the watcher dynamically refreshes every 10 seconds to pick up new daemons. (#255) - @benjaminwestern pitchfork config addgenerates valid TOML: Previously,pitchfork config addjoined all arguments into a singlerunstring, producing broken config. It now accepts proper CLI flags (--run,--retry,--watch,--depends,--env, etc.) and generates correct TOML with each option as a separate field. (#258) - @benjaminwesternpitchfork logs --tailbypasses pager: Using--tailno longer openslessand blocks with(END)-- logs now stream directly to stdout. (#253) - @jdx- TUI log rendering and scroll behavior: Fixed text wrapping causing lines to be hidden below the viewport. The scroll model was rewritten to track the last visible line rather than the first, and tab characters and ANSI clear-screen codes are now stripped from log output. (#264) - @dimmyjing
Breaking Changes
-
Daemon IDs now include a namespace: All daemon IDs are internally stored as
namespace/name(e.g.,frontend/api). The namespace is derived from the project directory name, or can be explicitly set withnamespace = "..."at the top ofpitchfork.toml. Short IDs (e.g.,api) continue to work when unambiguous. ThePITCHFORK_DAEMON_IDenvironment variable now contains the fully-qualified ID, and a newPITCHFORK_DAEMON_NAMESPACEvariable is available. Log directories are renamed from<name>/to<namespace>--<name>/; existing logs are automatically migrated to alegacynamespace. Thedependsfield now accepts cross-namespace references (e.g.,global/postgres). (#213) - @gaojunranIf you have scripts that parse
PITCHFORK_DAEMON_ID, they will now receivenamespace/nameinstead of justname. Directory names containing--are reserved for internal encoding and will require an explicitnamespaceoverride.
New Contributors
- @gaojunran made their first contribution in #213
- @benjaminwestern made their first contribution in #255
- @dimmyjing made their first contribution in #264
Full Changelog: v1.6.0...v2.0.0
v1.6.0: Lifecycle Hooks and Reverse Proxy Support
Pitchfork v1.6.0 adds daemon lifecycle hooks for reacting to ready, failure, and retry events, along with new environment variables injected into every daemon process. This release also adds support for serving the web UI behind a reverse proxy path prefix and fixes ready_cmd to respect the daemon's working directory.
Highlights
- Lifecycle hooks: Run custom shell commands when daemons become ready, fail, or retry via a new
[daemons.<name>.hooks]config section - Daemon metadata env vars: Every daemon process now receives
PITCHFORK_DAEMON_IDandPITCHFORK_RETRY_COUNTautomatically - Reverse proxy path prefixes: Serve the web UI under a custom path using
--web-pathorPITCHFORK_WEB_PATH
Added
-
Daemon lifecycle hooks -- Configure
on_ready,on_fail, andon_retryhooks under[daemons.<name>.hooks]to fire shell commands in response to daemon events. Hooks are fire-and-forget and run in the daemon's working directory. (#245) - @jdx[daemons.api] run = "node server.js" retry = 3 [daemons.api.hooks] on_ready = "echo 'api is ready'" on_fail = "./scripts/cleanup.sh" on_retry = "echo 'retrying...'"
-
Daemon process environment variables --
PITCHFORK_DAEMON_IDandPITCHFORK_RETRY_COUNTare now automatically set for every daemon process and its hooks. Theon_failhook additionally receivesPITCHFORK_EXIT_CODE. (#245) - @jdx -
Web UI path prefix support -- Use
--web-path <PATH>orPITCHFORK_WEB_PATH=<PATH>to serve the web UI under a subpath, making it easy to run behind a reverse proxy. All routes, links, HTMX polling, SSE streaming, and static assets work correctly under the prefix. (#244) - @jdxPITCHFORK_WEB_PORT=9001 PITCHFORK_WEB_PATH=ps pitchfork supervisor run # Web UI is then available at /ps/ on port 9001
Fixed
ready_cmdnow respects daemon working directory -- The readiness check command (ready_cmd) previously ran in the supervisor's directory rather than the daemon's configureddir. It now correctly uses the daemon's working directory. (#243) - @gaojunran
New Contributors
- @gaojunran made their first contribution in #243
Full Changelog: v1.5.0...v1.6.0
v1.5.0: Cleaner process shutdowns
This release improves how pitchfork stops daemons and their child processes, making shutdown more reliable and eliminating orphaned processes.
Highlights
-
Process group-based shutdown — Pitchfork now spawns each daemon in its own process group via
setsid()and useskillpg()to signal the entire group at once. This replaces the old approach of walking/procto find children, which was prone to race conditions where a child could fork between the scan and the kill. Daemons that spawn multiple children (e.g.npm run dev) will now shut down cleanly and atomically. #239 -
SIGKILL escalation after SIGTERM — When a daemon doesn't respond to SIGTERM, pitchfork now automatically escalates to SIGKILL to ensure the process is terminated. No more stuck daemons that ignore graceful shutdown signals. #238 — thanks @gaojunran!
For full documentation, see pitchfork.jdx.dev.
v1.4.3: State file bug fix
A small patch release fixing an issue where errored daemons could corrupt the state file.
Bug Fixes
- Fixed invalid state file when daemons error without an exit code — When a daemon entered an errored state without a known exit code, the state file could become unreadable due to TOML's lack of null support. Pitchfork now handles unknown exit codes gracefully, preventing state file corruption. (#231)
v1.4.1
v1.3.0: Pitchfork v1.3.0 Release Notes
Local Config Overrides, Smarter Logs, and Stop Everything
This release focuses on developer workflow improvements. You can now keep machine-specific configuration separate from shared project config with pitchfork.local.toml, filter logs by time with --since, and stop all running daemons in one command. We've also added a new ready_cmd option for more flexible readiness checks.
Highlights
Local Configuration Files (#198)
You can now create pitchfork.local.toml files alongside your pitchfork.toml to store machine-specific overrides. This is perfect for developer-specific ports, paths, or environment variables that shouldn't be committed to version control.
# pitchfork.local.toml (add to .gitignore)
[daemons.api]
run = "exec npm start -- --port 3001"See the configuration docs for details.
Stop All Daemons (#195)
Shut down everything at once with pitchfork stop --all. Daemons are stopped in reverse dependency order, so dependents stop before the services they rely on.
pitchfork stop --allCustom Readiness Commands (#187)
The new ready_cmd option lets you specify a shell command to determine when a daemon is ready. The daemon is marked ready when the command exits successfully—useful for databases, health endpoints, or any service that needs custom readiness logic.
[daemons.postgres]
run = "exec postgres -D /var/lib/postgres/data"
ready_cmd = "pg_isready -h localhost"See readiness checks for all available options.
Time-Based Log Filtering (#204)
Filter logs with human-friendly time expressions using --since. Supports relative times, clock times, and full datetimes. Logs also now use a pager by default for easier reading.
pitchfork logs api --since 5min # last 5 minutes
pitchfork logs api --since 1h # last hour
pitchfork logs api --since '10:30' # since 10:30 AM todayImprovements
-
List command now shows available daemons (#206) - Daemons defined in config but not yet started now appear in
pitchfork list, so you can see everything available without checking your config file. -
Web UI improvements (#191) - The web interface got a visual refresh.
Bug Fixes
- Fixed daemon stop logic to handle edge cases more reliably (#192)
v1.2.0: Beautiful Errors and a Visual Config Editor
Pitchfork v1.2.0 brings a major quality-of-life upgrade: a built-in config editor in the TUI and completely overhauled error messages. No more guessing what went wrong—errors now show exactly where problems occur with source code highlighting, helpful suggestions, and links to documentation.
The new TUI config editor lets you create, edit, and delete daemons without leaving the terminal or hand-editing TOML files. Combined with the improved error diagnostics, configuring pitchfork is now significantly more approachable for new users.
Highlights
TUI Config Editor (#171)
Create and manage daemon configurations directly from the TUI:
- Press
nto create a new daemon - Press
Eto edit an existing daemon - Press
Dto delete a daemon - Full form-based editor with validation for all daemon options
See the TUI guide for complete keybinding reference.
Rich Error Diagnostics (#180, #181, #182, #183)
Errors now include context that actually helps you fix problems:
× failed to parse configuration
╭─[pitchfork.toml:5:1]
4 │ [daemons.api]
5 │ run =
· ╰── expected string
╰────
help: check TOML syntax at https://toml.io
- Config errors show the exact file and line where parsing failed
- IPC errors suggest fixes like "ensure the supervisor is running with: pitchfork supervisor start"
- All errors include documentation links to pitchfork.jdx.dev
Internal Improvements
- Modularized supervisor into focused submodules for better maintainability (#175)
Full Changelog: v1.1.0...v1.2.0
v1.1.0
Pitchfork v1.1.0 brings file watching for automatic daemon restarts—a much-requested feature for development workflows. When your source files change, pitchfork can now automatically restart the affected daemons, eliminating the need for manual restarts during development. This release also includes improved retry configuration, better cron scheduling accuracy, and makes the web UI opt-in to reduce resource usage.
Highlights
File Watching for Auto-Restart (#165)
Daemons can now automatically restart when specified files change. Use glob patterns to watch your source files:
[daemons.api]
run = "npm run dev"
watch = ["src/**/*.ts", "package.json"]Changes are debounced (1 second) to avoid rapid restarts during batch saves. See the File Watching guide for details.
Boolean Retry Configuration (#170)
Retry configuration now accepts boolean values for common cases:
[daemons.api]
retry = true # Retry forever on failure
[daemons.oneshot]
retry = false # Never retryNumeric values continue to work for specific retry counts. See the Auto-Restart guide for more information.
Web UI Now Opt-In (#172)
The web UI no longer runs by default, reducing resource usage for CLI and TUI users. Enable it when needed with --web-port or PITCHFORK_WEB_PORT:
pitchfork supervisor run --web-port 19876Improvements
-
Auto-generated JSON schema (#167) - The JSON schema for
pitchfork.tomlis now auto-generated from Rust types, ensuring it always stays in sync with the actual configuration options. -
macOS code signing (#173) - macOS binaries are now code-signed, reducing security warnings during installation.
Bug Fixes
-
Cron scheduling accuracy (#163) - Sub-minute cron schedules (e.g.,
*/30 * * * * *) now work correctly. The cron watcher checks every 10 seconds instead of 60, and properly tracks the last trigger time to prevent missed or duplicate executions. -
Log tailing reliability (#164) - Fixed race conditions and position tracking in
pitchfork logs --tailthat could cause gaps in log output.
Full Changelog: v1.0.0...v1.1.0
v1.0.1
Added
Fixed
- restart command preserves daemon dependency configuration (#142)
- add missing depends field to restart command (#136)
- set IPC socket permissions to 0600 for security (#133)
- handle shell command parsing errors instead of silently failing (#132)
Other
- bump version to 1.0.0 (#147)
- release v0.3.1 (#121)
- reduce unnecessary daemon cloning in loops (#144)
- use periodic log flushing instead of per-line (#139)
- refresh only tracked PIDs instead of all processes (#141)
- cache compiled regex patterns (#143)
Security
v1.0.0
Added
Fixed
- restart command preserves daemon dependency configuration (#142)
- add missing depends field to restart command (#136)
- set IPC socket permissions to 0600 for security (#133)
- handle shell command parsing errors instead of silently failing (#132)
Other
- reduce unnecessary daemon cloning in loops (#144)
- use periodic log flushing instead of per-line (#139)
- refresh only tracked PIDs instead of all processes (#141)
- cache compiled regex patterns (#143)