Skip to main content

featherbit/config/
gateway.rs

1//! Schema for `gateway.yaml`: routes (match rules bound to a policy) and
2//! node-graph policies (nodes plus success/error edges). This file is the
3//! hot-reloadable half of the configuration and is also mutated at runtime
4//! by the Admin API.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Root of `gateway.yaml`: the route table and the policies it references.
10///
11/// Both sections default to empty, so a missing or minimal file is valid.
12///
13/// ```yaml
14/// routes:
15///   - name: api
16///     match: { path: /api/* }
17///     policy: api-policy
18/// policies:
19///   - name: api-policy
20///     nodes:
21///       - { id: in, type: listener }
22///       - { id: up, type: upstream, config: { url: "http://backend:3000" } }
23///     edges:
24///       - { from: in.out, to: up.in }
25/// ```
26#[derive(Debug, Deserialize, Serialize, Clone)]
27pub struct GatewayConfig {
28    /// Routes evaluated in declaration order; the first match wins.
29    #[serde(default)]
30    pub routes: Vec<RouteConfig>,
31    /// Policies referenced by name from `routes`.
32    #[serde(default)]
33    pub policies: Vec<PolicyConfig>,
34    /// Named API clients with per-auth-plugin credentials; resolved by auth
35    /// nodes configured with `use_consumers: true`.
36    #[serde(default)]
37    pub consumers: Vec<crate::consumers::ConsumerConfig>,
38}
39
40/// Binds a request match rule to a named policy.
41#[derive(Debug, Deserialize, Serialize, Clone)]
42pub struct RouteConfig {
43    /// Unique route name, used in logs, metrics labels, and the Admin API.
44    pub name: String,
45    /// Conditions a request must satisfy (YAML key: `match`).
46    #[serde(rename = "match")]
47    pub match_rule: MatchRule,
48    /// Name of the [`PolicyConfig`] to execute; must exist or compilation fails.
49    pub policy: String,
50}
51
52/// Request-matching conditions for a route.
53///
54/// All specified criteria must match (logical AND); every field defaults to
55/// unset/empty, which matches any request.
56#[derive(Debug, Deserialize, Serialize, Clone)]
57pub struct MatchRule {
58    /// Path pattern to match (e.g. `/api/*`); `None` matches any path.
59    #[serde(default)]
60    pub path: Option<String>,
61    /// Allowed HTTP methods; empty means any method.
62    #[serde(default)]
63    pub methods: Vec<String>,
64    /// Required header name → value pairs; empty means no header constraints.
65    #[serde(default)]
66    pub headers: HashMap<String, String>,
67    /// Required `Host` value; `None` matches any host.
68    #[serde(default)]
69    pub host: Option<String>,
70}
71
72/// A node-graph policy: a named pipeline of plugin nodes wired by edges.
73///
74/// Compiled into a `CompiledGraph` at load/reload time; execution starts at
75/// the `listener` node and follows each node's `success`/`error` ports.
76#[derive(Debug, Deserialize, Serialize, Clone)]
77pub struct PolicyConfig {
78    /// Unique policy name, referenced by [`RouteConfig::policy`].
79    pub name: String,
80    /// Optional id of a node to jump to when a node errors without an explicit error edge.
81    #[serde(default)]
82    pub error_handler: Option<String>,
83    /// Plugin nodes making up the pipeline.
84    #[serde(default)]
85    pub nodes: Vec<NodeConfig>,
86    /// Directed connections between node ports.
87    #[serde(default)]
88    pub edges: Vec<EdgeConfig>,
89}
90
91/// One plugin node in a policy graph.
92#[derive(Debug, Deserialize, Serialize, Clone)]
93pub struct NodeConfig {
94    /// Unique node id within the policy, referenced by edges as `id.port`.
95    pub id: String,
96    /// Plugin type (YAML key: `type`), e.g. `upstream`, `key-auth`, `script`;
97    /// must be one of the registered plugin types.
98    #[serde(rename = "type")]
99    pub node_type: String,
100    /// Free-form plugin-specific configuration; defaults to empty.
101    #[serde(default)]
102    pub config: HashMap<String, serde_json::Value>,
103    /// Canvas coordinates for the Web UI node editor; ignored by the engine
104    /// and omitted from serialized output when unset.
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub position: Option<Position>,
107}
108
109/// 2D canvas coordinates of a node in the Web UI graph editor.
110#[derive(Debug, Deserialize, Serialize, Clone)]
111pub struct Position {
112    pub x: f64,
113    pub y: f64,
114}
115
116/// A directed edge between two node ports, written as `node_id.port`.
117///
118/// Ports: `out`, `success`, `error` on the source side; `in` on the target
119/// side (e.g. `from: auth.success`, `to: upstream.in`).
120#[derive(Debug, Deserialize, Serialize, Clone)]
121pub struct EdgeConfig {
122    /// Source endpoint, `node_id.port`.
123    pub from: String,
124    /// Target endpoint, `node_id.port`.
125    pub to: String,
126}