Claude/cpu guard script dn d8u - #126
Conversation
… main
Combines all changes from this PR into a single clean commit:
- backend.js: store raw buffer on req.rawBody before express.json() runs
so HMAC verification uses the original bytes (not JSON.stringify output);
guard log stream creation in try-catch with .on('error') fallback to
console so filesystem failures never crash the process
- package.json: broaden engine range from >=20 <22 to >=20 (Node v24 compat)
- package-lock.json: regenerate with express@4.x and all transitive deps
(was missing, causing npm ci to fail on VPS)
- runewager-endpoint.service: User/Group → root (VPS runs as root)
- runewager.service: User/Group → root (VPS runs as root)
https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
- Pin express to 4.22.1 (remove ^ semver range) to match the explicitly installed version and prevent future version drift that caused the missing-dependency failure on Node 21. - Add ExecStartPre to runewager-endpoint.service: kills any stale process holding port 3001 before each systemd start, eliminating the EADDRINUSE → instant-crash → restart loop. Also tightened StartLimitBurst=4 / RestartSec=10 / StartLimitIntervalSec=120 to reduce CPU spike if the service still fails repeatedly. - Add section 9d to prod-run.sh: installs/refreshes the endpoint service unit from the repo copy, clears port 3001 before restart, restarts via systemd with a nohup fallback — so a full prod-run now brings both services up cleanly without manual intervention. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
Monitoring-only companion to rw_cpu_guard.sh. Generates timestamped Markdown reports under /root/CPU_LOGS for /var/www/html/gcz and /var/www/html/Runewager stacks, rotates to keep last 10 reports, and symlinks the latest as CPU_report.md. Never kills, renice-s, or restarts any process — purely observational. Coexists safely with rw_cpu_guard.sh (separate log path, no shared state). Features: - System-wide CPU summary via top - High-CPU process table (>85% threshold) - GCZ stack process listing with⚠️ flag on hot processes - Best-effort dpkg package attribution for flagged pids - --status, --install-cron, --dry-run modes - Self-exclusion and kernel-thread exclusion from reports https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
…de processes `pkill -f "node .*backend.js"` was too broad — on a shared host it could kill any Node process whose cmdline contains "backend.js". Narrowed both pkill calls (systemd fallback + no-systemd path) to `node .*<PROJECT_DIR>/backend.js` so only the Runewager endpoint process is targeted. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
gcz_cpu_guard.sh lives on the VPS directly, not in this repo. Added "gcz_cpu_guard" to WHITELIST_PATTERNS in rw_cpu_guard.sh so the Runewager CPU governor never renice/kills/stops the GCZ monitoring script regardless of its CPU usage. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
Both static service files were hardcoding User=root/Group=root, meaning any RCE in index.js or backend.js yielded full system control and made ProtectSystem/NoNewPrivileges sandboxing meaningless. Changes: - runewager.service: User/Group → runewager (prod-run.sh already creates this system user and chowns logs/ + data/ to it) - runewager-endpoint.service: User/Group → runewager; ExecStartPre prefixed with '+' so systemd runs only that port-clear step as root (needed to kill stale root-owned processes), while backend.js itself runs unprivileged https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
prod-run.sh: kept scoped pkill pattern (node .*PROJECT_DIR/backend.js) over the broad main version (node .*backend.js) — prevents killing unrelated node processes on shared hosts. runewager-endpoint.service: kept our version with ExecStartPre '+' prefix (root-exec for port-clear step) and User/Group=runewager over main's root user with unprefixed ExecStartPre. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
backend.js — _write double-logging: Filter to writable streams first; fall back to a single console.log only when none are available. Previously logger.error() with two null streams would print the same line twice to stdout. backend.js — rawBody guard on webhook: Treat missing/non-Buffer req.rawBody as a 500 (middleware misconfiguration) rather than silently falling back to Buffer.alloc(0), which was indistinguishable from a genuine signature mismatch. prod-run.sh — port race in nohup fallback: After a failed systemctl restart, re-check and free ENDPOINT_PORT before launching the nohup fallback. The original cleanup ran before the systemd attempt, leaving a race window where another process could bind the port. Note: log paths are NOT a conflict — prod-run.sh LOG_DIR resolves to $PROJECT_DIR/logs, same as backend.js path.join(__dirname, 'logs'). https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
…rds, log fallback) runewager-endpoint.service — aggressive prestart kill: Replace direct SIGKILL with SIGTERM → 3s wait → SIGKILL escalation. Gives the port holder a chance to shut down gracefully before forcing. backend.js — stream write guards: Filter streams by !destroyed && writable before writing, and wrap each write in try/catch so a mid-flight stream error is logged to stderr rather than crashing or silently corrupting output. prod-run.sh — silent log creation: Replace `touch ... 2>/dev/null || true` with an explicit failure check; when the log directory is not writable, warn and fall back to /tmp so the nohup backend process always has somewhere to log. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
…scalation backend.js: kept writable/destroyed stream guard + try/catch over bare filter(Boolean) runewager-endpoint.service: kept SIGTERM→3s→SIGKILL escalation over direct kill -9 https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
prod-run.sh — pkill regex safety: Escape PROJECT_DIR with sed before interpolating into the pkill -f pattern. Spaces or regex metacharacters in the path (e.g. dots in /var/www) can cause unexpected matches or silent pattern failure. backend.js — rawBody missing response code: Change 500 → 400 when req.rawBody is absent. 500 caused webhook senders to treat the rejection as transient and retry; 400 signals a permanent bad-request so retries stop. The server-side misconfiguration is still logged as an error for visibility. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
fuser without -t outputs "3001/tcp: 1234" — the full string was being passed to kill, causing it to fail silently and leaving the port holder alive (EADDRINUSE crash loop when lsof is unavailable). fuser -t outputs only the bare PID, matching lsof -t behaviour. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
…fo write ProtectSystem=strict makes the entire FS read-only; only explicitly listed paths are writable. qa/context/repo_info.json write on startup was hitting EROFS because qa/ was not in ReadWritePaths. https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
|
CodeAnt AI is reviewing your PR. Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
Reviewer's guide (collapsed on small PRs)Reviewer's GuideImproves robustness of endpoint startup and logging by making log file creation more fault-tolerant, making process-kill patterns safe for special characters in PROJECT_DIR, hardening backend logger stream writes, and returning a 400 instead of 500 when the autofix webhook body middleware is misconfigured so senders don’t retry indefinitely. Sequence diagram for updated autofix webhook error handlingsequenceDiagram
actor AutofixSender
participant Backend
participant Logger
AutofixSender->>Backend: POST /autofix/webhook (payload, signature)
Backend->>Backend: Extract req.rawBody
alt rawBody missing or not Buffer
Backend->>Logger: error(autofix.webhook, Missing rawBody — raw-body middleware did not run, { requestId })
Backend-->>AutofixSender: 400 Bad Request (error: missing body)
else rawBody present and signature valid
Backend->>Backend: Process webhook normally
Backend-->>AutofixSender: 200 OK
else rawBody present but signature invalid
Backend-->>AutofixSender: 401 Unauthorized
end
Class diagram for updated backend logger stream handlingclassDiagram
class Logger {
+info(scope, message, meta)
+warn(scope, message, meta)
+error(scope, message, meta)
+_write(streams, obj)
}
class WritableStream {
+writable : boolean
+destroyed : boolean
+write(chunk)
}
Logger : filters streams where s && !s.destroyed && s.writable
Logger : iterates writable streams and calls write in try-catch
Logger : falls back to console.log when no writable streams
Logger --> WritableStream : writes log lines to
Flow diagram for prod-run endpoint startup and logging robustnessflowchart TD
A[Start prod-run endpoint setup] --> B[Set ENDPOINT_LOG_DIR and file paths]
B --> C[Try touch ENDPOINT_LOG and ENDPOINT_ERR]
C -->|success| D[Keep configured log directory]
C -->|failure| E[Warn cannot write to ENDPOINT_LOG_DIR]
E --> F[Set ENDPOINT_LOG and ENDPOINT_ERR under /tmp]
F --> G[Touch /tmp log files]
D --> H{systemctl available?}
G --> H
H -->|yes| I[Try systemctl restart runewager-endpoint]
I -->|success| J[Endpoint restarted via systemd]
I -->|failure| K[Warn systemctl restart failed]
K --> L[Escape PROJECT_DIR for regex and run pkill -f node .*_esc_dir/backend.js]
L --> M[Sleep 1 and handle port checks]
H -->|no| N[No systemd: direct nohup]
N --> O[Escape PROJECT_DIR for regex and run pkill -f node .*_esc_dir/backend.js]
O --> P[Sleep 1 and handle port checks]
J --> Q[End]
M --> Q
P --> Q
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR hardens system robustness through improved error handling in backend logging, graceful process termination in service startup, safer path handling in deployment scripts, and expanded service write permissions. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The
_esc_dirsed escaping pattern ([.[\*^$()+?{|]) is fragile (e.g.,]and-aren’t handled correctly and some chars after]aren’t actually in the class); consider centralizing this into a small helper or using a more robust escape function soPROJECT_DIRvalues with regex metacharacters are always safe. - The
_esc_dircomputation is duplicated in the twopkillbranches inprod-run.sh; consider computing it once near wherePROJECT_DIRis set or reusing a helper to keep the logic consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `_esc_dir` sed escaping pattern (`[.[\*^$()+?{|]`) is fragile (e.g., `]` and `-` aren’t handled correctly and some chars after `]` aren’t actually in the class); consider centralizing this into a small helper or using a more robust escape function so `PROJECT_DIR` values with regex metacharacters are always safe.
- The `_esc_dir` computation is duplicated in the two `pkill` branches in `prod-run.sh`; consider computing it once near where `PROJECT_DIR` is set or reusing a helper to keep the logic consistent and easier to maintain.
## Individual Comments
### Comment 1
<location path="prod-run.sh" line_range="405-407" />
<code_context>
ENDPOINT_LOG="$ENDPOINT_LOG_DIR/backend.log"
ENDPOINT_ERR="$ENDPOINT_LOG_DIR/backend-error.log"
-touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" 2>/dev/null || true
+if ! touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" 2>/dev/null; then
+ warn "Cannot write to $ENDPOINT_LOG_DIR — falling back to /tmp for endpoint logs"
+ ENDPOINT_LOG="/tmp/runewager-backend.log"
+ ENDPOINT_ERR="/tmp/runewager-backend-error.log"
+ touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" || true
+fi
</code_context>
<issue_to_address>
**suggestion:** Consider suppressing stderr and/or guarding the fallback touch to avoid noisy failures in /tmp
The fallback `touch` in `/tmp` isn’t redirected, so if `/tmp` is also unwritable (e.g., locked-down envs/chroots), it will start emitting errors to stderr and could behave differently under `set -e`. To preserve the original quiet, best‑effort behavior, consider something like:
```sh
touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" 2>/dev/null || true
```
for the fallback as well.
```suggestion
ENDPOINT_LOG="/tmp/runewager-backend.log"
ENDPOINT_ERR="/tmp/runewager-backend-error.log"
touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" 2>/dev/null || true
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| ENDPOINT_LOG="/tmp/runewager-backend.log" | ||
| ENDPOINT_ERR="/tmp/runewager-backend-error.log" | ||
| touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" || true |
There was a problem hiding this comment.
suggestion: Consider suppressing stderr and/or guarding the fallback touch to avoid noisy failures in /tmp
The fallback touch in /tmp isn’t redirected, so if /tmp is also unwritable (e.g., locked-down envs/chroots), it will start emitting errors to stderr and could behave differently under set -e. To preserve the original quiet, best‑effort behavior, consider something like:
touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" 2>/dev/null || truefor the fallback as well.
| ENDPOINT_LOG="/tmp/runewager-backend.log" | |
| ENDPOINT_ERR="/tmp/runewager-backend-error.log" | |
| touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" || true | |
| ENDPOINT_LOG="/tmp/runewager-backend.log" | |
| ENDPOINT_ERR="/tmp/runewager-backend-error.log" | |
| touch "$ENDPOINT_LOG" "$ENDPOINT_ERR" 2>/dev/null || true |
Nitpicks 🔍
|
|
CodeAnt AI finished reviewing your PR. |
User description
Summary by Sourcery
Improve robustness of endpoint startup, logging, and webhook handling.
Bug Fixes:
Enhancements:
CodeAnt-AI Description
Improve endpoint startup resilience, logging safety, and webhook error responses
What Changed
Impact
✅ Fewer endpoint crash/restart loops✅ Endpoint logs available even when log directory is unwritable✅ Clearer webhook errors and fewer unnecessary retries💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.
Summary by CodeRabbit
Bug Fixes
Improvements