hennery_kernel/capabilities.rs
1//! `GET /api/capabilities` (kernel spec §8; plan 4b): which mode this
2//! collector runs in, and which features it has switched on. The web UI
3//! shows the views of its mode (frontend spec §2) and hides a screen whose
4//! feature is off.
5//!
6//! Behind the session cookie: the shell asks for it first, so a 401 here is
7//! what sends a signed-out browser to the login screen. The login and setup
8//! screens never ask, so they render in either mode.
9
10use crate::operator::Operator;
11use axum::routing::get;
12use axum::{Json, Router};
13use hennery_proto::rest::{CapabilitiesResponse, DeploymentMode};
14use std::sync::Arc;
15
16/// The one place the mode is decided. Always the whole cockpit until the
17/// standalone gateway (plan 8g) adds its switch here.
18pub fn mode() -> DeploymentMode {
19 DeploymentMode::Full
20}
21
22/// The one place features are switched on: each gateway part adds its name
23/// when its API lands (`mcp_connections`, `mcp_stdio`, `mcp_oauth`,
24/// `mcp_clients`). Plan 8a's connections API (`/api/mcp/connections`) is
25/// served, alongside stdio servers and plan 8f's OAuth (`…/authorize`, the callback, the
26/// pre-registered client, the probe).
27pub fn features() -> Vec<String> {
28 vec![
29 "mcp_connections".to_owned(),
30 "mcp_stdio".to_owned(),
31 "mcp_oauth".to_owned(),
32 "web_push".to_owned(),
33 "session_defaults".to_owned(),
34 ]
35}
36
37/// What `GET /api/capabilities` answers.
38pub fn current() -> CapabilitiesResponse {
39 CapabilitiesResponse {
40 mode: mode(),
41 features: features(),
42 }
43}
44
45/// The route, behind the browser rules and the session cookie.
46pub fn router(operator: Arc<Operator>) -> Router {
47 crate::auth::operator_only(
48 Router::new().route("/api/capabilities", get(|| async { Json(current()) })),
49 operator,
50 )
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn the_collector_is_the_whole_cockpit_with_the_gateways_connections() {
59 assert_eq!(
60 serde_json::to_value(current()).unwrap(),
61 serde_json::json!({"mode": "full", "features": ["mcp_connections", "mcp_stdio", "mcp_oauth", "web_push", "session_defaults"]})
62 );
63 }
64
65 /// The other mode, as the web UI reads it (`lib/views.ts`).
66 #[test]
67 fn the_gateway_mode_is_named_gateway() {
68 assert_eq!(
69 serde_json::to_value(DeploymentMode::Gateway).unwrap(),
70 serde_json::json!("gateway")
71 );
72 }
73}