Claude/cpu guard script dn d8u - #124
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
|
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 GuideExtends the deployment script to manage a dedicated runewager-endpoint systemd service, hardens backend logging and webhook signature verification behavior with Express 4.22.1, broadens supported Node versions, and updates the CPU guard whitelist to protect an additional monitoring script. Sequence diagram for /autofix webhook handling with raw body capturesequenceDiagram
actor ExternalService
participant ExpressApp
participant RawBodyMiddleware as /autofix raw body middleware
participant JsonMiddleware as express.json middleware
participant WebhookHandler as /autofix/webhook handler
participant SignatureVerifier
participant Logger
ExternalService->>ExpressApp: POST /autofix/webhook (body, x-autofix-signature)
ExpressApp->>RawBodyMiddleware: Invoke for /autofix
RawBodyMiddleware->>RawBodyMiddleware: express.raw({ type:*/*, limit:1mb })
RawBodyMiddleware->>ExpressApp: Set req.rawBody if Buffer
ExpressApp->>JsonMiddleware: express.json({ limit:1mb })
JsonMiddleware->>ExpressApp: Parse JSON into req.body
ExpressApp->>WebhookHandler: Call handler with req, res
WebhookHandler->>WebhookHandler: Read sig from headers
WebhookHandler->>WebhookHandler: rawBody = req.rawBody or Buffer.alloc(0)
WebhookHandler->>SignatureVerifier: verifySignature(rawBody, sig)
SignatureVerifier-->>WebhookHandler: valid / invalid
alt Signature valid and AUTOFIX_SECRET set
WebhookHandler->>Logger: info(autofix_webhook_received)
WebhookHandler-->>ExternalService: 200 OK
else Signature invalid or secret missing
WebhookHandler->>Logger: warn(autofix_webhook_rejected)
WebhookHandler-->>ExternalService: 401/400 error
end
Class diagram for backend logging and webhook handlingclassDiagram
class Logger {
+info(eventType, msg, extra)
+warn(eventType, msg, extra)
+error(eventType, msg, extra)
+_entry(level, eventType, msg, extra)
+_write(streams, obj)
}
class LogStreamsModule {
<<module>>
-_logStream
-_errStream
+initLogStreams()
}
class BackendServer {
<<module>>
+app
+configureMiddleware()
+handleAutofixRaw(req, res, next)
+handleAutofixWebhook(req, res)
}
class SignatureVerifier {
<<utility>>
+verifySignature(bodyBuffer, signature)
}
LogStreamsModule <.. Logger : uses
BackendServer <.. Logger : logs_with
BackendServer <.. SignatureVerifier : verifies_with
BackendServer : app uses handleAutofixRaw as /autofix middleware
BackendServer : app uses express.json for JSON parsing
BackendServer : handleAutofixRaw stores req.rawBody
BackendServer : handleAutofixWebhook reads req.rawBody
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 (4)
✨ 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 3 issues, and left some high level feedback:
- The new /autofix raw-body middleware still relies on express.raw(), which will mark the request as already parsed and prevent express.json() from running for those routes; if any /autofix handlers expect a JSON-parsed req.body, consider using body-parser’s
verifyoption on express.json() instead, or explicitly re-run JSON parsing after capturing req.rawBody. - prod-run.sh directs the endpoint’s stdout/stderr to
$LOG_DIR/backend.logandbackend-error.log, while backend.js writes tologs/backend.logandbackend-error.logunder its own directory; consider consolidating these paths to avoid having two separate backend log locations with the same filenames.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new /autofix raw-body middleware still relies on express.raw(), which will mark the request as already parsed and prevent express.json() from running for those routes; if any /autofix handlers expect a JSON-parsed req.body, consider using body-parser’s `verify` option on express.json() instead, or explicitly re-run JSON parsing after capturing req.rawBody.
- prod-run.sh directs the endpoint’s stdout/stderr to `$LOG_DIR/backend.log` and `backend-error.log`, while backend.js writes to `logs/backend.log` and `backend-error.log` under its own directory; consider consolidating these paths to avoid having two separate backend log locations with the same filenames.
## Individual Comments
### Comment 1
<location path="backend.js" line_range="41-50" />
<code_context>
+let _logStream = null;
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid logging the same entry multiple times to stdout when multiple streams are null.
In `_write`, this logic means a call like `logger.error(...)` with `[_logStream, _errStream]` will log the same line twice to stdout if both streams are `null`. Consider filtering to writable streams first and only falling back to a single `console.log` when none are available.
Suggested implementation:
```javascript
function _write(streams, line) {
const writableStreams = streams.filter((s) => s);
if (writableStreams.length > 0) {
for (const stream of writableStreams) {
stream.write(line + '\n');
}
} else {
console.log(line);
}
}
```
If the actual `_write` implementation differs (e.g., different parameter names, using `process.stdout.write`, etc.), adjust the `SEARCH` section to match the existing function body, and keep the `REPLACE` logic the same:
1. Ensure `streams` (or equivalent) is filtered to only truthy/writable streams.
2. Write to each available stream without any console fallback inside that loop.
3. Add a single `console.log` (or your existing stdout mechanism) in the `else` branch when no streams are available.
</issue_to_address>
### Comment 2
<location path="backend.js" line_range="204-209" />
<code_context>
try {
const sig = req.headers['x-autofix-signature'] ?? req.headers['x-hub-signature-256'] ?? '';
- const rawBody = Buffer.isBuffer(req.body) ? req.body : Buffer.from(JSON.stringify(req.body ?? {}));
+ const rawBody = req.rawBody ?? Buffer.alloc(0);
if (!AUTOFIX_SECRET || !verifySignature(rawBody, sig)) {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider failing fast if req.rawBody is missing on the webhook route.
Using `Buffer.alloc(0)` means a missing `req.rawBody` will just look like a normal signature mismatch. If this endpoint should always have `rawBody` (because of the raw-body middleware), consider treating its absence as a 5xx (or at least logging it distinctly) so you can distinguish middleware misconfiguration from a genuinely bad signature.
```suggestion
const requestId = req.id;
try {
const sig = req.headers['x-autofix-signature'] ?? req.headers['x-hub-signature-256'] ?? '';
const rawBody = req.rawBody;
if (!rawBody || !Buffer.isBuffer(rawBody)) {
console.error('Missing or invalid rawBody on webhook request', { requestId });
res.status(500).send('Internal server error');
return;
}
if (!AUTOFIX_SECRET || !verifySignature(rawBody, sig)) {
```
</issue_to_address>
### Comment 3
<location path="prod-run.sh" line_range="423-433" />
<code_context>
+ sleep 1
+ fi
+
+ if systemctl restart "${ENDPOINT_SERVICE_NAME}.service" 2>/dev/null; then
+ say "runewager-endpoint restarted via systemd"
+ else
+ warn "systemctl restart runewager-endpoint failed — falling back to nohup"
+ pkill -f "node .*${PROJECT_DIR}/backend\.js" 2>/dev/null || true
+ sleep 1
+ nohup node "$PROJECT_DIR/backend.js" \
+ >> "$ENDPOINT_LOG" 2>> "$ENDPOINT_ERR" < /dev/null &
+ disown || true
</code_context>
<issue_to_address>
**suggestion:** Re-check and free the endpoint port before starting the nohup fallback after a failed systemd restart.
There’s a race window between the initial `ENDPOINT_PORT` cleanup and the failed `systemctl restart`: another process could bind the port in that gap, causing the nohup fallback to fail to bind (or bind differently). Consider running the `is_port_listening`/`free_port_if_conflicted` logic again in the fallback path immediately before `nohup node ...`.
```suggestion
if systemctl restart "${ENDPOINT_SERVICE_NAME}.service" 2>/dev/null; then
say "runewager-endpoint restarted via systemd"
else
warn "systemctl restart runewager-endpoint failed — falling back to nohup"
pkill -f "node .*${PROJECT_DIR}/backend\.js" 2>/dev/null || true
sleep 1
# Re-check and free endpoint port before nohup fallback, to avoid race with other binders
if is_port_listening "$ENDPOINT_PORT"; then
say "Port $ENDPOINT_PORT in use — freeing before nohup fallback..."
free_port_if_conflicted "$ENDPOINT_PORT" "" || true
sleep 1
fi
nohup node "$PROJECT_DIR/backend.js" \
>> "$ENDPOINT_LOG" 2>> "$ENDPOINT_ERR" < /dev/null &
disown || true
say "runewager-endpoint started via nohup fallback"
fi
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
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
Nitpicks 🔍
|
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
|
CodeAnt AI finished reviewing your PR. |
User description
Summary by Sourcery
Add systemd-managed backend endpoint service, harden backend logging and webhook body handling, and update runtime safeguards and dependencies.
New Features:
Bug Fixes:
Enhancements:
Deployment:
CodeAnt-AI Description
Install managed endpoint service; make logging and webhook verification robust
What Changed
Impact
✅ Clearer webhook verification (reduces signature mismatches on /autofix)✅ Fewer backend crashes when log files or directories are unavailable✅ Fewer endpoint startup crash-loops on port 3001💡 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.