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}