Skip to content

feat(rook): harden gateway transport and chat delivery#632

Merged
yacosta738 merged 1 commit into
developfrom
feat/rook-transport-hardening
Apr 22, 2026
Merged

feat(rook): harden gateway transport and chat delivery#632
yacosta738 merged 1 commit into
developfrom
feat/rook-transport-hardening

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

Related Issues


Summary

This PR hardens the Rook gateway transport and chat delivery path as a cohesive Rook-only change set.

  • adds inbound auth enforcement for /api/* and /v1/*
  • adds transport middleware for request IDs, structured observability, and strict-by-default forwarded-header handling
  • adds global-by-surface rate limiting for /api/*, /v1/models, and /v1/chat/completions
  • adds chat-completions idempotency for meaningful replay protection on keyed non-streaming requests
  • adds OpenAI-compatible SSE streaming for POST /v1/chat/completions when stream: true
  • syncs and archives the corresponding OpenSpec changes and gateway spec updates

Tested Information

Targeted Rust tests were run across the implemented slices, including:

  • auth boundary tests for protected /api/* and /v1/* routes
  • transport middleware tests for request IDs, trusted-proxy policy, sanitized context, and redacted observability fields
  • global surface rate-limit tests for independent budgets, 429, Retry-After, and startup/config validation
  • chat-completions idempotency tests for canonicalization, replay, mismatch, in-progress conflict, unavailable store handling, and route scoping
  • chat-completions streaming tests for SSE framing, [DONE] termination, setup failure behavior, and mid-stream abort behavior

I also ran cargo test --manifest-path "clients/rook/Cargo.toml" during validation passes to confirm the Rook slice behavior in the broader crate context, while noting unrelated existing failures outside these slices where applicable.


Documentation Impact

  • Docs updated in:
    • openspec/specs/gateway/spec.md
    • openspec/changes/archive/2026-04-21-rook-589-gateway-api/state.yaml
    • openspec/changes/archive/2026-04-22-rook-591-inbound-auth-boundary/
    • openspec/changes/archive/2026-04-22-rook-591-transport-middleware-baseline/
    • openspec/changes/archive/2026-04-22-rook-591-global-surface-rate-limits/
    • openspec/changes/archive/2026-04-22-rook-591-chat-completions-idempotency/
    • openspec/changes/archive/2026-04-22-rook-591-chat-completions-streaming-transport/
  • No docs update required because: n/a
  • I verified the documentation matches the current behavior.

Breaking Changes

None intended. This PR expands Rook transport capabilities and constraints without intentionally changing unrelated public contracts outside the Rook gateway path.


Checklist

  • I have checked that there isn’t already a PR solving the same problem.
  • I have read the Contributing Guidelines and followed the project conventions.
  • I have included tests or explained why tests were not necessary.
  • I have updated documentation or confirmed it is not needed.
  • My commit messages follow the Conventional Commits format.
  • I verified the changes locally before opening this PR.

Add inbound auth, transport middleware, global surface rate limits, chat idempotency, and streaming support so Rook can protect and serve OpenAI-compatible chat traffic more reliably.
@github-actions github-actions Bot added the size/xl Denotes an extra large change size label Apr 22, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added inbound bearer token authentication for protected API endpoints.
    • Added request ID tracking and propagation across responses.
    • Added per-endpoint rate limiting with independent budgets.
    • Added idempotency and replay protection for chat completions.
    • Added Server-Sent Events (SSE) streaming support for chat completions.
  • Configuration

    • Added CLI flags for authentication, rate limits, and idempotency settings.
  • Database

    • Added migration for idempotency state persistence.

Walkthrough

This PR implements comprehensive transport-layer hardening for the Rook gateway across five coordinated slices: inbound bearer-token authentication, transport middleware baseline (request-ID and forwarded-header sanitization), per-surface rate limiting, chat-completions idempotency with replay protection, and SSE streaming support.

Changes

Cohort / File(s) Summary
Dependencies & Migrations
Cargo.toml, migrations/0004_chat_completions_idempotency.sql
Added futures-util, sha2, ipnet dependencies and new idempotency table schema with expiry indexing.
Inbound Auth Layer
src/auth/bearer.rs, src/auth/types.rs, src/auth/middleware.rs, src/auth/mod.rs
New bearer token extraction with strict Authorization header parsing, inbound auth validation, and dual admin/gateway middleware adapters that inject authenticated principals into request extensions.
Configuration Infrastructure
src/config/mod.rs, src/main.rs
Expanded ServerConfig with new InboundAuthConfig, TransportConfig, RateLimitConfig, IdempotencyConfig subtrees; added CLI flags for inbound auth, per-surface rate limits, and idempotency window; introduced build_server_config() helper.
Transport Middleware Baseline
src/transport/context.rs, src/transport/request_id.rs, src/transport/forwarded.rs, src/transport/middleware.rs, src/transport/mod.rs
New request-ID adoption/generation, strict-by-default forwarded-header trust with optional CIDR-based proxy allowlisting, sanitized transport context injection, and structured completion logging with secret redaction.
Rate Limiting
src/transport/rate_limit.rs
Per-surface fixed-window rate limiting for /api/*, /v1/models, /v1/chat/completions with independent budgets, deterministic Retry-After calculation, and surface-specific error response shapes.
Database & Idempotency Service
src/db/idempotency.rs, src/db/mod.rs, src/services/idempotency.rs, src/services/mod.rs
SQLite-backed idempotency persistence with pruning, reservation logic (new/replay-completed/replay-in-progress/key-mismatch), and trait-based IdempotencyService abstraction.
Idempotency Middleware & Canonicalization
src/idempotency/canonical.rs, src/idempotency/middleware.rs, src/idempotency/types.rs, src/idempotency/mod.rs
Deterministic JSON canonicalization + SHA-256 hashing, principal-scoped idempotency keys (1–255 visible ASCII), middleware orchestrating reserve→execute→complete flow with 409 conflict and 503 fail-closed handling.
Streaming Support
src/gateway/streaming.rs, src/gateway/upstream.rs, src/gateway/types.rs, src/gateway/handlers.rs, src/gateway/mod.rs
New SSE parser accumulating chunks across boundaries with validation/[DONE] sentinel enforcement, UpstreamStreamingResponse abstraction, handler branching for stream: true, and route-router refactoring via build_models_router()/build_chat_router().
Admin Response Helpers
src/admin/types.rs
New helpers admin_unauthorized_response() and admin_rate_limited_response() returning 401/429 with admin error envelope and appropriate headers.
Gateway Response Helpers
src/gateway/types.rs
New helpers for gateway_unauthorized_response(), gateway_rate_limited_response(), gateway_idempotency_error_response() plus constants IDEMPOTENCY_REPLAYED_HEADER, STREAM_CONTENT_TYPE, STREAM_DONE_SENTINEL.
Server Composition
src/server/mod.rs, src/registry/mod.rs, src/lib.rs
Extended ServerConfig fields; added startup validation for auth/transport/rate-limits/idempotency; layered middleware in strict order (inbound auth → transport baseline → rate limit → idempotency for chat); added SharedIdempotencyService to registry.
OpenSpec Documentation
openspec/changes/archive/2026-04-22-rook-591-*/*, openspec/specs/gateway/spec.md
Comprehensive design/proposal/spec/task/verify docs for all five slices; updated main gateway spec with new R27–R52 requirements covering auth boundary, transport baseline, rate limiting, idempotency, and streaming.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant InboundAuth as Inbound Auth<br/>Middleware
    participant Transport as Transport<br/>Middleware
    participant RateLimit as Rate Limit<br/>Middleware
    participant Idempotency as Idempotency<br/>Middleware
    participant Handler as Chat Handler
    participant Upstream as Upstream<br/>Provider
    participant DB as SQLite DB

    Client->>InboundAuth: POST /v1/chat/completions<br/>Authorization: Bearer token
    InboundAuth->>InboundAuth: Extract & validate bearer
    alt Auth fails
        InboundAuth-->>Client: 401 Unauthorized
    end
    InboundAuth->>Transport: Inject AuthenticatedPrincipal
    Transport->>Transport: Resolve request ID<br/>Sanitize forwarded headers
    Transport->>RateLimit: Inject SanitizedTransportContext
    RateLimit->>RateLimit: Check surface budget
    alt Rate limit exceeded
        RateLimit-->>Client: 429 Too Many Requests<br/>Retry-After
    end
    RateLimit->>Idempotency: Route to handler
    Idempotency->>Idempotency: Extract Idempotency-Key
    Idempotency->>Idempotency: Canonicalize JSON body<br/>Compute SHA-256 hash
    Idempotency->>DB: Reserve (check scope,<br/>key, hash match)
    alt Key reused with<br/>different body
        DB-->>Idempotency: KeyReusedMismatch
        Idempotency-->>Client: 409 Conflict
    end
    alt Request in progress
        DB-->>Idempotency: ReplayInProgress
        Idempotency-->>Client: 409 Conflict
    end
    alt Completed replay found
        DB-->>Idempotency: ReplayCompleted{response}
        Idempotency-->>Client: Stored response<br/>+ Idempotency-Replayed
    end
    Idempotency->>Handler: New reservation created
    Handler->>Handler: Check stream flag
    alt stream: true
        Handler->>Upstream: open_chat_completion_stream()
        Upstream-->>Handler: UpstreamStreamingResponse
        Handler->>Handler: Parse upstream SSE<br/>Emit data: chunks
        Handler-->>Client: 200 text/event-stream<br/>Streamed [DONE]
    else stream: false/absent
        Handler->>Upstream: proxy_chat_completion()
        Upstream-->>Handler: Full buffered response
        Handler-->>Client: 200 application/json
    end
    Handler->>DB: Complete idempotency<br/>(status, body, timestamp)
    Transport->>Transport: Emit completion log<br/>request_id, status, duration
Loading
sequenceDiagram
    participant Req as Request<br/>with Idempotency-Key
    participant Middleware as Idempotency<br/>Middleware
    participant Service as Idempotency<br/>Service
    participant DB as SQLite DB
    participant Handler as Handler

    Req->>Middleware: Read & validate key
    alt Invalid key
        Middleware-->>Req: 400 Bad Request
    end
    Middleware->>Middleware: Read body bytes<br/>Canonicalize JSON
    Middleware->>Service: reserve_chat_completion()
    Service->>DB: SELECT BY scope+key+method+path
    alt Row exists
        DB->>Service: Existing record
        alt Hash matches & completed
            Service-->>Middleware: ReplayCompleted
            Middleware->>DB: Select response<br/>status/body/content-type
            Middleware-->>Req: Stored response<br/>+ Idempotency-Replayed: true
        else Hash differs
            Service-->>Middleware: KeyReusedMismatch
            Middleware-->>Req: 409 Conflict
        else Status in-progress
            Service-->>Middleware: ReplayInProgress
            Middleware-->>Req: 409 Conflict
        end
    else No row exists
        Service->>DB: INSERT in-progress<br/>started_at, expires_at
        Service-->>Middleware: ReservedNew
        Middleware->>Handler: Proceed with execution
        Handler->>Handler: Execute upstream call
        Handler->>Service: complete_chat_completion()<br/>response status/body
        Service->>DB: UPDATE to completed<br/>response data, completed_at
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Rationale: Five coordinated subsystems (auth, transport, rate limiting, idempotency, streaming) introduce substantial new logic with security-critical paths (bearer validation, CIDR matching, canonical JSON hashing, replay state machine). Heterogeneous changes span config validation, database schema, middleware composition ordering, error handling per surface, and SSE framing. Extensive test coverage and documentation mitigate scope, but interactions between auth→transport→rate-limit→idempotency→handler stacking and idempotency bypass for streaming require careful control-flow verification.

Possibly related PRs

Suggested labels

area:rust, area:docs, risk:security, risk:high

Suggested reviewers

  • yuniel-acosta
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rook-transport-hardening

@github-actions
Copy link
Copy Markdown
Contributor

✅ 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 585 >= 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 270 >= 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-22 to 2026-04-22

@yacosta738 yacosta738 merged commit aa7eeaa into develop Apr 22, 2026
7 of 8 checks passed
@yacosta738 yacosta738 deleted the feat/rook-transport-hardening branch April 22, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant