//! Shared server state and configuration for the `HttpAdapter` router. use std::path::PathBuf; use std::sync::Arc; use alkcall::core::auth::IdentityProvider; use alkcall::registry::registration::OperationRegistry; /// The stealth decoy surface for paths that are not registered /// operations and not part of the reserved default surface (the 6 /// gateway endpoints, `/healthz`, `/openapi.json`, the MCP route, the /// WS upgrade path). Set by the assembly layer at `HttpAdapter` /// construction. The existence of the decoy path is fixed by ADR-010; /// the variant is a two-way-door config default. #[derive(Clone, Default, Debug)] pub enum DecoyConfig { /// Serve a fake `404 Not Found` (the default — a fake nginx 404). #[default] NotFound, /// Serve a static site from a configured directory. StaticSite { root: PathBuf }, /// Redirect to a configured URL. Redirect { to: String }, } /// State embedded in the axum `Router`: the registry and identity /// provider every request handler reaches through the router state, plus /// the decoy config for the fallback. #[derive(Clone)] pub(crate) struct RouterState { pub(crate) registry: Arc, pub(crate) identity_provider: Arc, pub(crate) decoy: DecoyConfig, } impl axum::extract::FromRef for DecoyConfig { fn from_ref(state: &RouterState) -> Self { state.decoy.clone() } } impl axum::extract::FromRef for Arc { fn from_ref(state: &RouterState) -> Self { Arc::clone(&state.registry) } } impl axum::extract::FromRef for Arc { fn from_ref(state: &RouterState) -> Self { Arc::clone(&state.identity_provider) } } #[cfg(test)] mod tests { use super::*; #[test] fn decoy_config_default_is_not_found() { assert!(matches!(DecoyConfig::default(), DecoyConfig::NotFound)); } #[test] fn router_state_from_ref_extracts_decoy() { let state = RouterState { registry: Arc::new(OperationRegistry::new()), identity_provider: Arc::new(NoopProvider), decoy: DecoyConfig::Redirect { to: "https://example.com".to_string(), }, }; let extracted: DecoyConfig = axum::extract::FromRef::from_ref(&state); assert!(matches!(extracted, DecoyConfig::Redirect { .. })); } struct NoopProvider; impl IdentityProvider for NoopProvider { fn resolve_from_fingerprint(&self, _: &str) -> Option { None } fn resolve_from_token( &self, _: &alkcall::core::auth::AuthToken, ) -> Option { None } } }