Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ Data structures often include:

- When talking about "Rivet Actors" make sure to capitalize "Rivet Actor" as a proper noun and lowercase "actor" as a generic noun

### Documentation Sync
When making changes to the engine or RivetKit, ensure the corresponding documentation is updated:
- **Limits changes** (e.g., max message sizes, timeouts): Update `website/src/content/docs/actors/limits.mdx`
- **Config changes** (e.g., new config options in `engine/packages/config/`): Update `website/src/content/docs/self-hosting/configuration.mdx`
- **RivetKit config changes** (e.g., `rivetkit-typescript/packages/rivetkit/src/registry/config/index.ts` or `rivetkit-typescript/packages/rivetkit/src/actor/config.ts`): Update `website/src/content/docs/actors/limits.mdx` if they affect limits/timeouts

### Comments

- Write comments as normal, complete sentences. Avoid fragmented structures with parentheticals and dashes like `// Spawn engine (if configured) - regardless of start kind`. Instead, write `// Spawn the engine if configured`. Especially avoid dashes (hyphens are OK).
Expand Down
5 changes: 5 additions & 0 deletions engine/artifacts/errors/guard.request_body_too_large.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions engine/artifacts/errors/guard.response_body_too_large.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion engine/artifacts/openapi.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions engine/packages/config/src/config/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ pub struct Guard {
pub port: Option<u16>,
/// Enable & configure HTTPS
pub https: Option<Https>,
/// Route cache TTL in milliseconds.
pub route_cache_ttl_ms: Option<u64>,
/// Proxy state cache TTL in milliseconds.
pub proxy_state_cache_ttl_ms: Option<u64>,
/// Time to keep TCP connection open after WebSocket close, in milliseconds.
pub websocket_close_linger_ms: Option<u64>,
/// Max incoming WebSocket message size in bytes.
pub websocket_max_message_size: Option<usize>,
/// Max outgoing WebSocket message size in bytes.
pub websocket_max_outgoing_message_size: Option<usize>,
/// Max HTTP request body size in bytes (first line of defense).
pub http_max_request_body_size: Option<usize>,
}

impl Guard {
Expand All @@ -21,6 +33,31 @@ impl Guard {
pub fn port(&self) -> u16 {
self.port.unwrap_or(crate::defaults::ports::GUARD)
}

pub fn route_cache_ttl_ms(&self) -> u64 {
self.route_cache_ttl_ms.unwrap_or(10 * 60 * 1000) // 10 minutes
}

pub fn proxy_state_cache_ttl_ms(&self) -> u64 {
self.proxy_state_cache_ttl_ms.unwrap_or(60 * 60 * 1000) // 1 hour
}

pub fn websocket_close_linger_ms(&self) -> u64 {
self.websocket_close_linger_ms.unwrap_or(100)
}

pub fn websocket_max_message_size(&self) -> usize {
self.websocket_max_message_size.unwrap_or(32 * 1024 * 1024) // 32 MiB
}

pub fn websocket_max_outgoing_message_size(&self) -> usize {
self.websocket_max_outgoing_message_size
.unwrap_or(32 * 1024 * 1024) // 32 MiB
}

pub fn http_max_request_body_size(&self) -> usize {
self.http_max_request_body_size.unwrap_or(256 * 1024 * 1024) // 256 MiB
}
}

#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
Expand Down
197 changes: 197 additions & 0 deletions engine/packages/config/src/config/pegboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,76 @@ pub struct Pegboard {
///
/// **Experimental**
pub runner_pool_consecutive_successes_to_clear_error: Option<u32>,

// === Gateway Settings ===
/// WebSocket open/handshake timeout in milliseconds.
pub gateway_websocket_open_timeout_ms: Option<u64>,
/// Timeout for response to start in milliseconds.
pub gateway_response_start_timeout_ms: Option<u64>,
/// Ping interval for gateway updates in milliseconds.
pub gateway_update_ping_interval_ms: Option<u64>,
/// GC interval for in-flight requests in milliseconds.
pub gateway_gc_interval_ms: Option<u64>,
/// Tunnel ping timeout in milliseconds.
pub gateway_tunnel_ping_timeout_ms: Option<i64>,
/// Hibernating WebSocket message ack timeout in milliseconds.
pub gateway_hws_message_ack_timeout_ms: Option<u64>,
/// Max pending message buffer size for hibernating WebSockets in bytes.
pub gateway_hws_max_pending_size: Option<u64>,
/// Max HTTP request body size in bytes for requests to actors.
///
/// Note: guard-core also enforces a larger limit (default 256 MiB) as a first line of defense.
/// See `Guard::http_max_request_body_size`.
pub gateway_http_max_request_body_size: Option<usize>,
/// Rate limit: number of requests allowed per period.
pub gateway_rate_limit_requests: Option<u64>,
/// Rate limit: period in seconds.
pub gateway_rate_limit_period_secs: Option<u64>,
/// Maximum concurrent in-flight requests per actor per IP.
pub gateway_max_in_flight: Option<usize>,
/// HTTP request timeout in seconds for actor traffic.
///
/// This is the outer timeout for the entire request lifecycle.
/// Should be slightly longer than `gateway_response_start_timeout_ms` to provide a grace period.
pub gateway_actor_request_timeout_secs: Option<u64>,
/// HTTP request timeout in seconds for API traffic (api-public).
pub gateway_api_request_timeout_secs: Option<u64>,
/// Maximum retry attempts for failed requests.
pub gateway_retry_max_attempts: Option<u32>,
/// Initial retry interval in milliseconds (doubles with each attempt).
pub gateway_retry_initial_interval_ms: Option<u64>,
/// WebSocket proxy task timeout in seconds.
pub gateway_ws_proxy_timeout_secs: Option<u64>,
/// WebSocket connection attempt timeout in seconds.
pub gateway_ws_connect_timeout_secs: Option<u64>,
/// WebSocket send message timeout in seconds.
pub gateway_ws_send_timeout_secs: Option<u64>,
/// WebSocket flush timeout in seconds.
pub gateway_ws_flush_timeout_secs: Option<u64>,

// === API Settings ===
/// Rate limit for API traffic: number of requests allowed per period.
pub api_rate_limit_requests: Option<u64>,
/// Rate limit for API traffic: period in seconds.
pub api_rate_limit_period_secs: Option<u64>,
/// Maximum concurrent in-flight requests for API traffic.
pub api_max_in_flight: Option<usize>,
/// Maximum retry attempts for API traffic.
pub api_retry_max_attempts: Option<u32>,
/// Initial retry interval for API traffic in milliseconds.
pub api_retry_initial_interval_ms: Option<u64>,
/// Max HTTP request body size in bytes for API traffic.
pub api_max_http_request_body_size: Option<usize>,

// === Runner Settings ===
/// Max HTTP response body size in bytes from actors.
pub runner_http_max_response_body_size: Option<usize>,
/// Ping interval for runner updates in milliseconds.
pub runner_update_ping_interval_ms: Option<u64>,
/// GC interval for actor event demuxer in milliseconds.
pub runner_event_demuxer_gc_interval_ms: Option<u64>,
/// Max time since last seen before actor is considered stale, in milliseconds.
pub runner_event_demuxer_max_last_seen_ms: Option<u64>,
}

impl Pegboard {
Expand Down Expand Up @@ -139,4 +209,131 @@ impl Pegboard {
self.runner_pool_consecutive_successes_to_clear_error
.unwrap_or(3)
}

// === Gateway Settings ===

pub fn gateway_websocket_open_timeout_ms(&self) -> u64 {
self.gateway_websocket_open_timeout_ms.unwrap_or(15_000)
}

pub fn gateway_response_start_timeout_ms(&self) -> u64 {
self.gateway_response_start_timeout_ms
.unwrap_or(5 * 60 * 1000) // 5 minutes
}

pub fn gateway_update_ping_interval_ms(&self) -> u64 {
self.gateway_update_ping_interval_ms.unwrap_or(3_000)
}

pub fn gateway_gc_interval_ms(&self) -> u64 {
self.gateway_gc_interval_ms.unwrap_or(15_000)
}

pub fn gateway_tunnel_ping_timeout_ms(&self) -> i64 {
self.gateway_tunnel_ping_timeout_ms.unwrap_or(30_000)
}

pub fn gateway_hws_message_ack_timeout_ms(&self) -> u64 {
self.gateway_hws_message_ack_timeout_ms.unwrap_or(30_000)
}

pub fn gateway_hws_max_pending_size(&self) -> u64 {
self.gateway_hws_max_pending_size
.unwrap_or(128 * 1024 * 1024) // 128 MiB
}

pub fn gateway_http_max_request_body_size(&self) -> usize {
self.gateway_http_max_request_body_size
.unwrap_or(128 * 1024 * 1024) // 128 MiB
}

pub fn gateway_rate_limit_requests(&self) -> u64 {
self.gateway_rate_limit_requests.unwrap_or(1200)
}

pub fn gateway_rate_limit_period_secs(&self) -> u64 {
self.gateway_rate_limit_period_secs.unwrap_or(60)
}

pub fn gateway_max_in_flight(&self) -> usize {
self.gateway_max_in_flight.unwrap_or(32)
}

pub fn gateway_actor_request_timeout_secs(&self) -> u64 {
self.gateway_actor_request_timeout_secs.unwrap_or(6 * 60) // 6 minutes
}

pub fn gateway_api_request_timeout_secs(&self) -> u64 {
self.gateway_api_request_timeout_secs.unwrap_or(60) // 1 minute
}

pub fn gateway_retry_max_attempts(&self) -> u32 {
self.gateway_retry_max_attempts.unwrap_or(7)
}

pub fn gateway_retry_initial_interval_ms(&self) -> u64 {
self.gateway_retry_initial_interval_ms.unwrap_or(150)
}

pub fn gateway_ws_proxy_timeout_secs(&self) -> u64 {
self.gateway_ws_proxy_timeout_secs.unwrap_or(30)
}

pub fn gateway_ws_connect_timeout_secs(&self) -> u64 {
self.gateway_ws_connect_timeout_secs.unwrap_or(5)
}

pub fn gateway_ws_send_timeout_secs(&self) -> u64 {
self.gateway_ws_send_timeout_secs.unwrap_or(5)
}

pub fn gateway_ws_flush_timeout_secs(&self) -> u64 {
self.gateway_ws_flush_timeout_secs.unwrap_or(2)
}

// === API Settings ===

pub fn api_rate_limit_requests(&self) -> u64 {
self.api_rate_limit_requests.unwrap_or(1200)
}

pub fn api_rate_limit_period_secs(&self) -> u64 {
self.api_rate_limit_period_secs.unwrap_or(60)
}

pub fn api_max_in_flight(&self) -> usize {
self.api_max_in_flight.unwrap_or(32)
}

pub fn api_retry_max_attempts(&self) -> u32 {
self.api_retry_max_attempts.unwrap_or(3)
}

pub fn api_retry_initial_interval_ms(&self) -> u64 {
self.api_retry_initial_interval_ms.unwrap_or(100)
}

pub fn api_max_http_request_body_size(&self) -> usize {
self.api_max_http_request_body_size
.unwrap_or(256 * 1024 * 1024) // 256 MiB
}

// === Runner Settings ===

pub fn runner_http_max_response_body_size(&self) -> usize {
self.runner_http_max_response_body_size
.unwrap_or(128 * 1024 * 1024) // 128 MiB
}

pub fn runner_update_ping_interval_ms(&self) -> u64 {
self.runner_update_ping_interval_ms.unwrap_or(3_000)
}

pub fn runner_event_demuxer_gc_interval_ms(&self) -> u64 {
self.runner_event_demuxer_gc_interval_ms.unwrap_or(30_000)
}

pub fn runner_event_demuxer_max_last_seen_ms(&self) -> u64 {
self.runner_event_demuxer_max_last_seen_ms.unwrap_or(30_000)
}
}
12 changes: 12 additions & 0 deletions engine/packages/guard-core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,15 @@ pub struct WebSocketServiceTimeout;
"WebSocket target changed, retry not possible."
)]
pub struct WebSocketTargetChanged;

#[derive(RivetError, Serialize, Deserialize)]
#[error(
"guard",
"request_body_too_large",
"Request body too large.",
"Request body size {size} bytes exceeds maximum allowed {max_size} bytes."
)]
pub struct RequestBodyTooLarge {
pub size: usize,
pub max_size: usize,
}
Loading
Loading