Skip to content

Add optional per-agent request rate limiting to REST and MCP transports #122

Description

@se-jo-ma

Context

The broker enforces cumulative-exposure session limits, but there is no request-rate limiter on either inbound transport. A grep -rniE '429|Retry-After|rate.?limit|RateLimit|TooManyRequests|slowapi|token.?bucket' over nautilus/transport/ returns nothing.

Confirmed against the code:

  • POST /v1/request (nautilus/transport/fastapi_app.py:274) accepts unbounded request volume.
  • The nautilus_request MCP tool (nautilus/transport/mcp_server.py:232) is equally unbounded.
  • ApiConfig (nautilus/config/models.py:294) exposes only host, port, keys — no rate-limit field.

The roadmap (design-docs/05-ecosystem-roadmap.md:516,587) specifies only write-side capability gates (max_writes_per_minute / max_invocations_per_minute) as future work, not a read-path limiter. (Note: the rate_limit: 100/hour in design-docs/04-architecture-operations.md:216 is a per-source outbound throttle on the threat_intel REST source — a different concern, not an inbound limiter.)

Impact: a single runaway agent can flood POST /v1/request, exhausting adapter pools and inflating the append-only audit log (audit.jsonl is already ~1.3MB) with no backstop, and starving other agents.

Task

Add an optional rate limiter (fixed-window or token-bucket) keyed by agent_id (optionally by API key/source), off by default.

  • Add a rate_limit field to ApiConfig (requests-per-window, window, key strategy). Absent config ⇒ no limiting.
  • Enforce in the FastAPI POST /v1/request path (and its /v1/query alias) returning HTTP 429 + Retry-After.
  • Mirror the limit in the MCP nautilus_request tool, returning a structured throttle error.
  • Emit a rate_limited audit event via the existing sparse-event path (AuditLogger.emit_event, nautilus/audit/logger.py:186) so throttling is observable.

Design decisions to settle before implementation (hence needs-design): in-memory vs shared/distributed bucket (multi-worker uvicorn), exact key strategy (agent vs api_key vs both), and the MCP error shape.

Where

  • nautilus/transport/fastapi_app.py (request handler ~_handle_request/post_request, lines 252-298)
  • nautilus/transport/mcp_server.py (nautilus_request tool, line 232)
  • nautilus/config/models.py (ApiConfig, line 294)
  • nautilus/audit/logger.py (emit_event, line 186) for the audit event

Acceptance criteria

  • No rate_limit config ⇒ no limiting; existing behavior unchanged (covered by a -m unit test).
  • Exceeding the configured window for an agent ⇒ HTTP 429 + Retry-After on REST, structured throttle error on MCP.
  • A rate_limited audit entry is written when a request is throttled.
  • Limiting is per-agent: a noisy agent does not starve others (test with two agent_ids).
  • uv run ruff check, ruff format --check, and pyright pass; new code covered by -m unit tests.

Size: M

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priority: v2 epic or non-blocking gaparea/auditAudit log, attestations, JWSarea/coreBroker core, CLIPS routing, session stateenhancementNew feature or requestneeds-designDesign not yet settled — please don't open a PR yetperformancePerformance-relatedsize/M<2 days: multi-file feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions