{"id":43707255,"url":"https://github.com/jhlee0409/proc-janitor","last_synced_at":"2026-02-15T22:18:58.948Z","repository":{"id":336560647,"uuid":"1150154317","full_name":"jhlee0409/proc-janitor","owner":"jhlee0409","description":"Automatic orphan process cleanup daemon for macOS/Linux","archived":false,"fork":false,"pushed_at":"2026-02-09T01:54:08.000Z","size":568,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-09T07:38:40.978Z","etag":null,"topics":["cleanup","cli","daemon","launchagent","macos","orphan-process","process-management","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jhlee0409.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-05T00:12:31.000Z","updated_at":"2026-02-09T01:53:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jhlee0409/proc-janitor","commit_stats":null,"previous_names":["jhlee0409/proc-janitor"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/jhlee0409/proc-janitor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhlee0409%2Fproc-janitor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhlee0409%2Fproc-janitor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhlee0409%2Fproc-janitor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhlee0409%2Fproc-janitor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jhlee0409","download_url":"https://codeload.github.com/jhlee0409/proc-janitor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jhlee0409%2Fproc-janitor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29490549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T19:29:10.908Z","status":"ssl_error","status_checked_at":"2026-02-15T19:29:10.419Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cleanup","cli","daemon","launchagent","macos","orphan-process","process-management","rust"],"created_at":"2026-02-05T06:03:58.901Z","updated_at":"2026-02-15T22:18:58.943Z","avatar_url":"https://github.com/jhlee0409.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# proc-janitor\n\n\u003e Automatic orphan process cleanup daemon for macOS (Linux experimental)\n\n[![CI](https://github.com/jhlee0409/proc-janitor/actions/workflows/ci.yml/badge.svg)](https://github.com/jhlee0409/proc-janitor/actions/workflows/ci.yml)\n[![Release](https://github.com/jhlee0409/proc-janitor/actions/workflows/release.yml/badge.svg)](https://github.com/jhlee0409/proc-janitor/releases)\n[![crates.io](https://img.shields.io/crates/v/proc-janitor.svg)](https://crates.io/crates/proc-janitor)\n[![Downloads](https://img.shields.io/crates/d/proc-janitor.svg)](https://crates.io/crates/proc-janitor)\n[![GitHub Release](https://img.shields.io/github/v/release/jhlee0409/proc-janitor)](https://github.com/jhlee0409/proc-janitor/releases/latest)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n**proc-janitor** detects and terminates orphaned processes that linger after their parent terminal or application exits. No more zombie Node.js instances eating up your RAM.\n\n## Why?\n\nWhen you close a terminal (Ghostty, iTerm2, VS Code, etc.), child processes like **Claude Code**, **Node.js**, or **MCP servers** often keep running as orphans (`PPID=1`). Each one silently consumes 200-300MB of memory.\n\nThis happens because:\n\n- **Terminals don't always send SIGHUP** when closed via Cmd+W or the window button\n- **macOS lacks `prctl(PR_SET_PDEATHSIG)`** — there's no native way to auto-kill children when the parent dies\n- **Processes escape process groups** via `setsid`, `disown`, or background execution\n\nYou end up manually running `pkill -f claude` every few hours. proc-janitor automates this.\n\n## How It Works\n\n```\nEvery 5 seconds (configurable):\n\n1. Scan process table for PPID=1 processes\n2. Match against target patterns (regex)\n3. Skip whitelisted processes\n4. Wait grace period (default 30s) to avoid false positives\n5. Send SIGTERM → wait → SIGKILL if unresponsive\n6. Log everything\n```\n\n## Installation\n\n### Quick Install (Recommended)\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/jhlee0409/proc-janitor/main/scripts/install-binary.sh | sh\n```\n\nDownloads the latest pre-built binary for your platform (macOS/Linux, x86_64/ARM64) and installs it to `/usr/local/bin`.\n\n### Homebrew (macOS/Linux)\n\n```bash\nbrew install jhlee0409/tap/proc-janitor\n\n# Start as a background service\nbrew services start proc-janitor\n```\n\n### From crates.io\n\n```bash\ncargo install proc-janitor\n```\n\n### GitHub Releases\n\nPre-built binaries for every release are available on the [Releases page](https://github.com/jhlee0409/proc-janitor/releases/latest). Download the archive for your platform, extract, and place `proc-janitor` in your PATH.\n\n| Platform | Architecture | Download |\n|----------|-------------|----------|\n| macOS | Apple Silicon (M1/M2/M3/M4) | `proc-janitor-v*-aarch64-apple-darwin.tar.gz` |\n| macOS | Intel | `proc-janitor-v*-x86_64-apple-darwin.tar.gz` |\n| Linux | x86_64 | `proc-janitor-v*-x86_64-unknown-linux-gnu.tar.gz` |\n| Linux | ARM64 | `proc-janitor-v*-aarch64-unknown-linux-gnu.tar.gz` |\n\n### Build from Source\n\n```bash\ngit clone https://github.com/jhlee0409/proc-janitor.git\ncd proc-janitor\ncargo build --release\nsudo cp target/release/proc-janitor /usr/local/bin/\n```\n\n### macOS Full Setup (LaunchAgent)\n\n```bash\ngit clone https://github.com/jhlee0409/proc-janitor.git\ncd proc-janitor \u0026\u0026 bash scripts/install.sh\n```\n\nThis builds the binary, installs it, creates a default config, and sets up a macOS LaunchAgent for auto-start on login.\n\n### Linux (systemd)\n\n```bash\ncargo install proc-janitor\nsudo cp resources/proc-janitor.service /etc/systemd/user/\nsystemctl --user enable --now proc-janitor\n```\n\n### Uninstall\n\n```bash\n# If installed via install.sh\nbash scripts/install.sh --uninstall\n\n# If installed via Homebrew\nbrew uninstall proc-janitor\n```\n\n## Quick Start\n\n```bash\n# Create a config file with explanations\nproc-janitor config init\n\n# Detect orphaned processes (safe, no killing)\nproc-janitor scan\n\n# Watch mode: continuously scan every 5 seconds\nproc-janitor scan --watch 5\n\n# Kill all detected orphans\nproc-janitor clean\n\n# Kill only specific PIDs\nproc-janitor clean --pid 12345 67890\n\n# Kill only orphans matching a pattern\nproc-janitor clean --pattern \"node.*mcp\"\n\n# Kill only orphans older than 5 minutes\nproc-janitor clean --min-age 300\n\n# Interactive mode: confirm each kill\nproc-janitor clean --interactive\n\n# Start the daemon (runs in background)\nproc-janitor start\n\n# Check status\nproc-janitor status\n\n# Stop the daemon\nproc-janitor stop\n\n# Restart the daemon (stop + start)\nproc-janitor restart\n\n# Reload config without restart (send SIGHUP)\nproc-janitor reload\n\n# View cleanup statistics (last 7 days)\nproc-janitor stats\nproc-janitor stats --days 30\n\n# View process tree filtered by pattern\nproc-janitor tree --pattern \"node\"\n\n# Diagnose issues\nproc-janitor doctor\n\n# Generate shell completions (add to your .zshrc/.bashrc)\nproc-janitor completions zsh \u003e ~/.zfunc/_proc-janitor\n\n# Get JSON output\nproc-janitor -j status\nproc-janitor -j config show\nproc-janitor -j scan\n```\n\n## Configuration\n\nConfig file: `~/.config/proc-janitor/config.toml` (all platforms)\n\n```toml\n# How often to scan (seconds, 1–3600)\nscan_interval = 5\n\n# Wait time before killing a new orphan (seconds)\ngrace_period = 30\n\n# Time to wait after SIGTERM before SIGKILL (seconds)\nsigterm_timeout = 5\n\n# Target process patterns (regex)\ntargets = [\n    \"node.*claude\",    # Claude Code\n    \"claude\",          # Claude CLI\n    \"node.*mcp\",       # MCP servers\n]\n\n# Never kill these (regex)\nwhitelist = [\n    \"node.*server\",    # Your web servers\n    \"pm2\",             # Process managers\n]\n\n[logging]\nenabled = true\npath = \"/Users/you/.proc-janitor/logs\"  # absolute path required (~ not expanded)\nretention_days = 7\n```\n\nEdit with: `proc-janitor config edit`\n\n### Environment Variable Overrides\n\nEvery config option can be overridden via environment variables. Values outside the valid range are rejected with a warning and the default is kept.\n\n| Variable | Valid Range | Example |\n|----------|------------|---------|\n| `PROC_JANITOR_SCAN_INTERVAL` | 1–3600 | `10` |\n| `PROC_JANITOR_GRACE_PERIOD` | 0–3600 | `60` |\n| `PROC_JANITOR_SIGTERM_TIMEOUT` | 1–60 | `15` |\n| `PROC_JANITOR_TARGETS` | comma-separated regexes | `\"python.*test,node.*dev\"` |\n| `PROC_JANITOR_WHITELIST` | comma-separated regexes | `\"safe1,safe2\"` |\n| `PROC_JANITOR_LOG_ENABLED` | `true` / `false` | `false` |\n| `PROC_JANITOR_LOG_PATH` | path under `$HOME` | `\"/Users/you/.proc-janitor/logs\"` |\n| `PROC_JANITOR_LOG_RETENTION_DAYS` | 0–365 | `14` |\n\n`PROC_JANITOR_LOG_PATH` is validated for safety: directory traversal (`..`), system paths (`/etc/`, `/usr/`, etc.), and paths outside `$HOME` are rejected. `/var/log/` is allowed as a standard log location.\n\n## CLI Reference\n\n### Global Options\n\n| Option | Short | Description |\n|--------|-------|-------------|\n| `--json` | `-j` | Output results in JSON format (supported by: `status`, `config show`, `scan`, `clean`, `stats`) |\n| `--quiet` | `-q` | Suppress non-essential output (hints, spinners). Useful for scripts and cron jobs. |\n\n### Core Commands\n\n| Command | Description |\n|---------|-------------|\n| `start [-f\\|--foreground] [-d\\|--dry-run]` | Start the daemon. With `--dry-run`, scan and log without killing. |\n| `stop` | Stop the daemon |\n| `status` | Show daemon status (systemctl-style with uptime) |\n| `scan [-w\\|--watch SECS]` | Detect orphaned processes (safe, no killing). With `--watch`, continuously scan at interval. |\n| `clean [--pid PIDs] [--pattern REGEX] [-i\\|--interactive] [--min-age SECS]` | Kill orphaned target processes (all by default, or filter by PID/pattern/age). With `-i`, confirm each kill. |\n| `restart [-f\\|--foreground] [-d\\|--dry-run]` | Restart the daemon (stop + start) |\n| `reload` | Reload daemon configuration (sends SIGHUP, no restart needed) |\n| `stats [--days N]` | Show cleanup statistics from the last N days (default: 7). Supports `--json`. |\n| `tree [-t\\|--targets-only] [-m\\|--pattern REGEX]` | Visualize process tree (optionally filter by regex pattern) |\n| `logs [-f\\|--follow] [-n N]` | View logs (N: 1–10000, default 50) |\n| `version` | Show version and build information |\n| `doctor` | Diagnose common issues and check system health |\n| `completions \u003cshell\u003e` | Generate shell completions (`bash`, `zsh`, `fish`, `powershell`) |\n\n### Config Commands\n\n| Command | Description |\n|---------|-------------|\n| `config init [--force] [--preset NAME] [-y\\|--yes]` | Create config (auto-detects orphans, or use preset: `claude`, `dev`, `minimal`). Use `--yes` to skip prompts. `--list-presets` to see available presets. |\n| `config show` | Display current config |\n| `config edit` | Edit config in `$EDITOR` (validates after save, supports flags like `code --wait`) |\n| `config env` | Show all environment variable overrides with current values |\n| `config validate` | Validate configuration file and show a summary |\n\n### Session Commands\n\nTrack related processes as a group. Each tracked PID stores its start_time for PID reuse detection — session cleanup verifies process identity before sending signals, even hours after registration.\n\n```bash\nproc-janitor session register --name \"my-session\" --source terminal\nproc-janitor session register --id custom-id --name \"dev\" --source vscode --parent-pid 1234\nproc-janitor session track \u003csession-id\u003e \u003cpid\u003e\nproc-janitor session list\nproc-janitor session clean \u003csession-id\u003e [--dry-run]\nproc-janitor session unregister \u003csession-id\u003e\nproc-janitor session auto-clean [--dry-run]\n```\n\nSupported `--source` values: `claude-code`, `terminal`, `vscode`, `tmux`, or any custom string.\n\n## Daemon Features\n\n### Config Auto-Reload\n\nThe daemon watches `config.toml` for changes and automatically reloads when the file is modified. No restart needed — just edit and save.\n\n### Desktop Notifications (macOS)\n\nWhen the daemon kills orphaned processes, it sends a macOS notification via Notification Center showing the count and process names.\n\n### Cleanup Statistics\n\nEvery cleanup action is recorded to `~/.proc-janitor/stats.jsonl` as append-only JSON Lines. Each entry includes a timestamp, the number of processes cleaned, and details of each kill (PID, name, signal used, success/failure). The file is automatically rotated (to `stats.jsonl.old`) when it exceeds 5 MB.\n\n## macOS LaunchAgent\n\nAuto-start on login:\n\n```bash\n# Install (done automatically by install.sh)\nlaunchctl load ~/Library/LaunchAgents/com.proc-janitor.plist\n\n# Uninstall\nlaunchctl unload ~/Library/LaunchAgents/com.proc-janitor.plist\n```\n\n## Safety\n\n- **Whitelist protection** — matching processes are never killed\n- **System PID guard** — PIDs 0, 1, 2 are always protected\n- **Grace period** — orphans get time to self-cleanup before termination\n- **PID reuse mitigation** — verifies process identity (start_time) before sending signals, including session-tracked PIDs\n- **Daemon identity verification** — `stop` confirms the PID file points to an actual proc-janitor process before sending signals\n- **Symlink protection** — refuses to write to symlinks at predictable paths (`~/.proc-janitor/`), preventing local symlink attacks\n- **TOCTOU-safe session store** — exclusive file lock held across full read-modify-write cycle\n- **Guided setup** — shows a helpful hint when no target patterns are configured, guiding users to `config init`\n- **Scan before clean** — `scan` is always safe (detection only), `clean` is always destructive (with optional filters)\n- **Atomic file operations** — config and session data use file locking with fsync for crash safety\n- **Directory permissions** — `~/.proc-janitor/` created with `0o700` (owner-only access)\n- **Audit logging** — every action is logged with timestamps\n\n## Architecture\n\n```text\nproc-janitor/\n├── src/\n│   ├── main.rs        # Entry point\n│   ├── cli.rs         # CLI argument parsing (clap)\n│   ├── daemon.rs      # Daemon lifecycle (start/stop/status)\n│   ├── scanner.rs     # Orphan process detection\n│   ├── cleaner.rs     # Process termination (SIGTERM/SIGKILL)\n│   ├── kill.rs        # Shared kill logic (system PID guard, PID reuse check, polling)\n│   ├── doctor.rs      # Health checks and diagnostics (8 checks)\n│   ├── config.rs      # TOML config + env var overrides + presets\n│   ├── config_template.toml  # Commented config template (embedded at compile time)\n│   ├── logger.rs      # Structured logging with rotation\n│   ├── session.rs     # Session-based process tracking (TrackedPid with start_time)\n│   ├── util.rs        # Shared utilities (color detection, symlink protection)\n│   └── visualize.rs   # ASCII process tree\n├── resources/\n│   └── com.proc-janitor.plist  # LaunchAgent template\n├── scripts/\n│   └── install.sh     # One-line installer\n├── tests/\n│   └── integration_test.rs\n├── Cargo.toml\n└── LICENSE\n```\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhlee0409%2Fproc-janitor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjhlee0409%2Fproc-janitor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjhlee0409%2Fproc-janitor/lists"}