2026-01-24 22:59:20 +00:00
|
|
|
use crate::api::ApiHandler;
|
2026-01-27 23:21:26 +00:00
|
|
|
use crate::config::{Config, ContainerRuntime};
|
2026-02-17 15:03:34 +00:00
|
|
|
use crate::container::{docker_packages, DockerPackageScanner};
|
|
|
|
|
use crate::identity::{self, NodeIdentity};
|
|
|
|
|
use crate::nostr_discovery;
|
2026-01-27 23:06:18 +00:00
|
|
|
use crate::state::StateManager;
|
2026-01-24 22:59:20 +00:00
|
|
|
use anyhow::Result;
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
use hyper::server::conn::Http;
|
|
|
|
|
use hyper::service::service_fn;
|
2026-01-24 22:59:20 +00:00
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
use std::sync::Arc;
|
2026-01-27 23:21:26 +00:00
|
|
|
use std::time::Duration;
|
2026-01-24 22:59:20 +00:00
|
|
|
use tokio::net::TcpListener;
|
2026-02-01 13:24:03 +00:00
|
|
|
use tracing::{debug, error, info};
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
pub struct Server {
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
_config: Config,
|
2026-02-17 15:03:34 +00:00
|
|
|
_identity: Arc<NodeIdentity>,
|
2026-01-24 22:59:20 +00:00
|
|
|
api_handler: Arc<ApiHandler>,
|
2026-02-01 05:42:05 +00:00
|
|
|
_state_manager: Arc<StateManager>,
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Server {
|
|
|
|
|
pub async fn new(config: Config) -> Result<Self> {
|
2026-01-27 23:06:18 +00:00
|
|
|
let state_manager = Arc::new(StateManager::new());
|
2026-02-17 15:03:34 +00:00
|
|
|
|
|
|
|
|
// Load node identity and set stable server_info
|
|
|
|
|
let identity_dir = config.data_dir.join("identity");
|
|
|
|
|
let identity = NodeIdentity::load_or_create(&identity_dir).await?;
|
|
|
|
|
let (mut data, _) = state_manager.get_snapshot().await;
|
|
|
|
|
data.server_info.id = identity.node_id();
|
|
|
|
|
data.server_info.pubkey = identity.pubkey_hex();
|
|
|
|
|
data.server_info.tor_address = docker_packages::read_tor_address("archipelago");
|
|
|
|
|
if let Some(ref tor) = data.server_info.tor_address {
|
|
|
|
|
data.server_info.node_address = Some(identity.node_address(tor));
|
|
|
|
|
}
|
|
|
|
|
state_manager.update_data(data.clone()).await;
|
|
|
|
|
|
|
|
|
|
// Revoke any previously published Nostr data (runs before publish so revocation is not overwritten)
|
|
|
|
|
let identity_dir = config.data_dir.join("identity");
|
|
|
|
|
let tor_proxy_revoke = config.nostr_tor_proxy.clone();
|
|
|
|
|
if let Err(e) = nostr_discovery::revoke_if_needed(&identity_dir, tor_proxy_revoke.as_deref()).await {
|
|
|
|
|
tracing::debug!("Nostr revoke (non-fatal): {}", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Publish node identity to Nostr only when opt-in (nostr_discovery_enabled + relays)
|
|
|
|
|
if config.nostr_discovery_enabled
|
|
|
|
|
&& !config.nostr_relays.is_empty()
|
|
|
|
|
&& data.server_info.node_address.is_some()
|
|
|
|
|
{
|
|
|
|
|
let identity_dir = config.data_dir.join("identity");
|
|
|
|
|
let did = identity::did_key_from_pubkey_hex(&data.server_info.pubkey).unwrap_or_default();
|
|
|
|
|
let node_addr = data.server_info.node_address.clone().unwrap_or_default();
|
|
|
|
|
let version = data.server_info.version.clone();
|
|
|
|
|
let relays = config.nostr_relays.clone();
|
|
|
|
|
let tor_proxy = config.nostr_tor_proxy.clone();
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
if let Err(e) = nostr_discovery::publish_node_identity(
|
|
|
|
|
&identity_dir,
|
|
|
|
|
&did,
|
|
|
|
|
&node_addr,
|
|
|
|
|
&version,
|
|
|
|
|
&relays,
|
|
|
|
|
tor_proxy.as_deref(),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
tracing::debug!("Nostr publish (non-fatal): {}", e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
info!("🔑 Node identity: {} (pubkey: {}...)", identity.node_id(), &identity.pubkey_hex()[..16.min(identity.pubkey_hex().len())]);
|
|
|
|
|
|
|
|
|
|
let identity = Arc::new(identity);
|
2026-01-27 23:21:26 +00:00
|
|
|
let api_handler = Arc::new(ApiHandler::new(config.clone(), state_manager.clone()).await?);
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
// Periodic Tor address refresh (runs regardless of dev_mode)
|
|
|
|
|
// Picks up hostname when Tor creates it after startup/rotation (30-60s delay)
|
|
|
|
|
{
|
|
|
|
|
let state = state_manager.clone();
|
|
|
|
|
let identity_clone = identity.clone();
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(30));
|
|
|
|
|
loop {
|
|
|
|
|
interval.tick().await;
|
|
|
|
|
if let Err(e) = refresh_tor_address(&state, identity_clone.as_ref()).await {
|
|
|
|
|
debug!("Tor address refresh (non-fatal): {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
// Initialize Docker scanner if in dev mode
|
|
|
|
|
if config.dev_mode {
|
|
|
|
|
let scanner = create_docker_scanner(&config).await?;
|
|
|
|
|
let state = state_manager.clone();
|
2026-02-17 15:03:34 +00:00
|
|
|
let identity_clone = identity.clone();
|
2026-01-27 23:21:26 +00:00
|
|
|
|
|
|
|
|
// Initial scan
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
info!("🐳 Scanning Docker containers...");
|
2026-02-17 15:03:34 +00:00
|
|
|
if let Err(e) = scan_and_update_packages(&scanner, &state, identity_clone.as_ref()).await {
|
2026-01-27 23:21:26 +00:00
|
|
|
error!("Failed to scan Docker containers: {}", e);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 13:24:03 +00:00
|
|
|
// Periodic scan every 10 seconds (only broadcasts if state changed)
|
|
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(10));
|
2026-01-27 23:21:26 +00:00
|
|
|
loop {
|
|
|
|
|
interval.tick().await;
|
2026-02-17 15:03:34 +00:00
|
|
|
if let Err(e) = scan_and_update_packages(&scanner, &state, identity_clone.as_ref()).await {
|
2026-01-27 23:21:26 +00:00
|
|
|
error!("Failed to update Docker containers: {}", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
Ok(Self {
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
_config: config,
|
2026-02-17 15:03:34 +00:00
|
|
|
_identity: identity,
|
2026-01-24 22:59:20 +00:00
|
|
|
api_handler,
|
2026-02-01 05:42:05 +00:00
|
|
|
_state_manager: state_manager,
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn serve(&self, addr: SocketAddr) -> Result<()> {
|
|
|
|
|
let listener = TcpListener::bind(addr).await?;
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let (stream, peer_addr) = match listener.accept().await {
|
|
|
|
|
Ok(conn) => conn,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
error!("Failed to accept connection: {}", e);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let handler = self.api_handler.clone();
|
|
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
let service = service_fn(move |req| {
|
|
|
|
|
let handler = handler.clone();
|
|
|
|
|
async move {
|
|
|
|
|
handler.handle_request(req).await
|
2026-01-24 23:09:46 +00:00
|
|
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("{}", e)))
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
Update archipelago: API, auth, container, parmanode, performance, security
- API handler, RPC, and server updates
- Auth and coding rules
- Container data manager, dev orchestrator, health monitor, podman client
- Parmanode script runner
- Performance resource manager
- Security container policies and secrets manager
- Add build scripts and documentation
2026-01-27 22:27:17 +00:00
|
|
|
if let Err(e) = Http::new()
|
|
|
|
|
.serve_connection(stream, service)
|
2026-01-27 22:47:51 +00:00
|
|
|
.with_upgrades()
|
2026-01-24 22:59:20 +00:00
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
error!("Error serving connection from {}: {}", peer_addr, e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-27 23:21:26 +00:00
|
|
|
|
|
|
|
|
async fn create_docker_scanner(config: &Config) -> Result<DockerPackageScanner> {
|
|
|
|
|
let user = std::env::var("USER").unwrap_or_else(|_| "archipelago".to_string());
|
|
|
|
|
|
|
|
|
|
let runtime: Arc<dyn archipelago_container::ContainerRuntime> = match &config.container_runtime {
|
|
|
|
|
ContainerRuntime::Podman => {
|
|
|
|
|
Arc::new(archipelago_container::PodmanRuntime::new(user.clone()))
|
|
|
|
|
}
|
|
|
|
|
ContainerRuntime::Docker => {
|
|
|
|
|
Arc::new(archipelago_container::DockerRuntime::new(user.clone()))
|
|
|
|
|
}
|
|
|
|
|
ContainerRuntime::Auto => {
|
|
|
|
|
Arc::new(
|
|
|
|
|
archipelago_container::AutoRuntime::new(user.clone())
|
|
|
|
|
.await?
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(DockerPackageScanner::new(runtime))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
async fn refresh_tor_address(state: &StateManager, identity: &NodeIdentity) -> Result<()> {
|
|
|
|
|
let tor_addr = docker_packages::read_tor_address("archipelago");
|
|
|
|
|
let (current_data, _) = state.get_snapshot().await;
|
|
|
|
|
if tor_addr != current_data.server_info.tor_address {
|
|
|
|
|
let mut data = current_data;
|
|
|
|
|
data.server_info.tor_address = tor_addr.clone();
|
|
|
|
|
data.server_info.node_address = tor_addr.as_ref().map(|t| identity.node_address(t));
|
|
|
|
|
state.update_data(data).await;
|
|
|
|
|
if let Some(ref addr) = tor_addr {
|
|
|
|
|
info!("🔒 Tor address updated: {}", addr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-27 23:21:26 +00:00
|
|
|
async fn scan_and_update_packages(
|
|
|
|
|
scanner: &DockerPackageScanner,
|
|
|
|
|
state: &StateManager,
|
2026-02-17 15:03:34 +00:00
|
|
|
identity: &NodeIdentity,
|
2026-01-27 23:21:26 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
|
let packages = scanner.scan_containers().await?;
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
let (current_data, _) = state.get_snapshot().await;
|
|
|
|
|
let packages_changed = !packages.is_empty() && current_data.package_data != packages;
|
|
|
|
|
let tor_addr = docker_packages::read_tor_address("archipelago");
|
|
|
|
|
let tor_changed = tor_addr != current_data.server_info.tor_address;
|
|
|
|
|
|
|
|
|
|
if packages_changed || tor_changed {
|
|
|
|
|
let mut data = current_data;
|
|
|
|
|
if !packages.is_empty() {
|
2026-02-01 13:24:03 +00:00
|
|
|
data.package_data = packages;
|
|
|
|
|
}
|
2026-02-17 15:03:34 +00:00
|
|
|
data.server_info.tor_address = tor_addr.clone();
|
|
|
|
|
data.server_info.node_address = tor_addr.as_ref().map(|t| identity.node_address(t));
|
|
|
|
|
state.update_data(data).await;
|
|
|
|
|
debug!("📦 State changed (packages={}, tor={}), broadcasting update", packages_changed, tor_changed);
|
2026-01-27 23:21:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|