fix(cerebro): harden production readiness#687
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 48 minutes and 59 seconds. ⌛ 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. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAdds CI smoke and Rust quality gates, enables TOML serde feature, hardens container defaults and docs, introduces startup validation that requires auth for non-loopback bindings, adds /healthz and /readyz with storage readiness checks, enforces a 1 MiB request body limit, hardens token parsing/comparison, and adds related tests. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Router as Axum Router
participant Server as Cerebro Service
participant Storage as Storage Backend
Client->>Router: GET /healthz
Router->>Server: handle_healthz()
Server-->>Client: 200 {status:"ok"}
Client->>Router: GET /readyz
Router->>Server: handle_readyz()
Server->>Storage: storage.ready()
alt storage healthy
Storage-->>Server: Ok
Server-->>Client: 200 {status:"ready"}
else storage unhealthy
Storage-->>Server: Err(error)
Server-->>Client: 503 {status:"not_ready", error: "<msg>"}
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
✅ Contributor ReportUser: @yacosta738
Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-26 to 2026-04-26 |
Deploying corvus with
|
| Latest commit: |
916d33c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://2b3570bf.corvus-42x.pages.dev |
| Branch Preview URL: | https://cerebro.corvus-42x.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
clients/cerebro/src/server.rs (1)
226-255:⚠️ Potential issue | 🔴 CriticalCritical: token comparisons are not constant-time—timing-attack risk that this PR was supposed to fix.
Lines 246 and 250 use plain
==to compare bearer tokens againstaudit_tokenandexpected.strequality short-circuits on the first differing byte, leaking byte-by-byte token information via response timing. PR objective#676explicitly calls for "secure comparison semantics."Replace both comparisons with constant-time comparison using
subtle::ConstantTimeEq:Fix using subtle crate
Add
subtle = "2"toCargo.toml, then:+use subtle::ConstantTimeEq; fn authorize(&self, auth_header: Option<&str>) -> Result<AuthContext, CerebroError> { // ... let token = parse_bearer_token(auth_header)?; - if audit_token.is_some_and(|audit| token == audit) { + if audit_token.is_some_and(|audit| token.as_bytes().ct_eq(audit.as_bytes()).into()) { return Ok(AuthContext { is_audit: true }); } - if token == expected { + if token.as_bytes().ct_eq(expected.as_bytes()).into() { return Ok(AuthContext { is_audit: false }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/cerebro/src/server.rs` around lines 226 - 255, In authorize(), replace the unsafe short-circuiting == comparisons (token == audit and token == expected) with constant-time comparisons: add subtle = "2" to Cargo.toml, use subtle::ConstantTimeEq, and compare byte slices via token.as_bytes().ct_eq(audit.as_bytes()) and token.as_bytes().ct_eq(expected.as_bytes()), converting the Choice result to a boolean (e.g., .unwrap_u8() == 1) to decide the branch; keep the rest of authorize (parse_bearer_token, audit_token handling, returning AuthContext/CerebroError::Unauthorized) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/pull-request-check.yml:
- Around line 135-139: Update the Cerebro workflow steps to enforce the PR gate:
add a formatting check step that runs cargo fmt --all -- --check against the
Cerebro manifest, add a linting step that runs cargo clippy --all-targets -- -D
warnings against the Cerebro manifest, and make the existing "🦀 Check Cerebro
crate" step include --locked to match "🧪 Test Cerebro crate"; locate and update
the steps named "🦀 Check Cerebro crate" and "🧪 Test Cerebro crate" and insert
new steps (e.g., "🧹 Fmt Cerebro crate" and "🧭 Clippy Cerebro crate") that
reference clients/cerebro/Cargo.toml so format and clippy failures block the PR
as intended.
In `@clients/cerebro/Dockerfile.release-prebuilt`:
- Around line 18-30: The bundled demo config binds the service to loopback and
ships a static password, which causes two problems: (1) document clearly in the
Dockerfile/comment/README that the default host="127.0.0.1" inside the container
prevents access via docker -p and that operators must override host (e.g., to
"0.0.0.0") and supply auth_token to use port mapping; and (2) harden the startup
validator (the function validate_embedded_surreal and the startup auth_token
gate) so when the configured host is non-loopback it rejects known demo
credentials (add a denylist like "local-dev-only",
"CHANGE_ME_BEFORE_PRODUCTION", "root" instead of just checking non-empty) and
fail startup with a clear error message instructing operators to set a secure
password or mount real credentials.
In `@clients/cerebro/README.md`:
- Around line 37-78: Update the docs to (1) replace "a conservative HTTP request
body limit" with the explicit value "1 MiB (1,048,576 bytes)" (this is asserted
in http_limits_test.rs); (2) state explicitly that the service will refuse to
start if you bind to a non-loopback address without an auth_token (reflecting
the startup check in main that requires auth_token for non-loopback bindings);
and (3) reword the POST /mcp line to read that it is "not a substitute for the
probe endpoints above" instead of implying probe usage—adjust the lines
mentioning request limits, production deployment auth_token, and the POST /mcp
probe note accordingly.
In `@clients/cerebro/src/config.rs`:
- Around line 285-290: The validation trims auth_token before checking emptiness
which mismatches tokens loaded verbatim from config files; change the code so
the token is canonicalized at storage time (trim once on deserialization or in
the config's constructor) rather than only on validation—update the
deserialization logic (or the method apply_env_overrides if you prefer a single
normalization point) to trim and store the token into auth_token, ensuring
auth_token, apply_env_overrides, and any places that read auth_token (e.g., the
code computing auth_is_present) all operate on the same canonical trimmed value.
In `@clients/cerebro/src/server.rs`:
- Around line 79-86: Extract the literal body limit into a named constant and
use it in router(): define const MAX_REQUEST_BODY_BYTES: usize = 1024 * 1024 and
replace DefaultBodyLimit::max(1024 * 1024) with
DefaultBodyLimit::max(MAX_REQUEST_BODY_BYTES) in the Arc<Self>::router method so
tests/docs can grep the value; keep the rest of router wiring (routes
handle_health, handle_ready, handle_mcp and with_state(self)) unchanged.
- Around line 295-306: The readiness endpoint currently calls storage().count()
inside handle_ready which triggers an O(rows) table scan; replace that with a
cheap connectivity-ready check by adding an async trait method
Storage::ready(&self) -> Result<(), CerebroError> (with a cheap default no-op),
implement ready for the SurrealDB backend in surreal.rs to perform a bounded
probe (e.g., SELECT 1 / INFO FOR DB or a single-row LIMIT 1 query) and have
in-memory/disk backends return Ok immediately; finally update handle_ready to
call service.storage().ready().await and return not_ready only on error
(optionally add a short TTL cache around ready if you want to avoid frequent
backend probes).
- Around line 128-130: request_id is being set to request.id.to_string(), which
serializes JSON strings with embedded quotes; change the normalization so string
IDs are unwrapped and non-string IDs are stringified. Replace the current
assignment of request_id with logic that checks request.id: if it's a JSON
string use the inner &str (e.g., Value::String or as_str()), otherwise call
to_string() on the Value; update the test in tui_event_emission_tests.rs to
expect "1" instead of "\"1\"". Ensure you modify the symbol request_id
initialization (near where request.params.name and start are set) so tracing and
ToolCallEvent use the unquoted ID.
- Around line 258-270: The parse_bearer_token function currently uses
header.strip_prefix("Bearer ") which enforces a case-sensitive scheme and
rejects RFC 7235-compliant variants; update parse_bearer_token to accept the
"Bearer" scheme case-insensitively and allow HTTP linear whitespace
(tabs/multiple spaces) between scheme and token, while still returning
CerebroError::Unauthorized for missing/empty tokens and preserving strict token
comparison elsewhere; locate and modify the parse_bearer_token implementation to
perform a case-insensitive prefix check for "bearer" (or normalize the scheme to
ASCII lowercase) then trim only the LWS before validating token presence.
- Around line 128-150: The span guard created with let _enter = span.enter()
(variable span and the enter call) is held across the await to
self.tools.handle(...).await and must be removed; instead, avoid keeping an
EnteredSpan alive across await boundaries by using tracing::instrumentation
(e.g., wrap the future with .instrument(span) or annotate the async
block/function with #[tracing::instrument]) so the span is entered/exited on
each poll; specifically, remove or stop using span.enter() where authorize(...)
and the subsequent self.tools.handle(...).await execute and apply
.instrument(span) to the async call that awaits self.tools.handle(...) (or
annotate the surrounding async function) to ensure correct span lifetime without
holding the EnteredSpan across await points.
In `@clients/cerebro/tests/config_load_test.rs`:
- Around line 25-31: Update the test to assert actual secret values instead of
only presence: after calling CerebroConfig::load(Some(&path)), compare
config.auth_token (and config.surreal.password) against the expected literal
test secrets used in the fixture to ensure secret_string_opt::deserialize
preserved content; keep the existing checks for host/port/username but replace
assert!(config.auth_token.is_some()) and
assert!(config.surreal.password.is_some()) with exact equality assertions that
the tokens/passwords match the known test values.
In `@clients/cerebro/tests/health_endpoints_test.rs`:
- Around line 29-49: Add a failing readiness-path test that ensures handle_ready
returns 503 when storage.count() errors: create a small fake Storage
implementation whose count() returns Err(...) (or a test double implementing the
same trait), construct a CerebroService with that fake storage instead of
InMemoryStorage, call the /readyz endpoint via
service.router().oneshot(Request::builder().uri("/readyz")...), and assert the
response.status() is StatusCode::SERVICE_UNAVAILABLE; this locks both branches
of handle_ready and references Storage::count, CerebroService, handle_ready, and
the /readyz endpoint.
In `@clients/cerebro/tests/mcp_auth_policy.rs`:
- Around line 102-150: The test rejects_bearer_token_with_wrong_case_prefix
enforces a non-RFC behavior (Bearer scheme is case-insensitive); either rename
the test to reflect a deliberate strict policy (e.g.,
rejects_lowercase_prefix_per_strict_policy) so intent is clear, or update the
auth parsing in server.rs (the code path used by
CerebroService::handle_json_rpc) to compare the auth-scheme case-insensitively
(use eq_ignore_ascii_case("Bearer") or normalize to_lowercase before comparing)
and accept "bearer" as valid; pick one of these fixes and apply it consistently
to the test and the parser.
In `@clients/cerebro/tests/storage_config_test.rs`:
- Around line 127-144: Add two unit tests to fully exercise
validate_startup_requirements: one that sets config.host to a non-loopback
(e.g., "0.0.0.0"), assigns a non-empty auth_token (e.g., "secrettoken") and
asserts validate_startup_requirements() returns Ok (to verify a real token
unblocks startup), and another that sets host to non-loopback and auth_token to
a whitespace-only string (e.g., " \t\n") and asserts
validate_startup_requirements() returns an Err containing the "auth token is
required" message (to verify the trim+is_empty rejection). Use the existing
base_config() helper and the same assert patterns as the other tests so they
integrate consistently with the tests named
startup_validation_requires_auth_token_for_non_loopback_host and
startup_validation_allows_loopback_without_auth_token_for_local_dev.
---
Outside diff comments:
In `@clients/cerebro/src/server.rs`:
- Around line 226-255: In authorize(), replace the unsafe short-circuiting ==
comparisons (token == audit and token == expected) with constant-time
comparisons: add subtle = "2" to Cargo.toml, use subtle::ConstantTimeEq, and
compare byte slices via token.as_bytes().ct_eq(audit.as_bytes()) and
token.as_bytes().ct_eq(expected.as_bytes()), converting the Choice result to a
boolean (e.g., .unwrap_u8() == 1) to decide the branch; keep the rest of
authorize (parse_bearer_token, audit_token handling, returning
AuthContext/CerebroError::Unauthorized) unchanged.
🪄 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
Run ID: 29c19720-654e-40d4-a480-34577f385279
⛔ Files ignored due to path filters (1)
clients/cerebro/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
.github/workflows/_build-cerebro-binaries.yml.github/workflows/pull-request-check.ymlclients/cerebro/Cargo.tomlclients/cerebro/Dockerfile.release-prebuiltclients/cerebro/README.mdclients/cerebro/src/bin/cerebro.rsclients/cerebro/src/config.rsclients/cerebro/src/main.rsclients/cerebro/src/server.rsclients/cerebro/tests/config_load_test.rsclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/tests/http_limits_test.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/tests/tui_event_emission_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: sonar
- GitHub Check: pr-checks
- GitHub Check: dependency-review
- GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs
⚙️ CodeRabbit configuration file
**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.
Files:
clients/cerebro/src/main.rsclients/cerebro/src/bin/cerebro.rsclients/cerebro/tests/config_load_test.rsclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/src/config.rsclients/cerebro/tests/tui_event_emission_tests.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/http_limits_test.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/src/server.rs
**/*
⚙️ CodeRabbit configuration file
**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.
Files:
clients/cerebro/src/main.rsclients/cerebro/src/bin/cerebro.rsclients/cerebro/tests/config_load_test.rsclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/Cargo.tomlclients/cerebro/src/config.rsclients/cerebro/tests/tui_event_emission_tests.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/README.mdclients/cerebro/Dockerfile.release-prebuiltclients/cerebro/tests/http_limits_test.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/src/server.rs
**/*.{md,mdx}
⚙️ CodeRabbit configuration file
**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.
Files:
clients/cerebro/README.md
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow
Applied to files:
clients/cerebro/src/main.rsclients/cerebro/src/bin/cerebro.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths
Applied to files:
clients/cerebro/src/main.rsclients/cerebro/src/bin/cerebro.rsclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why
Applied to files:
clients/cerebro/src/main.rsclients/cerebro/src/bin/cerebro.rs.github/workflows/pull-request-check.yml.github/workflows/_build-cerebro-binaries.ymlclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/Cargo.tomlclients/cerebro/src/config.rsclients/cerebro/README.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/.github/**/*.{yml,yaml} : For workflow/template-only changes, ensure YAML/template syntax validity
Applied to files:
.github/workflows/pull-request-check.yml
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/channels/**/*.rs : Implement `Channel` trait in `src/channels/` with consistent `send`, `listen`, and `health_check` semantics and cover auth/allowlist/health behavior with tests
Applied to files:
clients/cerebro/tests/health_endpoints_test.rsclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions
Applied to files:
clients/cerebro/Cargo.toml
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Preserve release-size profile assumptions in `Cargo.toml` and avoid adding heavy dependencies unless clearly justified
Applied to files:
clients/cerebro/Cargo.toml
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable
Applied to files:
clients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks
Applied to files:
clients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path
Applied to files:
clients/cerebro/src/server.rs
🔇 Additional comments (7)
clients/cerebro/Cargo.toml (2)
51-51: LGTM —tower/utilis the right scope for the new tests.
ServiceExt::oneshotfromtower::utilis what the newhealth_endpoints_test.rsandhttp_limits_test.rsrely on. Restricted to dev-deps, no impact on release size.
34-34: LGTM —serde+parsefeatures correctly enabletoml::from_str.In toml 1.x,
toml::from_strrequires bothparseandserdefeatures when default-features are disabled. This matches the requirements ofCerebroConfig::loadand introduces no unnecessary surface..github/workflows/_build-cerebro-binaries.yml (1)
87-96: LGTM — smoke probe wired correctly per OS.Paths line up with the prior build steps (
clients/cerebroworking dir on Unix, repo-root prefix on Windows),--helpis a cheap liveness check, and exit codes will propagate from bothbashandpwsh. Nice incremental gate against shipping a binary that fails to even initialize CLI parsing.clients/cerebro/src/config.rs (1)
282-299: LGTM — secure-by-default startup gate.Order is right (storage validation first, then network-exposure rule),
ExposeSecretis held only long enough to trim and check emptiness, and the loopback predicate treats unrecognized hostnames as non-loopback (fail closed). Aligns with the deny-by-default policy for the security/gateway/config surface.clients/cerebro/src/bin/cerebro.rs (1)
79-83: LGTM — validation lands before any side effects.Calling
validate_startup_requirements()afterapply_env_overrides()and thetui.enabledfinalization, but beforefrom_config(...).awaitandTcpListener::bind, gives operators a clean fast-fail with no half-initialized state. Matches the parallel call insrc/main.rs.clients/cerebro/src/main.rs (1)
19-22: LGTM — fail-fast wired correctly.Validation runs after env overrides and before any network or storage side effects, matching
bin/cerebro.rs. The two startup paths stay in sync, which is what the new tests instorage_config_test.rsrely on.clients/cerebro/tests/http_limits_test.rs (1)
1-38: LGTM — clean over-limit assertion for the newDefaultBodyLimit.The padding sized to
2 * 1024 * 1024cleanly exceeds the 1 MiB router cap and the bearer/content-type headers ensure you're testing the body-size path rather than tripping on auth or parser preconditions first. Nothing more to do here.
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
clients/cerebro/src/server.rs (1)
53-90: 🧹 Nitpick | 🔵 TrivialBody limit + healthz/readyz wiring LGTM; consider rate-limiting the probes.
MAX_REQUEST_BODY_BYTESconstant is greppable from tests/docs (addresses the prior nit), and applyingDefaultBodyLimit::max(...)at the router level uniformly caps/mcpwhile leaving the GET probes unaffected (they have no body to read).Operationally:
/healthzand/readyzare unauthenticated. That's correct for kube-style probes inside the cluster, but if Cerebro is exposed to the internet (the same hardening this PR targets), they're DoS-amplification candidates —/readyzin particular still callsstorage().ready(), which on the SurrealDB backend issuesRETURN 1;over the connection. Not a blocker, but document the assumption (probes are network-policy-restricted) in the operator docs, or layer atower::limit::ConcurrencyLimitLayer/ per-route rate limiter on/readyzbefore exposing publicly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/cerebro/src/server.rs` around lines 53 - 90, The router exposes unauthenticated probes (handle_health, handle_ready) and while DefaultBodyLimit::max(MAX_REQUEST_BODY_BYTES) is fine for /mcp, you should mitigate DoS amplification by adding a per-route concurrency or rate limit for the probes and/or documenting the network-policy assumption; update router() to apply a tower::limit::ConcurrencyLimitLayer or a per-route rate limiter to the GET("/readyz") (and optionally GET("/healthz")) and ensure the operator docs mention probes must be network-policy-restricted because handle_ready calls storage().ready().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@clients/cerebro/src/config.rs`:
- Around line 179-184: The is_demo_credential function currently treats "root"
as a demo credential which overlaps with the explicit username == "root" &&
password == "root" check elsewhere; either remove "root" from is_demo_credential
to limit it to placeholder values ("local-dev-only",
"CHANGE_ME_BEFORE_PRODUCTION") or update non_demo_secret consumers to include a
clearer diagnostic when a password matches a known default. Modify
is_demo_credential (and any callers) to only match placeholder strings or
augment non_demo_secret/validate_startup_requirements to append a hint like
"password matches known demo/default value" when rejecting credentials so
operators see why their "root" password was flagged.
- Around line 304-333: The block in validate_startup_requirements that re-checks
EmbeddedSurreal credentials is redundant because validate_storage (via
validate_embedded_surreal) already enforces non-demo credentials and rejects
"root", so remove the entire if !is_loopback_host(&self.host) &&
self.storage_mode == StorageMode::EmbeddedSurreal { ... } block from
validate_startup_requirements to keep a single source of truth; ensure existing
tests cover validate_embedded_surreal and validate_startup_requirements
semantics and, if you prefer the alternative behavior (allow demo creds on
loopback but not on non-loopback), instead relax validate_embedded_surreal and
add a clarifying comment in validate_startup_requirements explaining the
asymmetry and add tests for both loopback and non-loopback cases.
- Around line 168-177: normalize_secret currently always allocates a new
SecretString even when trimming doesn't change the value; change it to borrow
the exposed secret (via secret.expose_secret()), compute trimmed, return None if
trimmed.is_empty(), return the original SecretString (the incoming secret) when
trimmed == exposed to avoid reallocating, and only construct a new SecretString
when trimmed differs; ensure you drop the borrow from expose_secret() before
moving/returning the original secret (explicitly drop the borrowed &str or scope
it) so the move is legal; update normalize_secret and its callers (e.g.,
apply_env_overrides) accordingly.
In `@clients/cerebro/src/server.rs`:
- Around line 267-285: The parse_bearer_token function performs redundant
trimming and uses a Unicode whitespace split; replace the dead
trim_start_matches call with a single rest.trim() and change
split_once(char::is_whitespace) to split_once(char::is_ascii_whitespace) so the
split and trimming are RFC-7230 compliant; update references in
parse_bearer_token (auth_header, scheme, rest, token) accordingly and keep the
same Unauthorized error handling for malformed headers.
- Around line 132-149: The current request_kind value records which tokens are
accepted (self.config.audit_token) rather than which token was actually used;
change this by leaving auth_mode unset when creating the tracing span and
instead record the per-request value after authorize() completes: create the
span with auth_mode empty (or a placeholder), call authorize() to obtain
auth_context, then call span.record("auth_mode", if auth_context.is_audit {
"audit" } else { "operator" }) before exiting the request handler so logs show
the actual token type used; reference the existing span, request_id, tool_name,
authorize(), and auth_context.is_audit symbols when implementing this.
- Around line 144-184: The span with request_id/tool_name/auth_mode is only
applied to self.tools.handle(...) so early logs (authorize failure,
event_bus.publish, final success/failure logs) lack those fields; instead attach
the span to the entire async block assigned to response (i.e., instrument the
async { ... } future that contains authorize, event_bus.publish, and the match)
so every tracing! call inside (tracing::warn! in authorize error path,
event_bus.publish-related logs, and the final tracing::info!/tracing::warn!)
inherit the span; update the use/import to the appropriate Instrument trait
(tracing_futures::Instrument or tracing::Instrument) if required.
In `@clients/cerebro/src/storage/surreal.rs`:
- Around line 477-481: The readiness probe in ready() currently only awaits
self.db.query("RETURN 1;") which misses per-statement failures; change the call
to await the query, then call .check() on the returned Response and propagate
any error into CerebroError::Storage (e.g., await self.db.query("RETURN 1;"),
then .map(|resp| resp.check()).map_err(|err| CerebroError::Storage(...)) or
explicitly match the response and convert resp.check() failure into the same
error format) so that execution errors on the statement cause ready() to fail.
In `@clients/cerebro/tests/health_endpoints_test.rs`:
- Around line 108-128: Update the test
readyz_returns_service_unavailable_when_storage_readiness_fails to also parse
and assert the JSON response body returned by handle_ready: after receiving the
response from service.router().oneshot(...), read and deserialize the body to
ensure it contains {"status":"not_ready","error":<string>} (assert that status
== "not_ready" and that error is a non-empty string); locate the test function
name readyz_returns_service_unavailable_when_storage_readiness_fails and the
handler handle_ready in server.rs to mirror the expected payload shape, and use
the same request setup (CerebroService::new with FailingReadyStorage and
router()) when adding these assertions.
In `@clients/cerebro/tests/storage_config_test.rs`:
- Around line 161-173: The test
startup_validation_rejects_whitespace_only_auth_token_for_non_loopback can be
polluted by ambient CEREBRO_AUTH_TOKEN/CEREBRO_AUDIT_TOKEN because it calls
apply_env_overrides(); fix it by acquiring ENV_LOCK and using EnvVarGuard to
clear or unset both "CEREBRO_AUTH_TOKEN" and "CEREBRO_AUDIT_TOKEN" for the
duration of the test before calling apply_env_overrides(), restoring them on
drop; if EnvVarGuard lacks an unset helper, add EnvVarGuard::unset(key) that
records the previous value and removes the var so the test reliably verifies
whitespace-only tokens in validate_startup_requirements().
---
Outside diff comments:
In `@clients/cerebro/src/server.rs`:
- Around line 53-90: The router exposes unauthenticated probes (handle_health,
handle_ready) and while DefaultBodyLimit::max(MAX_REQUEST_BODY_BYTES) is fine
for /mcp, you should mitigate DoS amplification by adding a per-route
concurrency or rate limit for the probes and/or documenting the network-policy
assumption; update router() to apply a tower::limit::ConcurrencyLimitLayer or a
per-route rate limiter to the GET("/readyz") (and optionally GET("/healthz"))
and ensure the operator docs mention probes must be network-policy-restricted
because handle_ready calls storage().ready().
🪄 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
Run ID: f46a22d4-b595-4cbb-8173-c3bfb994d4c3
⛔ Files ignored due to path filters (2)
clients/agent-runtime/Cargo.lockis excluded by!**/*.lockclients/cerebro/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (14)
.github/workflows/dependency-review.yml.github/workflows/pull-request-check.ymlclients/cerebro/Cargo.tomlclients/cerebro/Dockerfile.release-prebuiltclients/cerebro/README.mdclients/cerebro/src/config.rsclients/cerebro/src/server.rsclients/cerebro/src/storage/mod.rsclients/cerebro/src/storage/surreal.rsclients/cerebro/tests/config_load_test.rsclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/tests/tui_event_emission_tests.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: sonar
- GitHub Check: pr-checks
- GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (3)
**/*.rs
⚙️ CodeRabbit configuration file
**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.
Files:
clients/cerebro/src/storage/mod.rsclients/cerebro/tests/tui_event_emission_tests.rsclients/cerebro/tests/config_load_test.rsclients/cerebro/src/storage/surreal.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/src/config.rsclients/cerebro/src/server.rs
**/*
⚙️ CodeRabbit configuration file
**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.
Files:
clients/cerebro/src/storage/mod.rsclients/cerebro/tests/tui_event_emission_tests.rsclients/cerebro/tests/config_load_test.rsclients/cerebro/Cargo.tomlclients/cerebro/src/storage/surreal.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/Dockerfile.release-prebuiltclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/src/config.rsclients/cerebro/README.mdclients/cerebro/src/server.rs
**/*.{md,mdx}
⚙️ CodeRabbit configuration file
**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.
Files:
clients/cerebro/README.md
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/channels/**/*.rs : Implement `Channel` trait in `src/channels/` with consistent `send`, `listen`, and `health_check` semantics and cover auth/allowlist/health behavior with tests
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why
Applied to files:
.github/workflows/pull-request-check.ymlclients/cerebro/tests/config_load_test.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/tests/health_endpoints_test.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/.github/**/*.{yml,yaml} : For workflow/template-only changes, ensure YAML/template syntax validity
Applied to files:
.github/workflows/pull-request-check.yml
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Applied to files:
clients/cerebro/tests/config_load_test.rsclients/cerebro/src/config.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable
Applied to files:
clients/cerebro/tests/config_load_test.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/Dockerfile.release-prebuiltclients/cerebro/README.mdclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths
Applied to files:
clients/cerebro/tests/config_load_test.rsclients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/README.mdclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions
Applied to files:
clients/cerebro/Cargo.toml
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Preserve release-size profile assumptions in `Cargo.toml` and avoid adding heavy dependencies unless clearly justified
Applied to files:
clients/cerebro/Cargo.toml
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/channels/**/*.rs : Implement `Channel` trait in `src/channels/` with consistent `send`, `listen`, and `health_check` semantics and cover auth/allowlist/health behavior with tests
Applied to files:
clients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/tests/storage_config_test.rsclients/cerebro/tests/health_endpoints_test.rsclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks
Applied to files:
clients/cerebro/tests/mcp_auth_policy.rsclients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency
Applied to files:
clients/cerebro/src/server.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path
Applied to files:
clients/cerebro/src/server.rs
🔇 Additional comments (14)
.github/workflows/dependency-review.yml (1)
24-24: LGTM, thoughfetch-depth: 0is overkill here.
actions/dependency-review-actiononly needs base/head refs onpull_requestevents, so full history isn't required — but it's harmless aside from a slight checkout cost. Happy to leave as-is..github/workflows/pull-request-check.yml (1)
135-145: Quality gates now complete for Cerebro — nicely done. 🎯fmt + clippy (
-D warnings) + check + test (both--locked) line up with#674and the agent-runtime conventions.One tiny nit (entirely optional):
cargo clippy --all-targetsalready type-checks everythingcargo checkwould, so step at line 141-142 is largely redundant on green builds. You could drop it to shave ~CI seconds, or keep it as a fast-fail pre-clippy guard — your call.clients/cerebro/src/storage/mod.rs (1)
83-85: LGTM — sensible default forStorage::ready.A trait-level
Ok(())default keeps the existingInMemoryStorageandDiskBackedStoragebehaviorally always-ready, while lettingSurrealStorageopt into a real probe. Symmetry withcount()etc. is fine.clients/cerebro/README.md (1)
37-83: LGTM — README accurately mirrors the new startup/probe/limits behavior.The explicit 1 MiB body limit, the fail-fast statement for non-loopback bindings without
auth_token, the demo-credential denylist (local-dev-only,CHANGE_ME_BEFORE_PRODUCTION,root), and the/healthz//readyzprobe semantics all match the implementation inserver.rs/config.rsand the tests. Prior feedback on this section is resolved.As per coding guidelines: "Verify technical accuracy and that docs stay aligned with code changes."
clients/cerebro/Cargo.toml (1)
33-35: LGTM — dependency additions are tightly scoped and justified.
subtleunderpins constant-time token comparison inserver.rs, theserdefeature ontomlis required for typed config loading (issue#669), andtowerutilis dev-only forServiceExtin health endpoint tests. No heavy footprint added.Also applies to: 52-52
clients/cerebro/tests/tui_event_emission_tests.rs (1)
28-33: LGTM — payload-level assertions match the publishing flow.Asserted fields line up with
handle_json_rpcStarted (L164-178) and Finished (L195-205) inserver.rs. Coverage is asymmetric (Started'sstatus: "started"isn't asserted), but that's a stylistic gap, not a correctness one.clients/cerebro/tests/config_load_test.rs (1)
1-43: LGTM — exact-value secret assertions close the prior gap.
ExposeSecret-based comparisons exercisesecret_string_opt::deserializeend-to-end and would catch silent value drops or sentinel substitutions. Past feedback addressed.clients/cerebro/Dockerfile.release-prebuilt (1)
18-31: LGTM — secure-by-default posture is now both documented and enforced.The loopback-bind caveat is called out inline (L18-21), and per the README the startup validator now rejects the bundled
local-dev-only/rootcreds on non-loopback binds, closing the prior soft-footgun. Defaults are demo-only as intended.Based on learnings: "Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable."
clients/cerebro/tests/mcp_auth_policy.rs (1)
97-151: LGTM — auth-policy coverage now matches RFC 7235 and#676.
accepts_bearer_token_with_lowercase_prefixresolves the earlier RFC-compliance concern by accepting case-insensitiveBearer, and the new audit-token success test plus the trailing-whitespace rejection case round out the valid/invalid/malformed matrix. Prior feedback addressed.clients/cerebro/tests/storage_config_test.rs (1)
127-159: LGTM — both new pass-paths exercise the validator end-to-end.
startup_validation_allows_non_loopback_with_real_auth_tokencorrectly upgrades the SurrealDB credentials to non-demo values (operator/secure-password), which is necessary becausevalidate_startup_requirementsalso gates onnon_demo_secretfor non-loopback embedded startup — without that, the test would have been masking the auth-token check behind a credential failure. Loopback default-pass test at 140–144 is the right negative control.clients/cerebro/tests/health_endpoints_test.rs (1)
12-62: Lock downcount()to also be unreachable — nice belt-and-braces against regression.Wiring
count()tounreachable!()is exactly what makes this test useful: if someone revertshandle_readyto the previous O(rows)storage.count()implementation (the prior critical concern), this test panics instead of silently passing. Good defensive contract test.clients/cerebro/src/config.rs (1)
429-442: LGTM — secret trimming is now applied at deserialization time, closing the file-vs-env divergence.This addresses the prior concern that file-loaded tokens were stored verbatim while env-loaded tokens were trimmed by
apply_env_overrides—Authorization: Bearer " secret "from a TOML file would have passed startup validation but failed (or matched the wrong canonical form against) the request-time bearer comparison. Now both ingress paths produce the same canonical secret. Good fix.clients/cerebro/src/server.rs (2)
237-264: Constant-time comparison + always-parse: looks good; one timing nit on the audit-vs-auth ordering.Both
ct_eqcalls compare bytes in constant time, andparse_bearer_tokenruns unconditionally so that "no token configured" and "wrong scheme" don't diverge in timing. One subtle observation: whenaudit_tokenis configured, a winning operator token does one extract_eqversus a winning audit token. An attacker who already holds a valid token could in principle distinguish "I'm authenticated as audit" vs "I'm authenticated as operator" via timing — but they already know that from the response/permissions, so this is essentially non-exploitable. Calling it out only so it doesn't surprise a future auditor.Also: per
subtle::ConstantTimeEqfor[u8], slices of unequal length short-circuit toChoice(0)— that's the intended behavior (length is not secret in HTTP), but worth noting if you ever want hardened-equal-length comparison.
306-321: LGTM — readiness now delegates to a cheap backend probe and locks the contract.Switching
/readyzfromstorage().count()tostorage().ready()resolves the prior critical concern about probe-induced full table scans on SurrealDB. Pairing this withFailingReadyStoragein the integration tests covers both branches. Returning a structurednot_readybody with the underlying error string is operator-friendly without leaking secrets (the error variants here are storage/IO messages, not token material).
|



Related Issues
fix #669
fix #670
fix #671
fix #672
fix #673
fix #674
fix #675
fix #676
Summary
Harden Cerebro for production readiness by fixing TOML config loading, enforcing startup validation, adding health/readiness endpoints, making request/body and auth behavior explicit, and improving structured observability.
Also tighten delivery safeguards with container default hardening, explicit CI checks for the crate, and binary smoke verification.
Tested Information
Verified from the repository root with targeted Cerebro checks:
cargo check --manifest-path clients/cerebro/Cargo.tomlcargo test --manifest-path clients/cerebro/Cargo.toml --test config_load_test --test storage_config_test --test health_endpoints_test --test http_limits_test --test mcp_auth_policy --test mcp_tools_contract --test tui_event_emission_testscargo test --manifest-path clients/cerebro/Cargo.toml --test mcp_tools_contract -- --nocaptureCoverage of the changes includes:
/healthzand/readyzDocumentation Impact
clients/cerebro/README.mdBreaking Changes
No intentional breaking API changes, but startup is now stricter for non-loopback deployments without an
auth_token, and production operators should useGET /healthzandGET /readyzinstead of probingPOST /mcp.Checklist