Claude/cpu guard script dn d8u - #127
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
…touch
- Add esc_regex_str() helper that escapes all non-alphanumeric/slash
chars via sed; replaces fragile [.[\*^+?{|] character class that
missed ] and - and mis-ordered chars after ]
- Compute _esc_dir once before the systemctl block instead of
duplicating the inline expression in both pkill branches
- Add 2>/dev/null to fallback touch in /tmp so locked-down envs
(chroot, ProtectSystem=strict) don't emit spurious stderr under set -e
- Add ${PROJECT_DIR}/qa to ReadWritePaths in ensure_systemd_service
(mirrors same fix already applied to runewager.service)
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 GuideShell script and backend robustness improvements: safer regex escaping for pkill, expanded writable paths and log directory fallback in prod-run.sh, plus more defensive logging stream handling and webhook error semantics in backend.js. Sequence diagram for the updated autofix webhook handlingsequenceDiagram
actor Sender
participant ExpressApp
participant AutofixWebhookHandler
participant Logger
participant SignatureVerifier
Sender->>ExpressApp: POST /autofix/webhook (raw body)
ExpressApp->>AutofixWebhookHandler: Invoke handler with req, res
AutofixWebhookHandler->>AutofixWebhookHandler: Read req.rawBody and signature header
AutofixWebhookHandler->>AutofixWebhookHandler: Validate rawBody is Buffer
alt rawBody missing or not Buffer
AutofixWebhookHandler->>Logger: error(scope: autofix.webhook, message: Missing rawBody — raw-body middleware did not run, meta: { requestId })
AutofixWebhookHandler-->>Sender: 400 Bad Request (missing body)
else rawBody present
AutofixWebhookHandler->>SignatureVerifier: verifySignature(rawBody, sig)
alt Signature invalid or AUTOFIX_SECRET missing
AutofixWebhookHandler->>Logger: warn(scope: autofix.webhook, message: Invalid signature, meta: { requestId })
AutofixWebhookHandler-->>Sender: 401 Unauthorized or 403 Forbidden
else Signature valid
AutofixWebhookHandler->>Logger: info(scope: autofix.webhook, message: Accepted payload, meta: { requestId })
AutofixWebhookHandler-->>Sender: 200 OK
end
end
Class diagram for updated logger and webhook handler behaviorclassDiagram
class Logger {
+info(scope, message, meta)
+warn(scope, message, meta)
+error(scope, message, meta)
+_write(streams, obj)
}
class WritableStream {
+boolean destroyed
+boolean writable
+write(line)
}
class AutofixWebhookHandler {
+postAutofixWebhook(req, res)
}
class Request {
+rawBody
+headers
+requestId
}
class Response {
+status(code)
+json(body)
}
class SignatureVerifier {
+verifySignature(rawBody, signature)
}
Logger "*" o-- "*" WritableStream : writes_to
AutofixWebhookHandler --> Logger : logs_via
AutofixWebhookHandler --> Request : reads
AutofixWebhookHandler --> Response : writes
AutofixWebhookHandler --> SignatureVerifier : uses
%% Logger._write behavior details
class Logger_WriteBehavior {
+filterWritable(streams) streams
+handleWriteError(error)
+fallbackToConsole(line)
}
Logger ..> Logger_WriteBehavior : delegates
class Logger_WriteBehaviorNotes {
+filters streams where destroyed == false and writable == true
+wraps stream.write(line) in try/catch
+logs stream write error to console.error
+logs to console.log when no writable streams
}
Logger_WriteBehavior .. Logger_WriteBehaviorNotes
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
✨ 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 |
Keep our improvements: - fallback touch with 2>/dev/null (silences errors in locked-down envs) - _esc_dir computed once via esc_regex_str() helper (robust escaping) - inline fragile sed patterns from main dropped in favour of helper https://claude.ai/code/session_01WpEqAiDbpqnBywMjYXJHf4
Nitpicks 🔍
|
|
CodeAnt AI finished reviewing your PR. |
User description
Summary by Sourcery
Improve robustness of service startup, logging, and webhook handling for the runewager backend.
Enhancements:
Build:
CodeAnt-AI Description
Improve service startup resilience, backend logging safety, and webhook error responses
What Changed
Impact
✅ Fewer service crash-restarts due to logging failures✅ Fewer webhook retry loops from missing-body errors✅ More reliable service startup when logs or ports are conflicted💡 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.