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 ]
34}
35
36/// What `GET /api/capabilities` answers.
37pub fn current() -> CapabilitiesResponse {
38 CapabilitiesResponse {
39 mode: mode(),
40 features: features(),
41 }
42}
43
44/// The route, behind the browser rules and the session cookie.
45pub fn router(operator: Arc<Operator>) -> Router {
46 crate::auth::operator_only(
47 Router::new().route("/api/capabilities", get(|| async { Json(current()) })),
48 operator,
49 )
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn the_collector_is_the_whole_cockpit_with_the_gateways_connections() {
58 assert_eq!(
59 serde_json::to_value(current()).unwrap(),
60 serde_json::json!({"mode": "full", "features": ["mcp_connections", "mcp_stdio", "mcp_oauth", "web_push"]})
61 );
62 }
63
64 /// The other mode, as the web UI reads it (`lib/views.ts`).
65 #[test]
66 fn the_gateway_mode_is_named_gateway() {
67 assert_eq!(
68 serde_json::to_value(DeploymentMode::Gateway).unwrap(),
69 serde_json::json!("gateway")
70 );
71 }
72}