Skip to content

feat: integrate reliakit (Secret + PositiveInt) in RPC auth, rate-limit, exporter#772

Merged
github-actions[bot] merged 2 commits into
mainfrom
feat/reliakit-deep-integration
Jun 3, 2026
Merged

feat: integrate reliakit (Secret + PositiveInt) in RPC auth, rate-limit, exporter#772
github-actions[bot] merged 2 commits into
mainfrom
feat/reliakit-deep-integration

Conversation

@satyakwok
Copy link
Copy Markdown
Collaborator

@satyakwok satyakwok commented Jun 3, 2026

Deeper integration of the reliakit crates (crates.io) into non-consensus boundaries, after verifying each candidate against real code rather than the audit summary.

Shipped

reliakit-secret::SecretString — API key (routes/auth.rs)
SENTRIX_API_KEY is now held in SecretString. Debug/Display prints [REDACTED]; .expose_secret() is the only read path (used once, at the constant-time compare). Closes the accidental-key-in-logs class.

reliakit-primitives::PositiveInt — rate-limit env (routes/ratelimit.rs)
SENTRIX_GLOBAL_RATE_LIMIT=0 / SENTRIX_WRITE_RATE_LIMIT=0 previously capped the limiter at zero requests, silently locking every client out (self-DoS). 0/unparseable now falls back to the default. 4 unit tests cover the guard.

reliakit-primitives::PositiveInt — probe interval (prom-exporter)
SENTRIX_PROBE_INTERVAL_SEC=0 turned poll_loop into a busy-spin (from_secs(0) = no sleep), pinning a core and hammering every target. 0 now falls back to 15s.

Deliberately rejected (verified against real code)

  • BoundedStr for token name/symbol (block_executor.rs): BoundedStr measures chars().count(); the consensus rule uses byte length (name.len()) plus a different empty-check. Swapping it changes Pass-1 block-validity → chain fork. Consensus rule changes need a fork gate; left untouched.
  • BoundedVec for RPC batch (jsonrpc/mod.rs): the current code checks arr.len() on the cheap Value::Array before the typed deserialize. Wrapping in BoundedVec<JsonRpcRequest> would force the expensive deserialize first — a regression. Current code is already optimal.

Test plan

  • cargo check --workspace -D warnings clean
  • cargo clippy -p sentrix-rpc -p sentrix-prom-exporter --all-targets -D warnings clean
  • 4 new ratelimit tests pass (0→default, unparseable→default, valid→override, unset→default)

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation for startup probe interval and rate-limit configuration; invalid or zero inputs now fall back to safe defaults.
  • Security

    • Enhanced API key handling to prevent accidental exposure in logs or error messages.

@github-actions github-actions Bot enabled auto-merge (squash) June 3, 2026 06:49
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Warning

Review limit reached

@satyakwok, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 52 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 74998f1d-7d8e-4a3e-9914-37fd5f06ff9a

📥 Commits

Reviewing files that changed from the base of the PR and between 6b9406f and 682251e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (5)
  • crates/sentrix-prom-exporter/Cargo.toml
  • crates/sentrix-prom-exporter/src/main.rs
  • crates/sentrix-rpc/Cargo.toml
  • crates/sentrix-rpc/src/routes/auth.rs
  • crates/sentrix-rpc/src/routes/ratelimit.rs
📝 Walkthrough

Walkthrough

This PR introduces input validation and secret safety improvements across the sentrix codebase. Two new dependencies—reliakit-primitives (for PositiveInt validation) and reliakit-secret (for SecretString wrapping)—are added to enable type-safe configuration parsing. The SENTRIX_PROBE_INTERVAL_SEC environment variable is validated to reject zero and invalid values, falling back to 15 seconds. The API key configuration is wrapped in SecretString to prevent accidental logging or display. Rate limit configurations (global and write) are validated using a shared helper that rejects zero and unparseable inputs, with comprehensive unit tests ensuring correctness across edge cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description provides comprehensive context about what was changed, why, and deliberate rejections with technical reasoning, but does not follow the repository's required PR template structure (Summary, Scope, Checks, Linked issue, Deploy impact sections). Restructure the description to match the template: add Summary, Scope checkboxes, Checks section with build/test/lint status, linked issue, and Deploy impact assessment.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: integrating reliakit crates (Secret and PositiveInt) across three modules (RPC auth, rate-limit, exporter).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/reliakit-deep-integration

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/sentrix-prom-exporter/src/main.rs (1)

345-385: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Serialize the env-mutating test (or make parsing pure)

parse_targets() reads SENTRIX_TARGETS via std::env::var, and parse_targets_all_scenarios mutates/removes SENTRIX_TARGETS inside an unsafe block. Even though no other tests in crates/sentrix-prom-exporter/src/main.rs touch SENTRIX_TARGETS, the unsafe contract for std::env::set_var/remove_var still requires that no other thread may access the process environment concurrently—current “single-test scope” doesn’t enforce that under parallel test execution. Extract a pure parse_targets_from_str(&str) and test that, or guard env mutation with a global mutex / run that test serially.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sentrix-prom-exporter/src/main.rs` around lines 345 - 385, The test
parse_targets_all_scenarios mutates the process environment unsafely by calling
std::env::set_var/remove_var around parse_targets(), so fix by making parsing
pure: extract a new function parse_targets_from_str(&str) that contains the
parsing logic currently in parse_targets(), have parse_targets() call
std::env::var and forward to parse_targets_from_str, and update
parse_targets_all_scenarios to call parse_targets_from_str with literal strings
for each scenario (or alternatively, if you must keep env mutation, serialize
the test by acquiring a global Mutex/lock (e.g., a
once_cell::sync::Lazy<Mutex<()>>) in the test before set_var/remove_var to
prevent concurrent env access); update references to parse_targets() in tests to
use parse_targets_from_str or wrap env changes with the mutex accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/sentrix-rpc/src/routes/ratelimit.rs`:
- Around line 208-244: The tests mutate process-wide env unsafely; instead
extract the pure parsing/validation into a new function (e.g.,
rate_limit_from_str) which accepts an Option<&str> or &str and returns the
numeric rate or default so you can unit-test parsing logic without touching
std::env, then update rate_limit_from_env to call this helper; if you must keep
env-based tests, wrap them with a test-level serialization mechanism (a global
Mutex or the serial_test crate) so tests that call rate_limit_from_env do not
run in parallel and remove all unsafe std::env::set_var/remove_var calls inside
tests.

---

Outside diff comments:
In `@crates/sentrix-prom-exporter/src/main.rs`:
- Around line 345-385: The test parse_targets_all_scenarios mutates the process
environment unsafely by calling std::env::set_var/remove_var around
parse_targets(), so fix by making parsing pure: extract a new function
parse_targets_from_str(&str) that contains the parsing logic currently in
parse_targets(), have parse_targets() call std::env::var and forward to
parse_targets_from_str, and update parse_targets_all_scenarios to call
parse_targets_from_str with literal strings for each scenario (or alternatively,
if you must keep env mutation, serialize the test by acquiring a global
Mutex/lock (e.g., a once_cell::sync::Lazy<Mutex<()>>) in the test before
set_var/remove_var to prevent concurrent env access); update references to
parse_targets() in tests to use parse_targets_from_str or wrap env changes with
the mutex accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1e2a9307-c081-4998-9a8f-c04818b9bedc

📥 Commits

Reviewing files that changed from the base of the PR and between a3e0d7a and 6b9406f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (5)
  • crates/sentrix-prom-exporter/Cargo.toml
  • crates/sentrix-prom-exporter/src/main.rs
  • crates/sentrix-rpc/Cargo.toml
  • crates/sentrix-rpc/src/routes/auth.rs
  • crates/sentrix-rpc/src/routes/ratelimit.rs

Comment thread crates/sentrix-rpc/src/routes/ratelimit.rs
Three genuine type-safety wins from the reliakit crates (crates.io):

- auth.rs: SENTRIX_API_KEY now wrapped in reliakit_secret::SecretString.
  Accidental Debug/Display of the key prints [REDACTED]; .expose_secret()
  is the only read path, so every access is auditable. Closes the
  accidental-key-leak-in-logs class.

- ratelimit.rs: rate-limit env overrides parsed through PositiveInt.
  SENTRIX_GLOBAL_RATE_LIMIT=0 / SENTRIX_WRITE_RATE_LIMIT=0 previously
  capped the limiter at zero, silently locking every client out of the
  endpoint (self-DoS). 0/unparseable now falls back to the default.
  4 unit tests cover the guard.

- prom-exporter: SENTRIX_PROBE_INTERVAL_SEC parsed through PositiveInt.
  A 0 interval turned poll_loop into a busy-spin (from_secs(0) = no sleep)
  pinning a core and hammering every target. 0 now falls back to 15s.

Deliberately NOT applied to consensus paths: BoundedStr uses chars().count()
while the token-name/symbol validation in block_executor uses byte length
(name.len()) plus a different empty-check — swapping it would change Pass-1
block-validity rules and fork the chain. Left untouched.
@satyakwok satyakwok force-pushed the feat/reliakit-deep-integration branch from 6b9406f to ec3b96a Compare June 3, 2026 06:58
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

CodeRabbit flagged the unit tests using unsafe std::env::set_var/remove_var.
The finding is valid: under edition 2024 these are unsafe because the race
is on the process-wide environment table, not key names — unique var names
don't make parallel tests sound (another thread reading any env var, or
stdlib doing a DNS lookup, can race the write → UB).

Fix: extract pure rate_limit_from_str(Option<&str>, default) -> u32 and test
that directly. rate_limit_from_env now just reads the env and delegates.
Removes all unsafe from the tests; adds a clamp-not-truncate test for the
u64 → u32 path.
@github-actions github-actions Bot merged commit 8dc4f43 into main Jun 3, 2026
17 checks passed
@satyakwok satyakwok deleted the feat/reliakit-deep-integration branch June 3, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant