Rate limit the AI Catalog enable and disable mutations per user

What does this MR do and why?

Adds a per-user rate limit of 30 requests per minute to the aiCatalogItemConsumerCreate and aiCatalogItemConsumerDelete GraphQL mutations, which enable and disable an agent or flow in a project or top-level group.

  • Registers ai_catalog_item_consumer_create and ai_catalog_item_consumer_delete in the Labkit rate limit registry (30 per minute, scoped by user).
  • Each mutation checks the limit after authorization and before any write. Over the limit, it returns an error in the mutation response (You have enabled items too many times. Try again in a minute. / You have disabled items too many times. Try again in a minute.) and logs the throttled request to the auth log, so hits can be counted per user after rollout.
  • Extracts the check into a Mutations::Ai::Catalog::RateLimitable concern. The existing aiCatalogItemConsumerBulkCreate mutation, rate limited in !265715 (merged), is switched onto the concern with no behaviour change, so all three mutations share one implementation.
  • Documents the limits in the GraphQL reference, the custom agent and flow pages, and the non-configurable rate limits page.

The 30 per minute value follows the sibling bulk edit limits. Both UI callers surface the mutation errors to the user and neither loops over the mutation, so normal use is unaffected.

References

  • !265715 (merged) added the same style of limit to the bulk enable mutation.
  • !266134 (merged) adds the bulk enable row to doc/rate_limits/non_configurable.md. Whichever of the two merges second gets a one-line adjacent conflict in that table.

How to validate locally

  1. Create a personal access token with the api scope for a user who is Owner of a group with a project, and find a public agent in the AI Catalog that has a released version.

  2. In GraphiQL (/-/graphql-explorer) or with curl, run the create mutation 31 times within a minute, each time against a different project in the group (or the same project, the duplicate error still counts):

    mutation($i: AiCatalogItemID!, $p: ProjectID!) {
      aiCatalogItemConsumerCreate(input: { itemId: $i, target: { projectId: $p } }) {
        itemConsumer { id }
        errors
      }
    }

    Calls 1 to 30 behave as before. Call 31 returns itemConsumer: null and the error You have enabled items too many times. Try again in a minute., and no item consumer is created.

  3. After a minute, run the delete mutation 31 times against existing item consumers:

    mutation($c: AiCatalogItemConsumerID!) {
      aiCatalogItemConsumerDelete(input: { id: $c }) { success errors }
    }

    Calls 1 to 30 delete. Call 31 returns success: false with You have disabled items too many times. Try again in a minute. and the item consumer remains.

  4. log/auth_json.log has one Application_Rate_Limiter_Request entry per limit with env set to ai_catalog_item_consumer_create_request_limit or ai_catalog_item_consumer_delete_request_limit, plus the user id, username, IP, method and path.

Verified this way on GDK against 31 fresh projects: creates 1 to 30 succeeded and 31 was rejected; deletes 1 to 30 succeeded and 31 was rejected; one auth log entry per limit.

Rollout

  • Kibana, auth_json subcomponent: filter json.env: ai_catalog_item_consumer_create_request_limit or json.env: ai_catalog_item_consumer_delete_request_limit, unique count on json.user_id.
  • Prometheus: gitlab_labkit_rate_limiter_rule_evaluations_total{rule="limit_ai_catalog_item_consumer_create_by_user", result="exceeded"} and the same for limit_ai_catalog_item_consumer_delete_by_user.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Merge request reports

Loading
Loading