Skip to content

Claude/cpu guard script dn d8u - #127

Merged
gamblecodezcom merged 15 commits into
mainfrom
claude/cpu-guard-script-DnD8u
Mar 3, 2026
Merged

Claude/cpu guard script dn d8u#127
gamblecodezcom merged 15 commits into
mainfrom
claude/cpu-guard-script-DnD8u

Conversation

@gamblecodezcom

@gamblecodezcom gamblecodezcom commented Mar 3, 2026

Copy link
Copy Markdown
Owner

User description

Summary by Sourcery

Improve robustness of service startup, logging, and webhook handling for the runewager backend.

Enhancements:

  • Add a helper to safely escape project directory paths in regexes used for process cleanup, preventing pkill pattern issues with special characters.
  • Allow the systemd service to write to the qa directory in addition to logs and data, and fall back to /tmp if the configured endpoint log directory is not writable.
  • Harden backend logging by only writing to healthy streams and guarding writes with error handling to avoid crashes.
  • Adjust the autofix webhook to treat missing rawBody as a client error with clearer logging and a 400 response to discourage infinite retries.

Build:

  • Update systemd unit configuration to expand writable paths for the service.

CodeAnt-AI Description

Improve service startup resilience, backend logging safety, and webhook error responses

What Changed

  • Backend logging now skips closed/unwritable streams and catches stream write errors so logging failures no longer crash the process; logs fall back to console when no healthy stream exists.
  • Webhook endpoint returns 400 and logs a clear message when the raw request body is missing, preventing senders from retrying indefinitely.
  • Startup and service management scripts:
    • Ensure endpoint log files fall back to /tmp if the configured log directory is not writable so the service can start.
    • Kill any process holding the endpoint port before start using a safer SIGTERM then SIGKILL escalation to avoid immediate crash-restart loops.
    • Use a safely escaped project path when searching for stale backend processes to avoid incorrect process matches.
    • Permit the service to write to the repository's qa directory so that legitimate writes aren't blocked.

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:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

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:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

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.

claude added 14 commits March 3, 2026 20:23
… 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

codeant-ai Bot commented Mar 3, 2026

Copy link
Copy Markdown

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 ·
Reddit ·
LinkedIn

@sourcery-ai

sourcery-ai Bot commented Mar 3, 2026

Copy link
Copy Markdown

Reviewer's Guide

Shell 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 handling

sequenceDiagram
    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
Loading

Class diagram for updated logger and webhook handler behavior

classDiagram
    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
Loading

File-Level Changes

Change Details Files
Harden process matching and log directory handling in prod-run.sh for endpoint restarts.
  • Add esc_regex_str helper to escape arbitrary paths for safe use in ERE/BRE regular expressions.
  • Use escaped project directory when constructing pkill patterns targeting backend.js processes, both with and without systemd.
  • Expand systemd ReadWritePaths to include the qa directory so services can write there.
  • Make endpoint log file creation fall back to /tmp when the configured log directory is not writable, emitting a warning.
prod-run.sh
runewager-endpoint.service
runewager.service
Make backend logging and webhook error handling more robust in backend.js.
  • Filter logger output streams to those that are non-null, not destroyed, and writable before writing.
  • Wrap writes to each stream in try/catch and log write errors to stderr instead of allowing them to throw.
  • Treat missing rawBody in the autofix webhook as a client-visible 400 error while still logging it as a server-side misconfiguration, adjusting the error message to clarify middleware expectations.
backend.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@gamblecodezcom has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between d5aedf5 and 639145d.

📒 Files selected for processing (1)
  • prod-run.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/cpu-guard-script-DnD8u

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai Bot added the size:S This PR changes 10-29 lines, ignoring generated files label Mar 3, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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
@gamblecodezcom
gamblecodezcom merged commit 4925afb into main Mar 3, 2026
6 checks passed
@gamblecodezcom
gamblecodezcom deleted the claude/cpu-guard-script-DnD8u branch March 3, 2026 22:01
@codeant-ai

codeant-ai Bot commented Mar 3, 2026

Copy link
Copy Markdown

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • ExecStartPre complexity
    The ExecStartPre command was changed to perform graceful SIGTERM→SIGKILL escalation via a one-liner shell expression. It's long and dense; test quoting and behavior across distributions (lsof vs fuser availability) and ensure logs/echoes don't interfere with systemd unit startup semantics.

  • Logger robustness
    The new logger filters streams and wraps writes in try/catch, but it doesn't check that a stream implements write or handle backpressure (write returning false). This can silently drop logs or mis-handle streams that behave differently (e.g. custom streams). Consider guarding s.write and handling drain/backpressure.

  • ReadWritePaths addition
    ReadWritePaths now includes the repo qa directory in addition to logs/data. Confirm systemd version supports ReadWritePaths and that granting write access to 'qa' is intentional (wider filesystem write allowance).

  • Webhook response semantics
    The autofix webhook now logs missing rawBody as a server misconfiguration but returns HTTP 400 to discourage retries. Confirm callers and retries will behave correctly; returning 400 may hide an internal server setup issue from operators and could make debugging harder.

  • Endpoint log fallback
    The script falls back to /tmp when it cannot touch endpoint log files. Confirm /tmp is acceptable for long-running logs and that permissions/cleanup are acceptable; using touch to detect writability also creates files and may hide directory-level permission issues.

@codeant-ai

codeant-ai Bot commented Mar 3, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants