Skip to content

Inbound rate limits

The hub rate-limits its expensive endpoints — the ones that fan out to an LLM — per tenant, in a fixed 60-second window. Everything else (CRUD, views, ingest) is unlimited.

What is limited

Bucket Endpoint Default rpm
view-generate POST /api/views/generate 10
platform-chat POST /api/platform/rag/chat 60
user-chat POST /api/rag/chat 60
platform-search POST /api/platform/rag/search 60

view-generate gets its own tighter limit because one call = one full LLM manifest-drafting round. The other three fall back to lumnik.ratelimit.default.rpm (60).

Counting is per (bucket, tenant): one tenant hammering chat cannot exhaust another tenant's budget, and the buckets are independent — burning user-chat does not touch platform-search. Requests with no resolved tenant count under a shared system key.

The 429 contract

When a window is exhausted the request is refused before the resource runs:

HTTP/1.1 429 Too Many Requests
Retry-After: <seconds until the window resets>

{"error":"rate limit exceeded"}

Retry-After is exact, not advisory — the window is a fixed 60s clock, so waiting that many seconds is guaranteed to land in a fresh window. lm surfaces this verbatim — rate limited (429): rate limit exceeded, retry in Ns — rather than a generic error.

Tuning

Property form (per bucket, falling back to default):

lumnik.ratelimit.default.rpm=60
lumnik.ratelimit.view-generate.rpm=10

Env form for a deployed container — MicroProfile mapping, non-alphanumerics become underscores:

LUMNIK_RATELIMIT_DEFAULT_RPM=120
LUMNIK_RATELIMIT_VIEW_GENERATE_RPM=20
LUMNIK_RATELIMIT_USER_CHAT_RPM=0    # 0 disables that bucket entirely

0 disables — no counting, no 429s for that bucket. Restart the hub after changing either form; limits are read per request from config, but env vars only reach the process at boot.

Dev mode has NO limits

%dev sets both knobs to 0 — a local mvn quarkus:dev never returns 429, no matter how hard you hit it. If you are trying to reproduce a customer's 429 locally, you must set the properties back to a non-zero value; out of the box you can't. (%test inherits the prod defaults, which is why the filter is testable.)

Single-node caveat

The counters live in memory in the hub process. Both shipped deploy paths (selfhost compose, Helm chart) run a single hub replica, so today this is exact. If you ever scale the hub to N replicas behind a balancer, each replica counts independently — the effective limit becomes up to N× the configured rpm. That is a known, deliberate trade (no Redis dependency for a single-replica product); revisit it when a multi-replica deploy is real, not before.

Counters also reset on every hub restart — a restart forgives the current window.

Not to be confused: the OTHER 429

The quickstart's troubleshooting table mentions 429 Too Many Requests from the vendor — that one is a different animal. It is the source system (Stripe, Siebel, whatever your REST connector pulls from) throttling lumnik's outbound calls during a connector run; you tune it with default_rate_limit in the connector YAML, not with any lumnik.ratelimit.* knob. This page's 429 is the hub throttling inbound API callers (chat, search, view generation). Same status code, opposite directions: one protects the vendor from lumnik, the other protects lumnik from its own users.