
Rch
- 2 installs
- 55 repo stars
- Updated August 4, 2026
- dicklesworthstone/remote_compilation_helper
Offloads cargo/gcc/bun builds to remote workers and diagnoses worker fleet health, hook routing, and sync failures using rch CLI commands.
About
Operates the RCH remote-compilation tool to offload slow builds to remote workers, with a triage order, quick-fix table, and worker storage inspection commands. A developer uses it when compilation is slow, workers are unhealthy, or remote sync/execution is failing.
- Fast triage order from availability to remote compile proof
- Quick-fix table for hooks, daemon, socket, and worker storage
Rch by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,139 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dicklesworthstone/remote_compilation_helper --skill rchAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 55 |
| Last updated | August 4, 2026 |
| Repository | dicklesworthstone/remote_compilation_helper ↗ |
What it does
Offloads cargo/gcc/bun builds to remote workers and diagnoses worker fleet health, hook routing, and sync failures using rch CLI commands.
Files
RCH — Remote Compilation Helper
Use this skill for remote compilation offload, worker fleet health checks, and hook incident recovery.
Quick Start
rch check
rch status --workers --jobs
rch workers probe --all
rch hook status
rch diagnose --dry-run "cargo check --workspace --all-targets"
rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_<name> cargo check --workspace --all-targetsIMPORTANT: Always use ${TMPDIR:-/tmp} for target dirs, never hardcode /tmp or /var/tmp. On fleet machines TMPDIR=/data/tmp (disk-backed). Hardcoding /tmp wastes tmpfs RAM; /var/tmp survives reboots and accumulates silently.
If rch exec -- ... succeeds, remote offload is healthy and remaining failures are likely project/toolchain specific.
If rch status shows storage pressure, always check both / and /tmp on the worker before deciding what to fix:
ssh ubuntu@<host> 'df -h / /tmp && free -h && cat /proc/pressure/memory && cat /proc/pressure/io'---
Fast Triage Order
Run in this order and stop at the first failing stage:
1. Availability
rch check
rch status --workers --jobs
rch workers probe --all
rch queue2. Config + socket consistency
rch config show --sources
rch --json config get general.socket_path
rch --json daemon status3. Hook integration
rch hook status
rch agents status
rch hook install4. Command classification + path closure
rch diagnose "cargo build --release"
rch diagnose --dry-run "cargo test --workspace"5. Remote compile proof
rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_<name> cargo check --workspace --all-targets6. If sync fails or storage looks bad, inspect the worker directly
ssh ubuntu@<host> 'df -h / /tmp'
ssh ubuntu@<host> 'du -sh /tmp/rch-* /tmp/rch_target_* 2>/dev/null | sort -h'
ssh ubuntu@<host> 'find /data/projects -maxdepth 2 -type d \\( -name "target_rch_*" -o -name "target_*" -o -name "target-*" -o -name target \\) -exec du -sh {} + 2>/dev/null | sort -h | tail'---
Quick Fixes
| Symptom | Command |
|---|---|
| Hook not installed | rch hook install && rch hook status |
| Daemon not running | rch daemon start |
| Socket mismatch / stale daemon state | rch daemon restart -y then rch --json daemon status |
| No workers configured | rch workers discover --add --yes && rch workers setup --all |
| Workers unreachable | rch workers probe --all then fix SSH key/host reachability |
| Transfer churn under target dirs | Add excludes in ~/.config/rch/config.toml, then rch daemon reload |
| Path dependency missing remotely | Ensure required sibling repos exist on workers under canonical project roots, then retry rch exec -- ... |
Sync fails with Permission denied in /data/projects/<repo> | Fix remote mirror ownership: ssh ubuntu@<host> 'sudo chown -R ubuntu:ubuntu /data/projects/<repo> && sudo chmod 775 /data/projects/<repo>' |
| Worker shows pressure warning | Check / and /tmp separately, then inspect stale rch_target_*, rch-*, and target_rch_* dirs before broader cleanup |
| Need full environment diagnosis | rch doctor and rch config doctor |
---
Reference Index
Use these files for full depth:
- Runbooks + operational playbooks:
references/OPERATIONS.md - Troubleshooting flow + failure signatures:
references/TROUBLESHOOTING.md - Worker lifecycle operations:
references/WORKERS.md - Config hierarchy + environment controls:
references/CONFIGURATION.md - PreToolUse hook protocol and behavior:
references/HOOKS.md - Workers config template:
assets/workers-template.toml - Project docs: https://github.com/Dicklesworthstone/remote_compilation_helper
# RCH Workers Configuration Template
# Copy to: ~/.config/rch/workers.toml
#
# Each [[workers]] block defines a remote machine for compilation offloading.
# RCH selects workers based on available slots, priority, and project locality.
# Example: Primary fast build server
[[workers]]
id = "primary" # Unique identifier
host = "192.168.1.100" # IP or hostname
user = "ubuntu" # SSH username
identity_file = "~/.ssh/id_ed25519" # Path to SSH private key
total_slots = 16 # Max concurrent jobs (usually CPU cores)
priority = 100 # Higher = preferred (default: 50)
tags = ["rust", "bun", "fast"] # Optional capability tags
# enabled = true # Set to false to disable temporarily
# Example: Secondary backup server (lower priority)
# [[workers]]
# id = "backup"
# host = "build-backup.local"
# user = "build"
# identity_file = "~/.ssh/build_key"
# total_slots = 8
# priority = 50
# tags = ["rust"]
# Example: Specialized TypeScript worker
# [[workers]]
# id = "typescript-builder"
# host = "ts.internal"
# user = "node"
# identity_file = "~/.ssh/ts_key"
# total_slots = 12
# priority = 75
# tags = ["bun", "typescript"]
# Tips:
# - total_slots: Match to CPU cores. Leave 1-2 for system overhead.
# - priority: Range 1-100. Equal slots? Higher priority wins.
# - tags: Projects can require specific tags in .rch.toml
# - identity_file: Use ssh-agent for passphrase-protected keys
#
# Test your configuration:
# rch config validate
# rch config doctor
# rch workers probe --all
# rch workers setup --all
Command Reference
Core Commands
| Command | Purpose | Common Flags |
|---|---|---|
rch doctor | Diagnose issues | --fix, --verbose, --json |
rch status | Daemon status | --json |
rch workers probe | Test workers | --all, -v, worker_id |
rch workers list | List workers | --capabilities, --json |
rch workers discover | Auto-find workers | --from-ssh-config, --dry-run |
rch hook install | Setup Claude hook | --force |
rch hook status | Check hook | — |
rch config show | Show config | — |
rch config check | Validate config | — |
Daemon Commands
# Start (choose one)
rchd & # Background
rchd --foreground # Foreground (for debugging)
systemctl --user start rchd # Systemd (Linux)
launchctl load ~/Library/LaunchAgents/com.rch.daemon.plist # macOS
# Stop
systemctl --user stop rchd
launchctl unload ~/Library/LaunchAgents/com.rch.daemon.plist
# Logs
journalctl --user -u rchd -f # Linux
tail -f ~/.config/rch/logs/daemon.log # macOSDebug Commands
# Verbose diagnostics
rch doctor --verbose
# Export diagnostics
rch doctor --json > diagnostic.json
# Test hook directly
echo '{"tool":"Bash","input":{"command":"cargo check"}}' | rch hook
# Dry run (logs without execution)
RCH_DRY_RUN=1 cargo check
# Debug logging
RCH_LOG=debug cargo build
RCH_LOG=trace cargo build # Maximum detailWorker Probe Output
rch workers probe worker1 --verboseShows:
- SSH connectivity (port 22)
- Detected toolchains (rustc, cargo, bun, gcc, clang)
- Disk space (/tmp)
- System load
Environment Variables
| Variable | Purpose | Example |
|---|---|---|
RCH_LOG | Log level | debug, trace, info |
RCH_DRY_RUN | No remote execution | 1 |
RCH_CONFIG_DIR | Config location | ~/.config/rch |
RCH_NO_COLOR | Disable colors | 1 |
Config Files
| File | Purpose |
|---|---|
~/.config/rch/workers.toml | Worker definitions |
~/.config/rch/daemon.toml | Daemon settings |
~/.config/rch/config.toml | Hook settings |
.rch.toml (project) | Per-project overrides |
RCH Configuration Reference
Precedence and File Locations
RCH resolves settings in this order (highest to lowest):
1. CLI flags (--json, --verbose, etc.) 2. Environment variables (RCH_*) 3. Profile defaults (RCH_PROFILE) 4. .env / .rch.env 5. Project config (.rch/config.toml) 6. User config (~/.config/rch/config.toml) 7. Built-in defaults
Primary files:
- User config:
~/.config/rch/config.toml - Worker config:
~/.config/rch/workers.toml - Project override:
.rch/config.toml - Optional transfer excludes:
.rchignore
---
Main Config (~/.config/rch/config.toml)
[general]
enabled = true
force_local = false
force_remote = false
log_level = "info" # trace, debug, info, warn, error, off
socket_path = "~/.cache/rch/rch.sock" # default resolves from runtime/cache path
[compilation]
confidence_threshold = 0.85
min_local_time_ms = 2000
remote_speedup_threshold = 1.2
build_slots = 4
test_slots = 8
check_slots = 2
build_timeout_sec = 300
test_timeout_sec = 1800
bun_timeout_sec = 600
external_timeout_enabled = true
[transfer]
compression_level = 3
remote_base = "/tmp/rch"
adaptive_compression = true
verify_artifacts = false
exclude_patterns = [
"target/",
".git/objects/",
"node_modules/",
]
[selection]
strategy = "fair_fastest"
[output]
visibility = "summary" # none, summary, verbose
first_run_complete = true
[self_healing]
hook_starts_daemon = true
daemon_installs_hooks = true
auto_start_timeout_secs = 3Socket path default behavior:
- First choice:
$XDG_RUNTIME_DIR/rch.sock - Fallback:
~/.cache/rch/rch.sock - Last resort:
/tmp/rch.sock
---
Workers Config (~/.config/rch/workers.toml)
[[workers]]
id = "worker-name"
host = "203.0.113.20"
user = "ubuntu"
identity_file = "~/.ssh/id_ed25519"
total_slots = 16
priority = 100
tags = ["rust", "bun", "fast"]Slot guidance:
- Start with ~
2xphysical CPU cores for mixed workloads. - Reduce slots if workers hit CPU steal, swap pressure, or I/O saturation.
- Increase
priorityfor faster/more reliable workers.
---
Environment Variables
Common overrides:
| Variable | Purpose |
|---|---|
RCH_PROFILE | Base profile (dev, prod, test) |
RCH_LOG_LEVEL | Logging level override |
RCH_DAEMON_SOCKET | Daemon socket override (CLI layer) |
RCH_SOCKET_PATH | Socket override (config layer) |
RCH_DAEMON_TIMEOUT_MS | Daemon IPC timeout |
RCH_SSH_KEY | Default SSH key path |
RCH_TRANSFER_ZSTD_LEVEL | Transfer compression level |
RCH_ENV_ALLOWLIST | Forwarded env vars for remote execution |
RCH_VISIBILITY / RCH_VERBOSE / RCH_QUIET | Hook/CLI visibility controls |
RCH_OUTPUT_FORMAT / TOON_DEFAULT_FORMAT | Machine output format |
RCH_JSON / RCH_HOOK_MODE | Force machine/hook output mode |
NO_COLOR / FORCE_COLOR | ANSI color behavior |
---
Hook Configuration (Claude Code)
Location: ~/.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/absolute/path/to/rch"
}
]
}
]
}
}Recommended management commands:
rch hook install
rch hook status
rch hook uninstall---
Validation and Diagnostics
rch config show --sources
rch config validate
rch config lint
rch config doctor
rch check---
Runtime Data Paths
| Path | Purpose |
|---|---|
~/.local/share/rch/telemetry/telemetry.db | Telemetry persistence |
~/.local/share/rch/fleet_history/ | Fleet deployment history |
~/.cache/rch/ | Cache + default socket parent |
/tmp/rch/ | Remote transfer workspace base (default) |
Hook Integration
Execution Flow
Claude Code PreToolUse -> rch (no subcommand = hook mode)
|
+-- Non-Bash tool -> allow unchanged
+-- Bash non-compilation -> allow unchanged
+-- Bash compilation -> allow with modified command:
"rch exec -- <original command>"Important behavior:
- Hook returns quickly (classification path), then remote execution happens via
rch exec -- .... - On unsafe/unavailable remote conditions, hook fails open and allows local execution.
- For successful or failed remote runs, RCH preserves command semantics by rewriting to
trueorexit <code>as needed.
---
Installation and Status
rch hook install
rch hook status
rch hook test
rch hook uninstallFor multi-agent installs:
rch agents list
rch agents status
rch agents install-hook claude-code
rch agents uninstall-hook claude-codeInstalled Claude settings entry:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "/absolute/path/to/rch" }
]
}
]
}
}---
Hook Protocol (Current)
Input on stdin
{
"tool_name": "Bash",
"tool_input": {
"command": "cargo build --release",
"description": "Build project"
},
"session_id": "optional"
}Output on stdout
1. Allow unchanged command: empty stdout 2. Allow with command rewrite (transparent interception):
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "rch exec -- cargo build --release"
}
}
}3. Deny command (rare, policy-level):
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "reason text"
}
}---
What Gets Intercepted
Common intercepted command families:
- Rust:
cargo build,cargo check,cargo clippy,cargo doc,cargo test,cargo nextest run,cargo bench,rustc - Bun/TypeScript:
bun test,bun typecheck - C/C++:
gcc,g++,clang,clang++ - Build systems:
make,cmake --build,ninja,meson compile
Commonly not intercepted:
- Local-mutating package commands (
cargo install,cargo clean,bun install,bun add, etc.) - Interactive/dev commands (
bun run,bun dev,bun build,bunx) - Piped/redirected/backgrounded shell forms where deterministic offload is unsafe
---
Quick Hook Tests
# Direct protocol test (compilation command)
printf '%s\n' \
'{"tool_name":"Bash","tool_input":{"command":"cargo build --release"}}' | rch
# Direct protocol test (non-compilation command should usually return empty stdout)
printf '%s\n' \
'{"tool_name":"Bash","tool_input":{"command":"ls -la"}}' | rch
# Built-in integration test
rch hook testDebugging:
RCH_LOG_LEVEL=debug rch hook test
RCH_LOG_LEVEL=debug rch diagnose "cargo test --workspace"---
Performance and Safety Notes
- Non-compilation decisions target sub-millisecond latency.
- Compilation decisions target low-millisecond latency.
- Hook mode always keeps stdout protocol-clean; diagnostics go to stderr.
- If parsing/config/daemon selection fails, RCH allows local execution instead of blocking.
RCH Operations
Baseline Runbook
Use this sequence for most production incidents.
1) Confirm current posture
rch check
rch status --workers --jobs
rch workers probe --all
rch queue2) Validate config and daemon wiring
rch config show --sources
rch --json config get general.socket_path
rch --json daemon status
rch config doctor3) Validate hook routing
rch hook status
rch agents status
rch hook test4) Validate offload path directly
rch diagnose "cargo check --workspace --all-targets"
rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_<name> cargo check --workspace --all-targetsIf step 4 succeeds, RCH infrastructure is healthy and remaining failures are project/toolchain specific.
5) If workers show storage pressure, inspect the right filesystem
RCH pressure warnings often come from / while the immediate churn lives in /tmp. Check both before deciding whether the host actually needs intervention:
ssh ubuntu@<host> 'df -h / /tmp'
ssh ubuntu@<host> 'free -h && cat /proc/pressure/memory && cat /proc/pressure/io'Then inspect the usual large artifact surfaces:
ssh ubuntu@<host> 'du -sh /tmp/rch-* /tmp/rch_target_* 2>/dev/null | sort -h'
ssh ubuntu@<host> 'find /data/projects -maxdepth 2 -type d \( -name "target_rch_*" -o -name "target_*" -o -name "target-*" -o -name target \) -exec du -sh {} + 2>/dev/null | sort -h | tail -n 20'Before removing anything, verify the candidate is inactive:
ssh ubuntu@<host> 'sudo lsof +D /tmp/rch_target_<name>'
ssh ubuntu@<host> 'sudo lsof +D /data/projects/<repo>/target_rch_<name>'Only treat empty lsof results as a low-risk stale-artifact cleanup signal.
6) If rch exec fails at sync time, verify remote mirror ownership
When the canonical worker mirror under /data/projects/<repo> is owned by root or another account, rsync fails with Permission denied or Operation not permitted.
Check:
ssh ubuntu@<host> "stat -c '%U:%G %a %n' /data/projects/<repo>"Fix:
ssh ubuntu@<host> 'sudo chown -R ubuntu:ubuntu /data/projects/<repo> && sudo chmod 775 /data/projects/<repo>'After the fix, rerun:
rch diagnose --dry-run "cargo build --release"
rch exec -- cargo build --release---
Worker Fleet Lifecycle
Discovery and setup
rch workers discover
rch workers discover --probe
rch workers discover --add --yes
rch workers setup --allRuntime management
rch workers list --speedscore
rch workers capabilities --refresh
rch workers benchmark
rch workers drain <worker> -y
rch workers enable <worker>
rch workers disable <worker> --reason "maintenance" --drain -yToolchain/binary synchronization
rch workers sync-toolchain --all
rch workers deploy-binary --all---
Fleet Deploy/Rollback
rch fleet status
rch fleet deploy --verify
rch fleet deploy --canary 25 --canary-wait 60 --verify
rch fleet rollback --verify
rch fleet history --limit 20For large fleets:
- Prefer canary first, then full rollout.
- Use
--dry-runbefore disruptive operations. - Use
--drain-firstif workers are heavily loaded.
---
Path-Dependency and Multi-Repo Notes
RCH now supports dependency-closure planning and canonical topology handling, but path-based workspaces still require worker-accessible sibling repos.
Recommended checks:
rch diagnose --dry-run "cargo test --workspace"
rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_<name> cargo test --workspace --no-fail-fastIf remote path dependencies are missing:
- Ensure required sibling repos exist on worker hosts under canonical project roots.
- Re-run
rch workers setup --alland then retry therch exec -- ...command.
---
Transfer Stability (Rsync/Artifact Churn)
If sync fails due to active artifact churn, extend transfer excludes:
[transfer]
exclude_patterns = [
"target/",
"target_*/",
"target-*/",
".cargo-target/",
".cargo-target-*/",
]Then reload daemon config:
rch daemon reload
rch config show --sourcesOperational note:
- If you need a manual target dir for Rust builds, prefer
/tmp/rch_target_<name>. - If the working tree itself cannot sync because the remote canonical mirror is broken, either repair ownership on the worker or temporarily build from a clean directory under
/data/projects, not/tmp, because RCH canonical-root normalization expects/data/projects.
---
Queue and Cancellation Operations
rch queue
rch queue --watch
rch cancel <build-id>
rch cancel --all --yesUse cancellation when builds are wedged or backlog pressure is starving high-priority work.
---
Anti-Patterns
| Don't | Why | Do Instead |
|---|---|---|
| Assume remote failures mean local failures | Some failures are worker/config topology issues | Validate with rch diagnose + rch exec -- ... |
Hardcode /tmp/rch.sock in runbooks | Default socket may be runtime/cache path | Query via rch --json daemon status |
Skip rch check and jump to manual SSH surgery | Loses quick signal on daemon/hook/worker health | Start with rch check and rch status --workers --jobs |
| Ignore queue pressure | Can cascade into timeouts and local fallback | Monitor rch queue --watch and cancel stale builds |
| Apply broad/destructive worker cleanup | Risks collateral damage | Prefer targeted fixes + workers setup/fleet commands |
Assume /tmp pressure and / pressure are the same problem | They often are not; fixing the wrong one wastes time | Check df -h / /tmp and inspect the matching artifact surface |
| Delete large build dirs without checking for open files | Risks breaking active remote builds | Run sudo lsof +D <dir> first and only clean inactive candidates |
---
Debug Command Pack
RCH_LOG_LEVEL=debug rch diagnose "cargo build --release"
RCH_LOG_LEVEL=debug rch check
rch doctor --json > /tmp/rch-doctor.json
rch --json workers probe --all > /tmp/rch-workers-probe.json
rch daemon logs -n 200RCH Troubleshooting
Diagnostic Flow
Compilation running locally instead of remotely?
│
├─ Quick health gate:
│ $ rch check
│ │
│ ├─ Not ready/degraded?
│ │ ├─ Check daemon:
│ │ │ $ rch --json daemon status
│ │ │
│ │ ├─ Check workers:
│ │ │ $ rch workers probe --all
│ │ │
│ │ └─ Check hook install:
│ │ $ rch hook status
│ │
│ └─ Ready?
│ continue below
│
└─ Ready but behavior is wrong?
├─ Socket alignment:
│ $ rch --json config get general.socket_path
│ $ rch --json daemon status
│
├─ Explain routing decision:
│ $ rch diagnose "cargo build --release"
│
├─ Validate hook protocol path:
│ $ rch hook test
│
└─ Force direct offload proof:
$ rch exec -- cargo check --workspace --all-targets---
Common Errors
Daemon not running / check says not ready
Cause: daemon process absent or startup failure.
rch daemon start
rch --json daemon status
rch daemon logs -n 200Socket mismatch between config and daemon
Cause: general.socket_path differs from active daemon socket.
rch --json config get general.socket_path
rch --json daemon status
# then align and restart:
rch daemon restart -y"No workers available" / probe failures
Cause: no workers configured, SSH/auth failures, or workers are disabled/drained.
rch workers list
rch workers probe --all
rch workers discover --probe
rch workers discover --add --yes
rch workers setup --all"rustup: not found" / "cargo: not found" on worker
Cause: missing toolchain on one or more workers.
rch workers sync-toolchain --all
rch workers capabilities --refreshIf still failing, SSH to the specific worker and validate rustup, cargo, and PATH.
Hook not intercepting
Cause: hook missing, wrong binary path, or command classified as local.
rch hook status
rch hook install
rch hook test
rch diagnose "cargo build --release"Sync/transfer fails under active target churn
Cause: build artifacts changing during rsync.
# Add target-like excludes in ~/.config/rch/config.toml [transfer].exclude_patterns
rch daemon reload
rch config show --sourcesAlso inspect the worker directly:
ssh ubuntu@<host> 'df -h / /tmp'
ssh ubuntu@<host> 'du -sh /tmp/rch-* /tmp/rch_target_* 2>/dev/null | sort -h'If cleanup is needed, verify inactivity first:
ssh ubuntu@<host> 'sudo lsof +D /tmp/rch_target_<name>'If the directory is inactive, prefer targeted stale-artifact cleanup over broad cache deletion.
Sync fails with Permission denied or Operation not permitted inside /data/projects/<repo>
Cause: the canonical mirror on the worker is not writable by the SSH user. This commonly happens when a repo under /data/projects was created or updated as root.
Check:
ssh ubuntu@<host> "stat -c '%U:%G %a %n' /data/projects/<repo>"Fix:
ssh ubuntu@<host> 'sudo chown -R ubuntu:ubuntu /data/projects/<repo> && sudo chmod 775 /data/projects/<repo>'Then retry:
rch exec -- cargo check --workspace --all-targetsrch exec fails open for workdirs outside /data/projects
Cause: canonical-root normalization rejects workdirs outside the configured project root.
Symptoms include errors mentioning input resolves outside canonical root.
Fix:
pwd
rch diagnose --dry-run "cargo build --release"Then run the build from a workspace under /data/projects. If you need a clean copy for testing, stage it under /data/projects/<temp-repo> instead of /tmp/<temp-repo>.
Worker shows storage pressure even after cleanup
Cause: telemetry lag, large ballast allocation, or active live build churn.
Check:
rch status --workers --jobs
ssh ubuntu@<host> 'df -h / /tmp && free -h'
ssh ubuntu@<host> 'journalctl -u sbh -n 50 --no-pager'Interpretation:
- If
dfis healthy butrch statusstill warns, give telemetry a minute and refresh. - If
/tmpis healthy but/is still low, inspect large projecttarget_*trees under/data/projects. - If
sbhis active but repeatedly loggingscan channel saturatedorscan timed out, inspect stale build artifacts and verify the host is running the currentsbhbinary and the narrowed worker config.
Path dependency missing remotely (../.../Cargo.toml)
Cause: required sibling repositories are not available in worker topology.
rch diagnose --dry-run "cargo test --workspace"
rch exec -- env CARGO_TARGET_DIR=${TMPDIR:-/tmp}/rch_target_<name> cargo check --workspace --all-targetsThen ensure sibling repos exist on workers under canonical roots and retry.
---
Debug Mode
RCH_LOG_LEVEL=debug rch check
RCH_LOG_LEVEL=debug rch diagnose "cargo test --workspace"
RCH_LOG_LEVEL=debug rch exec -- cargo check --workspace --all-targetsProtocol-level hook test:
RCH_LOG_LEVEL=debug printf '%s\n' \
'{"tool_name":"Bash","tool_input":{"command":"cargo check"}}' | rch---
Safe Reset Sequence
rch daemon restart -y
rch config validate
rch config doctor
rch workers probe --all
rch hook status
rch hook test
rch checkIf still failing, capture artifacts for escalation:
rch doctor --json > /tmp/rch-doctor.json
rch --json daemon status > /tmp/rch-daemon-status.json
rch --json workers probe --all > /tmp/rch-workers-probe.jsonWorker Management
Worker Lifecycle
1) Discover and add workers
rch workers discover
rch workers discover --probe
rch workers discover --add --yes2) Complete setup
rch workers setup --allThis performs the standard bootstrap path (binary/toolchain setup, validation) for configured workers.
3) Validate runtime health
rch workers list --speedscore
rch workers probe --all
rch workers capabilities --refresh
rch check---
Add a Worker Manually
Edit ~/.config/rch/workers.toml:
[[workers]]
id = "new-worker"
host = "203.0.113.20"
user = "ubuntu"
identity_file = "~/.ssh/new_worker_ed25519"
total_slots = 16
priority = 90
tags = ["rust", "bun"]Then validate and setup:
rch config validate
rch workers probe new-worker
rch workers setup new-worker---
Drain / Disable / Enable
Use these for maintenance windows and incident isolation.
rch workers drain <worker> -y
rch workers disable <worker> --reason "maintenance" --drain -y
rch workers enable <worker>State model:
HEALTHY: accepting jobsDRAINING: finishing active jobs, no new jobsDRAINED: idle and not accepting jobsDISABLED: explicitly offline from scheduler
---
Toolchain and Binary Management
rch workers sync-toolchain --all
rch workers deploy-binary --allUse --dry-run before broad changes:
rch workers sync-toolchain --all --dry-run
rch workers deploy-binary --all --dry-run---
Fleet-Level Rollout Commands
rch fleet status
rch fleet deploy --verify
rch fleet deploy --canary 25 --canary-wait 60 --verify
rch fleet rollback --verify
rch fleet history --limit 20---
Worker Selection Notes
Selection favors availability and execution quality signals (slot capacity, health, and policy strategy).
Operational guidance:
- Keep
total_slotsrealistic for CPU and memory limits. - Prefer explicit
priorityshaping for known fast/reliable workers. - Drain before disruptive operations.
- Keep worker toolchains synchronized to avoid fallback churn.
---
SSH Verification Shortcuts
Single worker:
rch workers probe <worker>All workers with machine-readable output:
rch --json workers probe --allIf probes fail:
1. Verify identity_file exists and permissions are restrictive. 2. Verify worker host reachability and SSH service. 3. Re-run rch workers setup <worker> after connectivity is restored.
#!/usr/bin/env bash
# RCH diagnostic script (CLI + hook protocol aware)
# Usage: ./diagnose-rch.sh
set -euo pipefail
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
else
RED=''
GREEN=''
YELLOW=''
BLUE=''
NC=''
fi
FAILURES=0
WARNINGS=0
pass() { echo -e "${GREEN}✓${NC} $1"; }
fail() { echo -e "${RED}✗${NC} $1"; FAILURES=$((FAILURES + 1)); }
warn() { echo -e "${YELLOW}⚠${NC} $1"; WARNINGS=$((WARNINGS + 1)); }
info() { echo -e "${BLUE}i${NC} $1"; }
HAS_JQ=0
if command -v jq >/dev/null 2>&1; then
HAS_JQ=1
fi
json_jq() {
local json="$1"
local filter="$2"
if [[ "$HAS_JQ" -eq 1 ]]; then
printf '%s' "$json" | jq -r "$filter // empty" 2>/dev/null || true
fi
}
extract_string() {
local json="$1"
local key="$2"
printf '%s' "$json" | sed -n "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -n1
}
extract_bool() {
local json="$1"
local key="$2"
printf '%s' "$json" | sed -n "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p" | head -n1
}
extract_int() {
local json="$1"
local key="$2"
printf '%s' "$json" | sed -n "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p" | head -n1
}
if ! command -v rch >/dev/null 2>&1; then
echo "rch binary not found in PATH"
echo "Install or expose rch first, then re-run this script."
exit 127
fi
echo "═══════════════════════════════════════"
echo " RCH Diagnostic Report (Current)"
echo "═══════════════════════════════════════"
echo
# 1) quick health
echo "1. Quick Health (rch check)"
echo "───────────────────────────"
check_json="$(rch --json check 2>/dev/null || true)"
if [[ -z "$check_json" ]]; then
fail "No JSON returned from 'rch --json check'"
else
status=""
if [[ "$HAS_JQ" -eq 1 ]]; then
status="$(json_jq "$check_json" '.data.status')"
fi
if [[ -z "$status" ]]; then
status="$(extract_string "$check_json" 'status')"
fi
if [[ "$status" == "ready" ]]; then
pass "RCH status: ready"
elif [[ "$status" == "degraded" ]]; then
warn "RCH status: degraded"
elif [[ -n "$status" ]]; then
fail "RCH status: $status"
else
warn "Could not parse check status"
fi
fi
echo
# 2) daemon status
echo "2. Daemon Status"
echo "────────────────"
daemon_json="$(rch --json daemon status 2>/dev/null || true)"
if [[ -z "$daemon_json" ]]; then
fail "No response from 'rch --json daemon status'"
else
running=""
socket=""
if [[ "$HAS_JQ" -eq 1 ]]; then
running="$(json_jq "$daemon_json" '.data.running')"
socket="$(json_jq "$daemon_json" '.data.socket_path')"
fi
if [[ -z "$running" ]]; then
running="$(extract_bool "$daemon_json" 'running')"
fi
if [[ -z "$socket" ]]; then
socket="$(extract_string "$daemon_json" 'socket_path')"
fi
if [[ "$running" == "true" ]]; then
pass "Daemon running (${socket:-socket unknown})"
elif [[ "$running" == "false" ]]; then
fail "Daemon not running"
info "Fix: rch daemon start"
else
warn "Could not determine daemon running state"
fi
fi
echo
# 3) socket consistency
echo "3. Socket Consistency"
echo "─────────────────────"
config_socket_json="$(rch --json config get general.socket_path 2>/dev/null || true)"
config_socket=""
daemon_socket=""
if [[ -n "$config_socket_json" ]]; then
if [[ "$HAS_JQ" -eq 1 ]]; then
config_socket="$(json_jq "$config_socket_json" '.data.value')"
fi
if [[ -z "$config_socket" ]]; then
config_socket="$(extract_string "$config_socket_json" 'value')"
fi
fi
if [[ -n "$daemon_json" ]]; then
if [[ "$HAS_JQ" -eq 1 ]]; then
daemon_socket="$(json_jq "$daemon_json" '.data.socket_path')"
fi
if [[ -z "$daemon_socket" ]]; then
daemon_socket="$(extract_string "$daemon_json" 'socket_path')"
fi
fi
if [[ -n "$config_socket" && -n "$daemon_socket" ]]; then
if [[ "$config_socket" == "$daemon_socket" ]]; then
pass "Config socket matches daemon socket ($config_socket)"
else
fail "Socket mismatch: config=$config_socket daemon=$daemon_socket"
info "Fix: align socket path then run 'rch daemon restart -y'"
fi
else
warn "Could not fully verify socket consistency"
fi
echo
# 4) workers
echo "4. Worker Fleet"
echo "───────────────"
workers_json="$(rch --json workers list 2>/dev/null || true)"
worker_count=""
if [[ -n "$workers_json" ]]; then
if [[ "$HAS_JQ" -eq 1 ]]; then
worker_count="$(json_jq "$workers_json" '.data.count')"
fi
if [[ -z "$worker_count" ]]; then
worker_count="$(extract_int "$workers_json" 'count')"
fi
fi
if [[ -z "$worker_count" ]]; then
warn "Could not parse configured worker count"
worker_count=0
fi
if [[ "$worker_count" -gt 0 ]]; then
pass "Configured workers: $worker_count"
probe_json="$(rch --json workers probe --all 2>/dev/null || true)"
if [[ -z "$probe_json" ]]; then
fail "No response from 'rch --json workers probe --all'"
else
ok_count=0
bad_count=0
if [[ "$HAS_JQ" -eq 1 ]]; then
ok_count="$(printf '%s' "$probe_json" | jq -r '[.data[] | select(.status == "ok")] | length' 2>/dev/null || echo 0)"
bad_count="$(printf '%s' "$probe_json" | jq -r '[.data[] | select(.status != "ok")] | length' 2>/dev/null || echo 0)"
else
ok_count="$(printf '%s' "$probe_json" | grep -c '"status"[[:space:]]*:[[:space:]]*"ok"' || true)"
bad_count="$(printf '%s' "$probe_json" | grep -Ec '"status"[[:space:]]*:[[:space:]]*"(fail|error|timeout|unreachable|down)"' || true)"
fi
if [[ "$ok_count" -eq "$worker_count" ]]; then
pass "All workers probe successfully"
elif [[ "$ok_count" -gt 0 ]]; then
warn "Partial worker health: ${ok_count}/${worker_count} reachable"
else
fail "No workers are currently reachable"
fi
if [[ "$bad_count" -gt 0 ]]; then
warn "Workers with non-ok probe status: $bad_count"
fi
fi
else
fail "No workers configured"
info "Fix: rch workers discover --add --yes && rch workers setup --all"
fi
echo
# 5) hook install
echo "5. Hook Installation"
echo "────────────────────"
hook_json="$(rch --json hook status 2>/dev/null || true)"
if [[ -z "$hook_json" ]]; then
warn "Could not retrieve hook status"
else
claude_status=""
if [[ "$HAS_JQ" -eq 1 ]]; then
claude_status="$(printf '%s' "$hook_json" | jq -r '.data.agents[]? | select(.agent == "ClaudeCode") | .status' 2>/dev/null | head -n1)"
fi
if [[ -z "$claude_status" ]]; then
if printf '%s' "$hook_json" | grep -q '"agent"[[:space:]]*:[[:space:]]*"ClaudeCode"' && \
printf '%s' "$hook_json" | grep -q '"status"[[:space:]]*:[[:space:]]*"Installed"'; then
claude_status="Installed"
fi
fi
if [[ "$claude_status" == "Installed" ]]; then
pass "Claude Code hook is installed"
else
fail "Claude Code hook is not installed"
info "Fix: rch hook install"
fi
fi
echo
# 6) protocol rewrite test
echo "6. Hook Protocol Rewrite Test"
echo "─────────────────────────────"
hook_input='{"tool_name":"Bash","tool_input":{"command":"cargo build --release"}}'
hook_output="$(printf '%s\n' "$hook_input" | rch 2>/dev/null || true)"
if [[ -z "$hook_output" ]]; then
warn "Hook returned empty stdout (allow unchanged/local)"
elif printf '%s' "$hook_output" | grep -q '"updatedInput"' && \
printf '%s' "$hook_output" | grep -q 'rch exec --'; then
pass "Hook returns allow-with-modified-command (rch exec delegation)"
elif printf '%s' "$hook_output" | grep -q '"permissionDecision"[[:space:]]*:[[:space:]]*"deny"'; then
warn "Hook returned deny decision; inspect policy/logs"
else
warn "Hook returned unexpected protocol payload"
fi
echo
echo "═══════════════════════════════════════"
echo "Summary: ${FAILURES} failure(s), ${WARNINGS} warning(s)"
echo "═══════════════════════════════════════"
if [[ "$FAILURES" -gt 0 ]]; then
echo
echo "Suggested next commands:"
echo " rch doctor"
echo " rch config doctor"
echo " rch diagnose \"cargo build --release\""
echo " rch daemon logs -n 200"
exit 1
fi
exit 0
#!/usr/bin/env bash
# RCH Setup Validation Script
# Checks that RCH is properly configured and ready to use
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
ERRORS=0
WARNINGS=0
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; ((ERRORS++)); }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; ((WARNINGS++)); }
info() { echo -e " $1"; }
echo "RCH Setup Validation"
echo "===================="
echo
# 1. Check prerequisites
echo "Prerequisites:"
if command -v rch &>/dev/null; then
pass "rch binary found: $(which rch)"
else
fail "rch binary not found in PATH"
fi
if command -v rsync &>/dev/null; then
pass "rsync installed"
else
fail "rsync not installed"
fi
if command -v zstd &>/dev/null; then
pass "zstd installed"
else
fail "zstd not installed"
fi
if [ -n "${SSH_AUTH_SOCK:-}" ]; then
pass "ssh-agent running"
else
warn "ssh-agent not running (may need: eval \$(ssh-agent) && ssh-add)"
fi
echo
# 2. Check configuration
echo "Configuration:"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/rch"
WORKERS_FILE="$CONFIG_DIR/workers.toml"
if [ -d "$CONFIG_DIR" ]; then
pass "Config directory exists: $CONFIG_DIR"
else
fail "Config directory missing: $CONFIG_DIR"
fi
if [ -f "$WORKERS_FILE" ]; then
pass "Workers config exists: $WORKERS_FILE"
# Count workers
WORKER_COUNT=$(grep -c '^\[\[workers\]\]' "$WORKERS_FILE" 2>/dev/null || echo 0)
if [ "$WORKER_COUNT" -gt 0 ]; then
pass "Found $WORKER_COUNT worker(s) configured"
else
fail "No workers defined in $WORKERS_FILE"
fi
else
fail "Workers config missing: $WORKERS_FILE"
fi
echo
# 3. Check daemon
echo "Daemon:"
SOCKET_PATH="/tmp/rch.sock"
if [ -S "$SOCKET_PATH" ]; then
pass "Daemon socket exists: $SOCKET_PATH"
else
warn "Daemon socket not found (rchd may not be running)"
fi
if pgrep -x rchd &>/dev/null; then
pass "rchd process running"
else
warn "rchd not running (start with: rchd &)"
fi
echo
# 4. Check Claude Code hook
echo "Claude Code Hook:"
SETTINGS_FILE="$HOME/.claude/settings.json"
if [ -f "$SETTINGS_FILE" ]; then
if grep -q "PreToolUse" "$SETTINGS_FILE" 2>/dev/null; then
if grep -q "rch" "$SETTINGS_FILE" 2>/dev/null; then
pass "RCH hook registered in Claude Code"
else
fail "PreToolUse exists but RCH not configured"
fi
else
fail "No PreToolUse hook configured"
fi
else
fail "Claude Code settings not found: $SETTINGS_FILE"
fi
echo
# 5. Summary
echo "===================="
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
echo -e "${GREEN}All checks passed! RCH is ready.${NC}"
exit 0
elif [ $ERRORS -eq 0 ]; then
echo -e "${YELLOW}$WARNINGS warning(s), no errors. RCH may work with limitations.${NC}"
exit 0
else
echo -e "${RED}$ERRORS error(s), $WARNINGS warning(s). Run 'rch doctor --fix' to resolve.${NC}"
exit 1
fi