use std::net::SocketAddr;
use std::sync::Arc;
use axum::Router;
use thiserror::Error;
use tracing::info;
#[cfg(feature = "image-trust")]
use crate::artifacts::ArtifactVerifyError;
use crate::http::RouterBuilder;
#[cfg(feature = "kwt")]
use crate::kwt_access::KwtEnvError;
use crate::platform;
use crate::state::AppState;
use crate::surfaces::{Http1Forbidden, ListenEndpoints, SurfaceConfig, SurfaceConfigError, load_from_env};
use crate::telemetry;
use crate::tls::InboundTls;
#[cfg(feature = "filter")]
use crate::filter::{FilterParseError, FilterStore};
#[derive(Debug, Error)]
pub enum StartupError {
#[cfg(feature = "image-trust")]
#[error(transparent)]
Artifact(#[from] ArtifactVerifyError),
#[cfg(feature = "kwt")]
#[error(transparent)]
Kwt(#[from] KwtEnvError),
#[cfg(feature = "kwt")]
#[error(transparent)]
AetherRuntime(#[from] crate::kwt_replay::RuntimeError),
#[cfg(feature = "filter")]
#[error(transparent)]
Filter(#[from] FilterParseError),
#[error("rustls crypto provider already installed")]
CryptoProvider,
#[cfg(feature = "filter-control")]
#[error(transparent)]
Bus(#[from] crate::bus::BusError),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Tls(#[from] crate::tls::TlsConfigError),
#[error(transparent)]
Http1(#[from] Http1Forbidden),
#[error(transparent)]
Surfaces(#[from] SurfaceConfigError),
#[error(transparent)]
Security(#[from] crate::security::SecurityError),
#[error("at least one of HTTP/2 or HTTP/3 must be enabled")]
NoHttpListener,
}
pub fn state_from_env() -> Result<AppState, StartupError> {
#[cfg(feature = "image-trust")]
let attestation = crate::artifacts::verify_on_startup_from_env()?;
#[cfg(feature = "kwt")]
let aether_runtime = std::sync::Arc::new(crate::kwt_replay::AetherRuntime::bootstrap_from_env()?);
#[cfg(feature = "kwt")]
let kwt = crate::kwt_access::kwt_access_with_store(aether_runtime.jti_store())?;
#[cfg(feature = "filter")]
let filter = match crate::filter::policy_from_env()? {
Some(p) => FilterStore::new(p),
None => FilterStore::default(),
};
let state = AppState::new(
#[cfg(feature = "image-trust")]
attestation,
#[cfg(feature = "kwt")]
kwt,
#[cfg(feature = "kwt")]
Some(aether_runtime),
#[cfg(feature = "filter")]
filter,
);
install_env_subsystems(&state);
Ok(state)
}
pub fn install_env_subsystems(state: &AppState) {
crate::embed::install_subsystems_from_env(state);
}
#[must_use]
pub fn probe_addr_from_env() -> SocketAddr {
let port = listen_addr_from_env().port();
SocketAddr::from(([127, 0, 0, 1], port))
}
#[must_use]
pub fn listen_addr_from_env() -> SocketAddr {
SurfaceConfig::from_env().listen.h2
}
pub async fn warm_up_substrate() {
if let Err(e) = crate::dns::install_from_env() {
tracing::warn!(%e, "DNS install failed");
}
let ((), (), (), ()) = tokio::join!(
async { crate::time_info::log_sample_timezone() },
crate::dns::log_sample_resolution(),
async {
#[cfg(feature = "kwt")]
{
if let Err(e) = crate::aether_tunnel::warm_up() {
tracing::warn!(%e, "AetherDB wire tunnel warm-up failed");
}
crate::kwt_replay::warm_up_wire_dns().await;
}
#[cfg(not(feature = "kwt"))]
crate::dns::log_aether_connect_resolution().await;
},
crate::outbound::log_https_smoke(),
);
}
pub async fn serve(
endpoints: ListenEndpoints,
tls: Arc<InboundTls>,
router: Router,
#[cfg(feature = "filter")] filter: FilterStore,
) -> Result<(), StartupError> {
if !endpoints.h2_enabled && !endpoints.h3_enabled {
return Err(StartupError::NoHttpListener);
}
let require_client_auth = crate::tls::client_auth_required_from_env();
let h2_config = tls.h2_config_store();
let acme_challenge = tls.acme().map(crate::tls::AcmeHandle::challenge_config);
let quinn_config = tls.quinn_config();
let h2 = async {
if endpoints.h2_enabled {
crate::http_transport::serve_h2_tls(
endpoints.h2,
h2_config,
acme_challenge,
router.clone(),
require_client_auth,
)
.await
} else {
std::future::pending().await
}
};
let h3 = async {
if endpoints.h3_enabled {
crate::http_transport::serve_h3_quic(
endpoints.h3,
quinn_config.clone(),
require_client_auth,
router.clone(),
#[cfg(feature = "filter")]
filter.clone(),
)
.await
} else {
std::future::pending().await
}
};
#[cfg(feature = "kwt")]
let h3_wt = async {
if endpoints.h3_enabled {
if let Some(addr) = crate::wt_aether::dedicated_listen_from_env() {
if crate::webtransport::is_enabled() {
tracing::info!(%addr, "listening (h3/quic dedicated WebTransport)");
crate::http_transport::serve_h3_quic(
addr,
quinn_config.clone(),
require_client_auth,
router.clone(),
#[cfg(feature = "filter")]
filter.clone(),
)
.await
} else {
std::future::pending().await
}
} else {
std::future::pending().await
}
} else {
std::future::pending().await
}
};
#[cfg(not(feature = "kwt"))]
let h3_wt = async { std::future::pending().await };
tokio::try_join!(h2, h3, h3_wt)?;
info!("shutdown complete");
Ok(())
}
pub async fn run_default() -> Result<(), StartupError> {
run(RouterBuilder::new(state_from_env()?)).await
}
pub async fn run(builder: RouterBuilder) -> Result<(), StartupError> {
crate::surfaces::reject_http1_env()?;
platform::install_defaults().map_err(|e| {
tracing::error!(?e, "rustls crypto provider install failed");
StartupError::CryptoProvider
})?;
telemetry::init_tracing();
crate::security::init()?;
warm_up_substrate().await;
let surfaces = load_from_env()?;
crate::surfaces::log_startup(&surfaces);
let tls = Arc::new(InboundTls::from_env()?);
tls.clone().install_global();
if surfaces.tls_reload {
spawn_tls_reload_on_sighup(Arc::clone(&tls));
}
#[cfg(feature = "filter-control")]
crate::bus::start_control_plane(builder.state()).await?;
crate::security::validate_wt_aether_startup()?;
if !crate::tls::configured_from_env() {
return Err(StartupError::Tls(crate::tls::TlsConfigError::Required));
}
if crate::tls::mtls_configured_from_env() {
let mode = if crate::tls::client_auth_required_from_env() {
"required"
} else {
"optional"
};
info!(client_auth = mode, "inbound mTLS enabled");
}
if crate::webtransport::is_enabled() && surfaces.listen.h3_enabled {
info!(
wt_paths = ?{
#[cfg(feature = "kwt")]
{
crate::wt_aether::all_session_paths()
}
#[cfg(not(feature = "kwt"))]
{
vec![crate::webtransport::path_from_env()]
}
},
"WebTransport enabled on HTTP/3"
);
#[cfg(feature = "kwt")]
if crate::wt_aether::config_snapshot().enabled {
info!(
aether = %crate::wt_aether::config_snapshot().status_kdl(),
"WebTransport Aether bridge active"
);
}
}
info!("inbound TLS 1.3 + HTTP/2 (tcp) and HTTP/3 (quic) on PORT");
#[cfg(feature = "grpc-surface")]
let acme_challenge = tls.acme().map(crate::tls::AcmeHandle::challenge_config);
#[cfg(feature = "grpc-surface")]
{
let require_client_auth = crate::tls::client_auth_required_from_env();
crate::surfaces::spawn_grpc(
&surfaces,
tls.h2_config_store(),
acme_challenge,
require_client_auth,
);
}
#[cfg(not(feature = "grpc-surface"))]
crate::surfaces::spawn_grpc(&surfaces);
#[cfg(feature = "filter")]
let filter = builder.state().filter.clone();
let router = builder.build(&surfaces);
#[cfg(feature = "filter")]
serve(surfaces.listen, tls, router, filter).await?;
#[cfg(not(feature = "filter"))]
serve(surfaces.listen, tls, router).await?;
Ok(())
}
#[cfg(unix)]
fn spawn_tls_reload_on_sighup(tls: Arc<InboundTls>) {
tokio::spawn(async move {
use tokio::signal::unix::{SignalKind, signal};
let mut sighup = match signal(SignalKind::hangup()) {
Ok(s) => s,
Err(err) => {
tracing::warn!(%err, "SIGHUP handler unavailable; TLS reload disabled");
return;
}
};
loop {
if sighup.recv().await.is_none() {
break;
}
if let Err(err) = tls.reload_from_env().await {
tracing::warn!(%err, "inbound TLS reload failed");
}
}
});
}
#[cfg(not(unix))]
fn spawn_tls_reload_on_sighup(_tls: Arc<InboundTls>) {}