Skip to main content

systemprompt_models/profile/
rate_limits.rs

1//! Rate limits configuration.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use serde::{Deserialize, Serialize};
7
8/// Per-route-group request budgets, in requests per second.
9///
10/// Each `*_per_second` field governs one route group:
11///
12/// - `oauth_public` — `/api/v1/core/oauth` public endpoints,
13///   `/auth/link-passkey`, and the public gateway session routes under
14///   `/api/public/gateway`.
15/// - `oauth_auth` — the authenticated half of `/api/v1/core/oauth`, plus
16///   `/api/v1/core/users`.
17/// - `contexts` — `/api/v1/core/contexts` and the inbound `/api/v1/webhook`
18///   mount.
19/// - `tasks` — `/api/v1/core/tasks`.
20/// - `artifacts` — `/api/v1/core/artifacts`.
21/// - `agent_registry` — `/api/v1/agents/registry`, discovery only, kept apart
22///   from `agents_per_second` so listing agents cannot spend an execution
23///   budget.
24/// - `agents` — `/api/v1/agents` execution, and the Slack and Teams inbound
25///   mounts that dispatch to an agent.
26/// - `mcp_registry` — `/api/v1/mcp/registry`, the MCP server catalog, discovery
27///   only.
28/// - `mcp` — `/api/v1/mcp` tool calls, the busiest protocol route group.
29/// - `stream` — `/api/v1/stream`, long-lived SSE connections, so this is a
30///   connection budget rather than a request one and is set high.
31/// - `content` — `/api/v1/content`, `/api/v1/sync`, `/api/v1/marketplace`,
32///   `/api/v1/analytics` and `/track/engagement`.
33/// - `gateway` — inference traffic under `/v1`: `/v1/messages` and the
34///   OpenAI-shaped `/v1/chat/completions`.
35/// - `bridge_auth` — `/v1/auth/bridge/*`, deliberately separate from
36///   `gateway_per_second`: sign-in is low-volume and must stay reachable on an
37///   instance whose inference traffic is saturating its own budget. Sharing one
38///   bucket let a busy gateway lock every user out of authenticating.
39///
40/// `per_second_budgets` projects those fields as a list; both validators
41/// iterate it rather than naming the fields themselves, which had already let
42/// them drift apart and let a zero in an unnamed field through.
43#[derive(Debug, Clone, Copy, Serialize, Deserialize, schemars::JsonSchema)]
44#[serde(deny_unknown_fields)]
45pub struct RateLimitsConfig {
46    #[serde(default)]
47    pub disabled: bool,
48    #[serde(default = "default_oauth_public")]
49    pub oauth_public_per_second: u64,
50    #[serde(default = "default_oauth_auth")]
51    pub oauth_auth_per_second: u64,
52    #[serde(default = "default_contexts")]
53    pub contexts_per_second: u64,
54    #[serde(default = "default_tasks")]
55    pub tasks_per_second: u64,
56    #[serde(default = "default_artifacts")]
57    pub artifacts_per_second: u64,
58    #[serde(default = "default_agent_registry")]
59    pub agent_registry_per_second: u64,
60    #[serde(default = "default_agents")]
61    pub agents_per_second: u64,
62    #[serde(default = "default_mcp_registry")]
63    pub mcp_registry_per_second: u64,
64    #[serde(default = "default_mcp")]
65    pub mcp_per_second: u64,
66    #[serde(default = "default_stream")]
67    pub stream_per_second: u64,
68    #[serde(default = "default_content")]
69    pub content_per_second: u64,
70    #[serde(default = "default_gateway")]
71    pub gateway_per_second: u64,
72    #[serde(default = "default_bridge_auth")]
73    pub bridge_auth_per_second: u64,
74
75    #[serde(default = "default_burst")]
76    pub burst_multiplier: u64,
77}
78
79pub const fn default_oauth_public() -> u64 {
80    10
81}
82pub const fn default_oauth_auth() -> u64 {
83    10
84}
85pub const fn default_contexts() -> u64 {
86    100
87}
88pub const fn default_tasks() -> u64 {
89    50
90}
91pub const fn default_artifacts() -> u64 {
92    50
93}
94pub const fn default_agent_registry() -> u64 {
95    50
96}
97pub const fn default_agents() -> u64 {
98    20
99}
100pub const fn default_mcp_registry() -> u64 {
101    50
102}
103pub const fn default_mcp() -> u64 {
104    200
105}
106pub const fn default_stream() -> u64 {
107    100
108}
109pub const fn default_content() -> u64 {
110    50
111}
112pub const fn default_gateway() -> u64 {
113    100
114}
115pub const fn default_bridge_auth() -> u64 {
116    20
117}
118pub const fn default_burst() -> u64 {
119    3
120}
121
122impl RateLimitsConfig {
123    #[must_use]
124    pub const fn per_second_budgets(&self) -> [(&'static str, u64); 13] {
125        [
126            ("oauth_public_per_second", self.oauth_public_per_second),
127            ("oauth_auth_per_second", self.oauth_auth_per_second),
128            ("contexts_per_second", self.contexts_per_second),
129            ("tasks_per_second", self.tasks_per_second),
130            ("artifacts_per_second", self.artifacts_per_second),
131            ("agent_registry_per_second", self.agent_registry_per_second),
132            ("agents_per_second", self.agents_per_second),
133            ("mcp_registry_per_second", self.mcp_registry_per_second),
134            ("mcp_per_second", self.mcp_per_second),
135            ("stream_per_second", self.stream_per_second),
136            ("content_per_second", self.content_per_second),
137            ("gateway_per_second", self.gateway_per_second),
138            ("bridge_auth_per_second", self.bridge_auth_per_second),
139        ]
140    }
141
142    #[must_use]
143    pub fn production() -> Self {
144        Self::default()
145    }
146
147    #[must_use]
148    pub const fn testing() -> Self {
149        Self {
150            disabled: false,
151            oauth_public_per_second: 10000,
152            oauth_auth_per_second: 10000,
153            contexts_per_second: 10000,
154            tasks_per_second: 10000,
155            artifacts_per_second: 10000,
156            agent_registry_per_second: 10000,
157            agents_per_second: 10000,
158            mcp_registry_per_second: 10000,
159            mcp_per_second: 10000,
160            stream_per_second: 10000,
161            content_per_second: 10000,
162            gateway_per_second: 10000,
163            bridge_auth_per_second: 10000,
164            burst_multiplier: 100,
165        }
166    }
167
168    #[must_use]
169    pub const fn disabled() -> Self {
170        let mut config = Self::testing();
171        config.disabled = true;
172        config
173    }
174}
175
176impl Default for RateLimitsConfig {
177    fn default() -> Self {
178        Self {
179            disabled: false,
180            oauth_public_per_second: default_oauth_public(),
181            oauth_auth_per_second: default_oauth_auth(),
182            contexts_per_second: default_contexts(),
183            tasks_per_second: default_tasks(),
184            artifacts_per_second: default_artifacts(),
185            agent_registry_per_second: default_agent_registry(),
186            agents_per_second: default_agents(),
187            mcp_registry_per_second: default_mcp_registry(),
188            mcp_per_second: default_mcp(),
189            stream_per_second: default_stream(),
190            content_per_second: default_content(),
191            gateway_per_second: default_gateway(),
192            bridge_auth_per_second: default_bridge_auth(),
193            burst_multiplier: default_burst(),
194        }
195    }
196}