1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use serde::{Deserialize, Serialize};
fn default_require_auth_by_default() -> bool {
true
}
fn default_body_limit_bytes() -> usize {
16 * 1024 * 1024
}
/// API gateway configuration - reused from `api_gateway` module
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(deny_unknown_fields)]
#[allow(clippy::struct_excessive_bools)]
pub struct ApiGatewayConfig {
pub bind_addr: String,
#[serde(default)]
pub enable_docs: bool,
#[serde(default)]
pub cors_enabled: bool,
/// Optional detailed CORS configuration
#[serde(skip_serializing_if = "Option::is_none")]
pub cors: Option<CorsConfig>,
/// `OpenAPI` document metadata
#[serde(default)]
pub openapi: OpenApiConfig,
/// Global defaults
#[serde(default)]
pub defaults: Defaults,
/// Disable authentication and authorization completely.
/// When true, middleware automatically injects a default `SecurityContext` for all requests,
/// providing access with no tenant filtering.
/// This bypasses all tenant isolation and should only be used for single-user on-premise installations.
/// Default: false (authentication required via `AuthN` Resolver).
#[serde(default)]
pub auth_disabled: bool,
/// If true, routes without explicit security requirement still require authentication (AuthN-only).
#[serde(default = "default_require_auth_by_default")]
pub require_auth_by_default: bool,
/// Optional URL path prefix prepended to every route (e.g. `"/cf"` → `/cf/users`).
/// Must start with a leading slash; trailing slashes are stripped automatically.
/// Empty string (the default) means no prefix.
#[serde(default)]
pub prefix_path: String,
/// HTTP metrics settings.
#[serde(default)]
pub metrics: MetricsConfig,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct Defaults {
/// Fallback rate limit when operation does not specify one
pub rate_limit: RateLimitDefaults,
/// Global request body size limit in bytes
pub body_limit_bytes: usize,
}
impl Default for Defaults {
fn default() -> Self {
Self {
rate_limit: RateLimitDefaults::default(),
body_limit_bytes: default_body_limit_bytes(),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct RateLimitDefaults {
pub rps: u32,
pub burst: u32,
pub in_flight: u32,
}
impl Default for RateLimitDefaults {
fn default() -> Self {
Self {
rps: 50,
burst: 100,
in_flight: 64,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct CorsConfig {
/// Allowed origins: `["*"]` means any
pub allowed_origins: Vec<String>,
/// Allowed HTTP methods, e.g. `["GET","POST","OPTIONS","PUT","DELETE","PATCH"]`
pub allowed_methods: Vec<String>,
/// Allowed request headers; `["*"]` means any
pub allowed_headers: Vec<String>,
/// Whether to allow credentials
pub allow_credentials: bool,
/// Max age for preflight caching in seconds
pub max_age_seconds: u64,
}
impl Default for CorsConfig {
fn default() -> Self {
Self {
allowed_origins: vec!["*".to_owned()],
allowed_methods: vec![
"GET".to_owned(),
"POST".to_owned(),
"PUT".to_owned(),
"PATCH".to_owned(),
"DELETE".to_owned(),
"OPTIONS".to_owned(),
],
allowed_headers: vec!["*".to_owned()],
allow_credentials: false,
max_age_seconds: 600,
}
}
}
/// HTTP metrics configuration.
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(deny_unknown_fields, default)]
pub struct MetricsConfig {
/// Optional prefix for HTTP metrics instrument names.
///
/// When set, metric names become `{prefix}.http.server.request.duration`
/// and `{prefix}.http.server.active_requests` instead of the default
/// OpenTelemetry semantic convention names.
///
/// Empty string (the default) means no prefix — standard `OTel` names are used.
pub prefix: String,
}
/// `OpenAPI` document metadata configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct OpenApiConfig {
/// API title shown in `OpenAPI` documentation
pub title: String,
/// API version
pub version: String,
/// API description (optional)
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
impl Default for OpenApiConfig {
fn default() -> Self {
Self {
title: "API Documentation".to_owned(),
version: "0.1.0".to_owned(),
description: None,
}
}
}