//! Ideonomics: one binary, three tokio task groups (docs/07) — //! (1) the axum server, (2) Jetstream consumer + gloss worker, //! (3) the slow-loop scheduler. A vertical monolith whose loops are decoupled //! by design, so splitting into services later is a deployment change. mod atproto; mod bank; mod config; mod db; mod gloss; mod graph; mod infer; mod ingest; mod render; mod state; mod web; use std::sync::Arc; use state::AppState; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "ideonomics=info,warn".into()), ) .init(); let config = config::Config::from_env(); let pool = db::open(&config.db_path).await?; bank::seed_if_empty(&pool).await?; let authstore = atproto::authstore::SqliteAuthStore::new(pool.clone()); let oauth = atproto::oauth_client(&config, authstore); let publisher = atproto::publisher::Publisher::connect( config.app_handle.as_deref(), config.app_password.as_deref(), ) .await?; let ensemble = gloss::build_ensemble(&config); let embedder = gloss::embed::Embedder::from_config(&config); let state = Arc::new(AppState { config: config.clone(), pool, oauth, publisher, ensemble, embedder, gloss_notify: tokio::sync::Notify::new(), }); // Task group 2: ingestion + gloss worker (medium loop). tokio::spawn(ingest::jetstream::run(state.clone())); tokio::spawn(gloss::worker::run(state.clone())); // Task group 3: slow-loop scheduler. tokio::spawn(infer::slow::run(state.clone())); // Task group 1: the server. let app = web::router(state.clone()) .layer(tower_http::trace::TraceLayer::new_for_http()); let listener = tokio::net::TcpListener::bind(&config.bind).await?; tracing::info!("ideonomics listening on http://{} (public: {})", config.bind, config.public_url); axum::serve(listener, app).await?; Ok(()) }