Skip to content

feat(rook): add admin API for operator management#629

Merged
yacosta738 merged 2 commits into
developfrom
feat/rook-590-admin-api
Apr 21, 2026
Merged

feat(rook): add admin API for operator management#629
yacosta738 merged 2 commits into
developfrom
feat/rook-590-admin-api

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

Related Issues

closes #590


Summary

  • add a real /api admin surface for Rook backed by shared registry/services instead of internal-only management state
  • expose read/write endpoints for accounts, pools, routes, pool membership, health summaries, settings, and a documented usage placeholder
  • mount the admin router alongside the existing /v1 gateway and dashboard routes while preserving redacted account responses (has_api_key, never raw api_key)

Tested Information

  • cargo test --manifest-path "clients/rook/Cargo.toml"
  • cargo fmt --manifest-path "clients/rook/Cargo.toml" --all -- --check
  • cargo clippy --manifest-path "clients/rook/Cargo.toml" --all-targets -- -D warnings
  • SDD verify result for rook-590-admin-api: PASS

Reviewer focus:

  • admin transport contracts and redaction behavior
  • delete/reference-integrity semantics for accounts, pools, and routes
  • server composition under /api + coexistence with /v1 and dashboard

Documentation Impact

  • Docs updated in:
    • openspec/changes/rook-590-admin-api/proposal.md
    • openspec/changes/rook-590-admin-api/spec.md
    • openspec/changes/rook-590-admin-api/design.md
    • openspec/changes/rook-590-admin-api/tasks.md
    • openspec/changes/rook-590-admin-api/verify-report.md
  • No docs update required because:
  • I verified the documentation matches the current behavior.

Breaking Changes

None.


Checklist

  • I have checked that there isn’t already a PR solving the same problem.
  • I have read the Contributing Guidelines.
  • I ensured my code follows the project's style guidelines.
  • I have added or updated tests that prove my fix is effective or that my feature works.
  • I have updated the documentation, or I explained above why no documentation update is needed.
  • I verified the documentation matches the current behavior.
  • I have documented any breaking changes in the Breaking Changes section.
  • I have linked the related issue (if any).

Expose stable /api endpoints for accounts, pools, routes, health, settings, and usage status so the dashboard and automation can manage Rook through supported contracts instead of internal runtime state.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Warning

Rate limit exceeded

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

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 33 minutes and 12 seconds.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8fcffa5-886d-49d8-8c3f-0ad0e2a57b53

📥 Commits

Reviewing files that changed from the base of the PR and between 727c212 and b20252a.

📒 Files selected for processing (11)
  • clients/rook/src/admin/handlers.rs
  • clients/rook/src/db/account.rs
  • clients/rook/src/db/pool.rs
  • clients/rook/src/db/route.rs
  • clients/rook/src/server/mod.rs
  • clients/rook/src/services/account.rs
  • clients/rook/src/services/pool.rs
  • openspec/changes/rook-590-admin-api/design.md
  • openspec/changes/rook-590-admin-api/proposal.md
  • openspec/changes/rook-590-admin-api/spec.md
  • openspec/changes/rook-590-admin-api/verify-report.md
📝 Walkthrough

Walkthrough

Implements a complete administrative HTTP API under /api for Rook, exposing CRUD endpoints for accounts, pools, and routes, pool membership management, settings persistence, health summaries, and usage status. Adds request/response DTOs with API key redaction, structured error responses, referential integrity checks to prevent dangling references on deletion, and wires the admin router into the existing server composition.

Changes

Cohort / File(s) Summary
Admin Module Implementation
clients/rook/src/admin/handlers.rs, clients/rook/src/admin/types.rs, clients/rook/src/admin/mod.rs
New 500+ line handlers implementing 24 HTTP endpoints for health, accounts, pools, routes, settings, and usage; 475+ lines of DTOs with serde defaults and API key redaction (exposing has_api_key boolean instead of raw credentials); 881+ lines of integration tests verifying CRUD semantics, error handling, health aggregation, and reference-conflict detection.
Referential Integrity Checks
clients/rook/src/db/account.rs, clients/rook/src/db/pool.rs, clients/rook/src/services/account.rs, clients/rook/src/services/pool.rs
Added DB-layer is_account_referenced_by_pool() and is_pool_referenced_as_fallback() queries; updated service delete methods to check references pre-deletion, returning 409 Conflict when resources are in use (e.g., account referenced by pool, pool referenced by route).
Server Integration
clients/rook/src/server/mod.rs, clients/rook/src/lib.rs
Replaced /api stub router with real admin::build_router(registry), cloned registry for both gateway state and admin router; added tests for /api/usage and dashboard root route; exported pub mod admin.
Service/Route Refactoring
clients/rook/src/services/route.rs, clients/rook/src/services/pool.rs, clients/rook/src/services/settings.rs, clients/rook/src/registry/mod.rs
Reformatted signatures and lock patterns for readability; simplified trait method multi-line formatting; updated test struct initialization from mutation to struct-literal style with ..Default inheritance (no semantic changes).
Gateway/Dashboard Formatting
clients/rook/src/gateway/handlers.rs, clients/rook/src/gateway/mod.rs, clients/rook/src/gateway/types.rs, clients/rook/src/gateway/upstream.rs, clients/rook/src/gateway/vendor.rs, clients/rook/src/domain/mod.rs, clients/rook/src/db/mod.rs, clients/rook/src/db/route.rs, clients/rook/src/routing/mod.rs, clients/rook/src/services/health.rs
Reformatted signatures, import ordering, SQL error mapping, and test assertions into multi-line styles without functional logic changes.
Specification & Design Documentation
openspec/changes/rook-590-admin-api/proposal.md, openspec/changes/rook-590-admin-api/spec.md, openspec/changes/rook-590-admin-api/design.md, openspec/changes/rook-590-admin-api/tasks.md, openspec/changes/rook-590-admin-api/verify-report.md, openspec/changes/rook-590-admin-api/state.yaml
New design specification detailing admin API contracts, error mapping heuristics, redaction behavior, health computation, and reference-integrity safeguards; proposal outlining scope and risks; implementation tasks; verification report confirming functional alignment and test coverage; state manifest marking phase verify complete.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant AdminHandler as Admin Handler<br/>/api/accounts/{id}
    participant Registry as RookRegistry<br/>(Services)
    participant DB as SQLite DB
    participant Health as Health Tracker

    Client->>AdminHandler: DELETE /api/accounts/{id}
    activate AdminHandler
    AdminHandler->>AdminHandler: Validate account_id format
    AdminHandler->>Registry: account().get(id).await
    activate Registry
    Registry->>DB: fetch account by id
    activate DB
    DB-->>Registry: account (or not found)
    deactivate DB
    deactivate Registry
    
    alt Account not found
        AdminHandler-->>Client: 404 not_found response
    else Account found
        AdminHandler->>Registry: account().is_account_referenced_by_pool(id).await
        activate Registry
        Registry->>DB: SELECT 1 FROM pool_members WHERE account_id=?
        activate DB
        DB-->>Registry: row exists? (bool)
        deactivate DB
        deactivate Registry
        
        alt Referenced by pool
            AdminHandler-->>Client: 409 Conflict (reference_conflict)
        else Not referenced
            AdminHandler->>Registry: account().delete(id).await
            activate Registry
            Registry->>DB: DELETE FROM accounts WHERE id=?
            activate DB
            DB-->>Registry: rows_affected
            deactivate DB
            Registry->>Health: cleanup_health_for(id)
            activate Health
            Health-->>Registry: ok
            deactivate Health
            deactivate Registry
            AdminHandler-->>Client: 204 No Content
        end
    end
    deactivate AdminHandler
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #605: Introduces the SQLite database layer (SqliteDb, pool/account/route tables) that the new admin handlers and referential-check methods (is_account_referenced_by_pool, is_pool_referenced_as_fallback) directly depend on for persistence and integrity queries.
  • PR #627: Modifies the ProviderAccount model and API key handling alongside HTTP layer improvements; shares domain types and credential-redaction patterns with the new admin API implementation.

Suggested labels

area:rust, area:docs, risk:security

Suggested reviewers

  • yuniel-acosta
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title follows Conventional Commit style with a clear 'feat' prefix, specific scope (rook), and a concise, imperative description within the 72-character limit.
Description check ✅ Passed The PR description is well-structured with all key sections completed: related issues (#590), a clear summary of changes, tested information with specific commands and focus areas, documentation updates listed, and all checklist items marked complete.
Linked Issues check ✅ Passed The PR comprehensively implements all requirements from issue #590: delivers a stable admin API over shared registry/services [end-to-end implementation], includes extensive test coverage and SDD verification [validation strategy], and documents the contract via spec/design/proposal/tasks/verify-report [documentation for operators/developers].
Out of Scope Changes check ✅ Passed All code changes align with issue #590 scope: admin API endpoints (accounts/pools/routes/health/settings/usage), transport DTOs, error handling, redaction behavior, and server composition. Supporting changes (db reference checks, service error handling) are necessary for reference-integrity enforcement. Minor reformatting across gateway/routing/registry modules serves consistency without introducing functional drift.

✏️ 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/rook-590-admin-api

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.

@github-actions github-actions Bot added the size/xl Denotes an extra large change size label Apr 21, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

✅ Contributor Report

User: @yacosta738
Status: Passed (12/13 metrics passed)

Metric Description Value Threshold Status
PR Merge Rate PRs merged vs closed 91% >= 30%
Repo Quality Repos with ≥100 stars 0 >= 0
Positive Reactions Positive reactions received 11 >= 1
Negative Reactions Negative reactions received 0 <= 5
Account Age GitHub account age 3098 days >= 30 days
Activity Consistency Regular activity over time 108% >= 0%
Issue Engagement Issues with community engagement 0 >= 0
Code Reviews Code reviews given to others 584 >= 0
Merger Diversity Unique maintainers who merged PRs 2 >= 0
Repo History Merge Rate Merge rate in this repo 93% >= 0%
Repo History Min PRs Previous PRs in this repo 268 >= 0
Profile Completeness Profile richness (bio, followers) 90 >= 0
Suspicious Patterns Spam-like activity detection 1 N/A

Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-21 to 2026-04-21

Copy link
Copy Markdown
Contributor

@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: 11

Caution

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

⚠️ Outside diff range comments (4)
clients/rook/src/db/pool.rs (1)

78-105: ⚠️ Potential issue | 🟠 Major

Validate fallback_pool_id before inserting pools.

fallback_pool_id is persisted directly, while the provided schema shows only a nullable TEXT column. That allows admin-created pools to point at non-existent fallback pools, undermining the new delete/reference-integrity semantics.

Proposed guard
 pub async fn insert_pool(&self, pool: &ProviderPool) -> Result<(), RookError> {
     let id = pool.id.to_string();
     let strategy_str = strategy_to_db_str(&pool.strategy)?;
     let fallback = pool.fallback_pool_id.as_ref().map(|p| p.to_string());
     let now = Utc::now().to_rfc3339();

     // Start transaction to make pool + members insertion atomic
     let mut tx = self
         .pool()
         .begin()
         .await
         .map_err(|e| RookError::Registry(format!("failed to begin transaction: {e}")))?;
+
+    if let Some(fallback_pool_id) = &pool.fallback_pool_id {
+        if fallback_pool_id == &pool.id {
+            return Err(RookError::Registry(format!(
+                "fallback_pool_id {} cannot reference the pool itself",
+                fallback_pool_id
+            )));
+        }
+
+        let fallback_exists =
+            sqlx::query("SELECT 1 FROM provider_pools WHERE id = ? LIMIT 1")
+                .bind(fallback_pool_id.to_string())
+                .fetch_optional(&mut *tx)
+                .await
+                .map_err(|e| {
+                    RookError::Registry(format!("failed to validate fallback_pool_id: {e}"))
+                })?;
+
+        if fallback_exists.is_none() {
+            return Err(RookError::Registry(format!(
+                "fallback_pool_id {} does not exist",
+                fallback_pool_id
+            )));
+        }
+    }

     // Insert pool
     sqlx::query(

As per coding guidelines, **/*: Security first, performance second. Validate input boundaries, auth/authz implications, and secret management.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/rook/src/db/pool.rs` around lines 78 - 105, The insert_pool function
currently persists pool.fallback_pool_id without checking it exists; before
executing the INSERT in insert_pool, validate that if
pool.fallback_pool_id.is_some() then that id exists in provider_pools (or
whatever canonical provider table) and return a RookError::Registry if it does
not; perform the existence check within the same transaction (use the mut tx
already created) using a SELECT 1 WHERE id = ? and only proceed with the INSERT
when the check succeeds (treat NULL/None as allowed), so
provider_pools.fallback_pool_id cannot reference a non-existent pool and the
operation remains atomic.
clients/rook/src/db/route.rs (1)

180-197: ⚠️ Potential issue | 🟠 Major

Delete routes with an atomic fallback-reference guard.

Line 181 checks references, then Line 197 deletes later. A concurrent insert can add fallback_route_id = id between those statements, bypassing the guard. Use a transaction/conditional delete such as DELETE ... WHERE id = ? AND NOT EXISTS (...), then re-query only to classify “not found” vs “referenced”.

As per coding guidelines, **/*.rs: Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/rook/src/db/route.rs` around lines 180 - 197, The current two-step
check (the referencing query using referencing and id_str) then DELETE (result)
is vulnerable to a race where a concurrent insert sets fallback_route_id between
the checks; change to a single conditional delete inside the same DB operation:
perform a DELETE FROM model_routes WHERE id = ? AND NOT EXISTS (SELECT 1 FROM
model_routes WHERE fallback_route_id = ?) (using self.pool() and binding id_str
twice), then inspect the affected rows to decide whether to return success,
return RookError::Registry indicating "referenced by route ..." (by re-querying
the referencing route only if rows_affected == 0 and the route still exists), or
return not-found if the route itself does not exist—replace the separate
referencing lookup and delete (the referencing variable and subsequent
query("DELETE ...")) with this atomic conditional delete and the follow-up
disambiguation logic.
clients/rook/src/services/pool.rs (1)

188-197: ⚠️ Potential issue | 🔴 Critical

Make SQLite pool updates atomic and preserve not-found semantics.

This path currently deletes first and reinserts later, so an unknown pool ID becomes a create, and a failed reinsert can leave the existing pool deleted. Add a real transactional update/upsert-with-existence-check before exposing this through admin PUT /api/pools/{pool_id}.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/rook/src/services/pool.rs` around lines 188 - 197, The update(&self,
pool: ProviderPool) implementation deletes then reinserts which turns unknown
IDs into creates and risks data loss on failure; change update to run inside a
DB transaction on self.db.pool() (begin/commit/rollback), first SELECT FOR
UPDATE or query provider_pools WHERE id = ? to verify existence, return a
not-found RookError if absent, then perform an UPDATE (or an upsert) of the
existing row (or delete+insert inside the same transaction) and only commit if
all steps succeed; reference the update function, ProviderPool type, existing
sqlx::query("DELETE FROM provider_pools WHERE id = ?") usage and
self.db.insert_pool to locate where to introduce the transaction and
existence-check logic.
clients/rook/src/server/mod.rs (1)

61-80: ⚠️ Potential issue | 🟠 Major

Gate the admin router before exposing mutating endpoints.

/api now includes unauthenticated CRUD/delete/settings routes and inherits the configured bind address; with host = "0.0.0.0", these admin endpoints become remotely reachable before #591 lands. Keep /api loopback-only or add auth middleware before nesting it.

🛡️ Minimal loopback guard until admin auth exists
 async fn build_app_with_registry(
-    _config: ServerConfig,
+    config: ServerConfig,
     registry: RookRegistry,
 ) -> Result<Router, RookError> {
+    let addr: SocketAddr = config
+        .socket_addr()
+        .parse()
+        .map_err(|e: std::net::AddrParseError| RookError::Config(e.to_string()))?;
+    if !addr.ip().is_loopback() {
+        return Err(RookError::Config(
+            "admin API requires a loopback bind until admin auth is implemented".to_string(),
+        ));
+    }
+
     let engine = RoutingEngine::new(registry.clone());

If /v1 must still support remote binds before #591, split admin/gateway listeners or protect only the nested admin router with middleware. As per coding guidelines, **/*: Security first, performance second.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/rook/src/server/mod.rs` around lines 61 - 80, The admin router is
being exposed unguarded in build_app_with_registry via .nest("/api",
admin::build_router(registry)), which makes unauthenticated mutating endpoints
reachable; either restrict the admin nest to loopback only or wrap it with auth
middleware before adding it to the Router. Update build_app_with_registry to (a)
apply an authentication/middleware layer around admin::build_router(registry)
(e.g., using tower/axum middleware) or (b) remove the admin nest from this
externally bound Router and instead serve admin::build_router(registry) on a
loopback-only listener (split admin/gateway listeners), ensuring the change
touches the Router construction where .nest("/api", ...) is invoked.
🤖 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/rook/src/admin/handlers.rs`:
- Around line 32-69: The handlers currently leak internal error detail by
returning other.to_string() in classify_rook_error and the raw message in
classify_registry_message's default branch; change both to return a generic
transport message (e.g. use admin_error_response("internal_error", "Internal
server error") or similar) while preserving the original error text only for
logging; update classify_rook_error (the match arm for `other`) and the fallback
branch of classify_registry_message to use the generic message instead of the
raw error string.
- Around line 13-23: Handlers currently use raw Path and Json extractors (Path,
ExtractJson/Json) which let Axum return its default rejections; update those
handlers to accept rejection-aware extractors or explicitly map rejections into
the AdminErrorResponse envelope: change handler parameters from Path<T> and
Json<T> to Result<Path<T>, axum::extract::rejection::PathRejection> and
Result<ExtractJson<T>, axum::extract::rejection::JsonRejection> (or use a custom
wrapper extractor), then convert any Err(rejection) into the
AdminJson/AdminCreated/AdminEmpty error variant by constructing
(StatusCode::BAD_REQUEST, Json(AdminErrorResponse { error: ... })) so all
invalid UUIDs and malformed JSON produce the documented AdminErrorResponse;
ensure you reference and use the AdminJson and AdminErrorResponse types in the
handler return signatures.

In `@clients/rook/src/admin/mod.rs`:
- Around line 165-183: Add a new unified composition test (or extend
admin_router_preserves_health_and_usage_placeholder) that builds the composed
server via test_api_app() and build_router(registry) and then issues sequential
requests for "/api/health", "/v1/models", "/", and an "/assets/<some-file>" path
using the existing helpers request_text and request_json; assert expected
StatusCode and bodies for each response to ensure no route shadowing (verify
health returns "ok", usage/usage_json assertions remain, models endpoint returns
expected v1 models JSON, root "/" returns expected index, and "/assets/..."
returns the asset content). Use the same helper functions (test_api_app,
build_router, request_text, request_json) so the test exercises the full nested
router composition and covers the previously untested "/assets/*" route.

In `@clients/rook/src/services/account.rs`:
- Around line 149-157: The current delete method in AccountService uses two
separate DB calls (is_account_referenced_by_pool and delete_account) which
allows a race where a pool membership can be inserted between them; change this
to an atomic DB operation: implement a transactional/conditional delete in your
DB layer (e.g., a new method like delete_account_if_not_referenced or
delete_account_tx) that attempts to delete the account only if no pool
references exist and returns a discriminated result (Deleted, Referenced,
NotFound); update AccountService::delete to call that single DB operation
(replace is_account_referenced_by_pool + delete_account) and map the Returned
enum into the appropriate RookError variants so you can distinguish “not found”
from “referenced” after the atomic operation.

In `@openspec/changes/rook-590-admin-api/design.md`:
- Around line 773-788: The design currently says handle_remove_pool_member
should return 404 for removing a non-member but the implementation/verification
shows a 409 Conflict; update the design to match the implemented contract: in
the `handle_remove_pool_member` errors list, change the "unknown membership"
outcome to `409 Conflict` (or alternatively, if you prefer the 404 contract,
modify the implementation path `registry.pools().remove_member(pool_id,
account_id).await?` and associated tests to return 404 instead); ensure
references to `handle_remove_pool_member`, `registry.pools().remove_member`, and
`registry.pools().get` are consistent with the chosen behavior.

In `@openspec/changes/rook-590-admin-api/proposal.md`:
- Around line 110-114: The Settings endpoints list incorrectly advertises PATCH
/api/settings as supported; update the proposal so it reflects the MVP contract
by removing `PATCH /api/settings` from the endpoint list or explicitly marking
it as unsupported/“TBD” (i.e., leave only `GET /api/settings` and `PUT
/api/settings` or add a note like “PATCH /api/settings — unsupported in MVP”) to
match the design and tasks.
- Around line 55-57: The proposal references stale paths under
clients/agent-runtime (e.g., "clients/agent-runtime/src/gateway/admin/mod.rs",
"clients/agent-runtime/src/gateway/admin/types.rs",
"clients/agent-runtime/src/gateway/admin/handlers.rs"); update those occurrences
to the correct implementation paths under clients/rook (replace each
"clients/agent-runtime/..." with the corresponding "clients/rook/..." path) and
scan the rest of proposal.md (including the noted ranges around lines 133-137
and 177-178) to correct any other similar stale path references so the
documented implementation areas match the actual PR.

In `@openspec/changes/rook-590-admin-api/spec.md`:
- Around line 839-843: The example error object under the "code":
"resource_in_use" entry uses a numeric id ("123") which violates the spec
requiring UUID-string IDs; update the example so the details.id (and any id
shown inside the message text, e.g., "pool 123") are replaced with a UUID-shaped
string (for example a v4 UUID) and adjust the message to reference that UUID
string so the example aligns with the documented contract.
- Around line 746-755: The documented HealthAccountView JSON schema is out of
sync with the Rust transport struct used by clients/rook (see HealthAccountView
in clients/rook/src/admin/types.rs) which serializes display_name, vendor,
enabled, and is_available; update the spec to include these fields
(display_name, vendor, enabled, is_available) with their types and
nullable/optional semantics to match the implemented struct, or remove those
fields from the Rust transport view so both the spec and the HealthAccountView
implementation share one stable contract; ensure the doc example's keys,
nullability, and default values exactly mirror the serialization behavior in
HealthAccountView.

In `@openspec/changes/rook-590-admin-api/verify-report.md`:
- Around line 151-161: The verification report incorrectly shows a PASS while
two required gates failed: the formatting check (cargo fmt --manifest-path
"clients/rook/Cargo.toml" --all -- --check) and clippy (cargo clippy
--manifest-path "clients/rook/Cargo.toml" --all-targets -- -D warnings); run and
fix formatting issues raised by cargo fmt and resolve all clippy warnings in the
Rook crate (clients/rook) so cargo fmt and cargo clippy succeed, then re-run the
three gates (cargo test, cargo fmt --check, cargo clippy ...) and update the
verification state to FAILED/INCOMPLETE if any gate still fails rather than
marking as PASS.
- Around line 182-186: The implementation currently relies on Axum's default
extraction behavior for malformed path IDs, but the design requires returning
the admin `400 bad_request` envelope; change the route handling for path IDs
(where Path<...>/Uuid extraction or parse_id helper is used) to explicitly catch
UUID parse failures and convert them into the admin bad_request response (either
by implementing a custom extractor that maps parse errors to the admin error
envelope or by adding a rejection-to-response mapping in the app's
handle_rejection middleware), and add unit/integration tests asserting that
malformed path IDs produce the admin `400 bad_request` envelope rather than Axum
defaults.

---

Outside diff comments:
In `@clients/rook/src/db/pool.rs`:
- Around line 78-105: The insert_pool function currently persists
pool.fallback_pool_id without checking it exists; before executing the INSERT in
insert_pool, validate that if pool.fallback_pool_id.is_some() then that id
exists in provider_pools (or whatever canonical provider table) and return a
RookError::Registry if it does not; perform the existence check within the same
transaction (use the mut tx already created) using a SELECT 1 WHERE id = ? and
only proceed with the INSERT when the check succeeds (treat NULL/None as
allowed), so provider_pools.fallback_pool_id cannot reference a non-existent
pool and the operation remains atomic.

In `@clients/rook/src/db/route.rs`:
- Around line 180-197: The current two-step check (the referencing query using
referencing and id_str) then DELETE (result) is vulnerable to a race where a
concurrent insert sets fallback_route_id between the checks; change to a single
conditional delete inside the same DB operation: perform a DELETE FROM
model_routes WHERE id = ? AND NOT EXISTS (SELECT 1 FROM model_routes WHERE
fallback_route_id = ?) (using self.pool() and binding id_str twice), then
inspect the affected rows to decide whether to return success, return
RookError::Registry indicating "referenced by route ..." (by re-querying the
referencing route only if rows_affected == 0 and the route still exists), or
return not-found if the route itself does not exist—replace the separate
referencing lookup and delete (the referencing variable and subsequent
query("DELETE ...")) with this atomic conditional delete and the follow-up
disambiguation logic.

In `@clients/rook/src/server/mod.rs`:
- Around line 61-80: The admin router is being exposed unguarded in
build_app_with_registry via .nest("/api", admin::build_router(registry)), which
makes unauthenticated mutating endpoints reachable; either restrict the admin
nest to loopback only or wrap it with auth middleware before adding it to the
Router. Update build_app_with_registry to (a) apply an authentication/middleware
layer around admin::build_router(registry) (e.g., using tower/axum middleware)
or (b) remove the admin nest from this externally bound Router and instead serve
admin::build_router(registry) on a loopback-only listener (split admin/gateway
listeners), ensuring the change touches the Router construction where
.nest("/api", ...) is invoked.

In `@clients/rook/src/services/pool.rs`:
- Around line 188-197: The update(&self, pool: ProviderPool) implementation
deletes then reinserts which turns unknown IDs into creates and risks data loss
on failure; change update to run inside a DB transaction on self.db.pool()
(begin/commit/rollback), first SELECT FOR UPDATE or query provider_pools WHERE
id = ? to verify existence, return a not-found RookError if absent, then perform
an UPDATE (or an upsert) of the existing row (or delete+insert inside the same
transaction) and only commit if all steps succeed; reference the update
function, ProviderPool type, existing sqlx::query("DELETE FROM provider_pools
WHERE id = ?") usage and self.db.insert_pool to locate where to introduce the
transaction and existence-check logic.
🪄 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: 662a7daf-365a-444f-8bd9-4db08d7d3069

📥 Commits

Reviewing files that changed from the base of the PR and between c53c38c and 727c212.

📒 Files selected for processing (30)
  • clients/rook/src/admin/handlers.rs
  • clients/rook/src/admin/mod.rs
  • clients/rook/src/admin/types.rs
  • clients/rook/src/dashboard/mod.rs
  • clients/rook/src/db/account.rs
  • clients/rook/src/db/mod.rs
  • clients/rook/src/db/pool.rs
  • clients/rook/src/db/route.rs
  • clients/rook/src/db/settings.rs
  • clients/rook/src/domain/mod.rs
  • clients/rook/src/gateway/handlers.rs
  • clients/rook/src/gateway/mod.rs
  • clients/rook/src/gateway/types.rs
  • clients/rook/src/gateway/upstream.rs
  • clients/rook/src/gateway/vendor.rs
  • clients/rook/src/lib.rs
  • clients/rook/src/registry/mod.rs
  • clients/rook/src/routing/mod.rs
  • clients/rook/src/server/mod.rs
  • clients/rook/src/services/account.rs
  • clients/rook/src/services/health.rs
  • clients/rook/src/services/pool.rs
  • clients/rook/src/services/route.rs
  • clients/rook/src/services/settings.rs
  • openspec/changes/rook-590-admin-api/design.md
  • openspec/changes/rook-590-admin-api/proposal.md
  • openspec/changes/rook-590-admin-api/spec.md
  • openspec/changes/rook-590-admin-api/state.yaml
  • openspec/changes/rook-590-admin-api/tasks.md
  • openspec/changes/rook-590-admin-api/verify-report.md
📜 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). (1)
  • 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/rook/src/lib.rs
  • clients/rook/src/gateway/types.rs
  • clients/rook/src/gateway/vendor.rs
  • clients/rook/src/gateway/mod.rs
  • clients/rook/src/services/settings.rs
  • clients/rook/src/services/health.rs
  • clients/rook/src/services/account.rs
  • clients/rook/src/db/mod.rs
  • clients/rook/src/db/account.rs
  • clients/rook/src/registry/mod.rs
  • clients/rook/src/server/mod.rs
  • clients/rook/src/db/route.rs
  • clients/rook/src/db/settings.rs
  • clients/rook/src/dashboard/mod.rs
  • clients/rook/src/gateway/handlers.rs
  • clients/rook/src/db/pool.rs
  • clients/rook/src/services/route.rs
  • clients/rook/src/routing/mod.rs
  • clients/rook/src/services/pool.rs
  • clients/rook/src/domain/mod.rs
  • clients/rook/src/admin/mod.rs
  • clients/rook/src/gateway/upstream.rs
  • clients/rook/src/admin/handlers.rs
  • clients/rook/src/admin/types.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/rook/src/lib.rs
  • clients/rook/src/gateway/types.rs
  • clients/rook/src/gateway/vendor.rs
  • clients/rook/src/gateway/mod.rs
  • openspec/changes/rook-590-admin-api/state.yaml
  • clients/rook/src/services/settings.rs
  • clients/rook/src/services/health.rs
  • clients/rook/src/services/account.rs
  • clients/rook/src/db/mod.rs
  • clients/rook/src/db/account.rs
  • clients/rook/src/registry/mod.rs
  • clients/rook/src/server/mod.rs
  • clients/rook/src/db/route.rs
  • clients/rook/src/db/settings.rs
  • clients/rook/src/dashboard/mod.rs
  • clients/rook/src/gateway/handlers.rs
  • clients/rook/src/db/pool.rs
  • openspec/changes/rook-590-admin-api/verify-report.md
  • clients/rook/src/services/route.rs
  • clients/rook/src/routing/mod.rs
  • openspec/changes/rook-590-admin-api/design.md
  • openspec/changes/rook-590-admin-api/proposal.md
  • clients/rook/src/services/pool.rs
  • openspec/changes/rook-590-admin-api/tasks.md
  • clients/rook/src/domain/mod.rs
  • openspec/changes/rook-590-admin-api/spec.md
  • clients/rook/src/admin/mod.rs
  • clients/rook/src/gateway/upstream.rs
  • clients/rook/src/admin/handlers.rs
  • clients/rook/src/admin/types.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:

  • openspec/changes/rook-590-admin-api/verify-report.md
  • openspec/changes/rook-590-admin-api/design.md
  • openspec/changes/rook-590-admin-api/proposal.md
  • openspec/changes/rook-590-admin-api/tasks.md
  • openspec/changes/rook-590-admin-api/spec.md
🧠 Learnings (6)
📚 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/rook/src/gateway/vendor.rs
  • clients/rook/src/db/mod.rs
  • openspec/changes/rook-590-admin-api/verify-report.md
  • clients/rook/src/domain/mod.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/rook/src/gateway/mod.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/rook/src/gateway/mod.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/rook/src/db/mod.rs
  • clients/rook/src/db/route.rs
  • clients/rook/src/db/pool.rs
  • clients/rook/src/services/route.rs
  • clients/rook/src/routing/mod.rs
  • clients/rook/src/domain/mod.rs
  • clients/rook/src/gateway/upstream.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/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/rook/src/registry/mod.rs
  • clients/rook/src/server/mod.rs
  • clients/rook/src/routing/mod.rs
  • clients/rook/src/services/pool.rs
  • clients/rook/src/domain/mod.rs
  • clients/rook/src/admin/mod.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/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/rook/src/server/mod.rs
  • clients/rook/src/gateway/upstream.rs
🪛 LanguageTool
openspec/changes/rook-590-admin-api/design.md

[style] ~245-~245: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...| clients/rook/src/services/pool.rs | Maybe modify | Only if needed to tighten not-...

(REP_MAYBE)


[style] ~246-~246: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ... clients/rook/src/services/route.rs | Maybe modify | Only if needed to preserve sta...

(REP_MAYBE)


[style] ~246-~246: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...ices/route.rs` | Maybe modify | Only if needed to preserve stable not-found/conflict mapp...

(REP_NEED_TO_VB)

openspec/changes/rook-590-admin-api/proposal.md

[style] ~28-~28: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ..., get, update, and delete operations. - Expose pool membership endpoints for adding an...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~29-~29: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...emoving provider accounts from pools. - Expose route management endpoints for create, ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~30-~30: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ..., get, update, and delete operations. - Expose health read endpoints for account-level...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~31-~31: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... records and aggregate summary views. - Expose settings read and update endpoints. - A...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

openspec/changes/rook-590-admin-api/spec.md

[style] ~32-~32: Consider removing “of” to be more concise
Context: ...ok. The composed server MUST preserve all of the following at the same time: - GET /ap...

(ALL_OF_THE)

🔇 Additional comments (11)
clients/rook/src/gateway/vendor.rs (1)

51-54: Looks good — test-only formatting change with no behavior impact.
These assertion reflows preserve semantics and improve readability.

Also applies to: 61-64, 69-72

clients/rook/src/routing/mod.rs (2)

45-809: LGTM: Clean formatting pass.

The import reorganization, struct literal alignment, argument wrapping, and test assertion formatting improve readability without changing behavior.


813-813: Nice improvement: idiomatic range check.

Using (24..=32).contains(&heavy_count) is more idiomatic Rust than the manual >= and <= comparison. Clearer intent.

clients/rook/src/gateway/mod.rs (1)

14-14: No issues in module reorder.
This is a non-functional ordering change and does not affect gateway behavior.

clients/rook/src/gateway/types.rs (1)

281-284: Assertion reformat is safe.
No behavioral change in error-shape validation.

clients/rook/src/services/health.rs (1)

107-109: Health-service edits are behavior-preserving.
These are readability-only refactors; lock/error handling and health updates remain intact.

Also applies to: 128-130, 149-151, 156-156

clients/rook/src/dashboard/mod.rs (1)

13-13: Dashboard changes look safe.
Only formatting/import normalization; no functional impact.

Also applies to: 16-16, 92-95

clients/rook/src/domain/mod.rs (1)

371-390: Test-formatting updates are fine.
No behavior changes to vendor parsing or serialization coverage.

Also applies to: 436-437

clients/rook/src/db/settings.rs (1)

147-155: Settings test refactor is correct.
Struct-literal initialization keeps the same assertions and improves clarity.

Also applies to: 170-173, 176-179

clients/rook/src/services/settings.rs (1)

48-51: No concerns in settings-service changes.
Behavior is preserved in both in-memory save path and test setup updates.

Also applies to: 103-108, 122-125, 128-131

clients/rook/src/registry/mod.rs (1)

26-27: Registry edits are non-functional and safe.
No break in service composition or settings round-trip intent.

Also applies to: 114-115, 153-156

Comment thread clients/rook/src/admin/handlers.rs
Comment thread clients/rook/src/admin/handlers.rs
Comment thread clients/rook/src/admin/mod.rs
Comment thread clients/rook/src/services/account.rs Outdated
Comment thread openspec/changes/rook-590-admin-api/design.md Outdated
Comment thread openspec/changes/rook-590-admin-api/proposal.md
Comment thread openspec/changes/rook-590-admin-api/spec.md
Comment thread openspec/changes/rook-590-admin-api/spec.md Outdated
Comment thread openspec/changes/rook-590-admin-api/verify-report.md Outdated
Comment thread openspec/changes/rook-590-admin-api/verify-report.md
Wrap admin extractor failures in the documented error envelope, avoid leaking internal error details, make account and route deletes safer under references, validate pool fallbacks transactionally, and align Rook admin docs with the verified contract.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 21, 2026

Deploying corvus with  Cloudflare Pages  Cloudflare Pages

Latest commit: b20252a
Status: ✅  Deploy successful!
Preview URL: https://26459d05.corvus-42x.pages.dev
Branch Preview URL: https://feat-rook-590-admin-api.corvus-42x.pages.dev

View logs

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

Labels

area:docs area:rust risk:security size/xl Denotes an extra large change size

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant