shuttle-rs 2026.6.9

Local-first event log CLI for agent memory, repository context, task coordination, handoffs, messaging, mesh sync, and MCP access.
Documentation
use std::net::SocketAddr;
use std::path::PathBuf;
use std::time::Duration;

use anyhow::{Context, Result};
use clap::{Parser, Subcommand};

#[derive(Debug, Parser)]
#[command(name = "shuttle-gateway")]
struct Cli {
    #[command(subcommand)]
    command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
    #[command(name = "version")]
    ShowVersion,
    Serve {
        #[arg(long)]
        config: PathBuf,
        #[arg(long)]
        addr: Option<SocketAddr>,
        #[arg(long, default_value = "stl")]
        stl: PathBuf,
        #[arg(long, default_value = "10")]
        timeout: u64,
    },
}

fn main() -> Result<()> {
    let cli = Cli::parse();
    match cli.command {
        Command::ShowVersion => {
            println!("{}", env!("CARGO_PKG_VERSION"));
        }
        Command::Serve {
            config,
            addr,
            stl,
            timeout,
        } => {
            let cfg = shuttle_rs::gateway::GatewayConfig::load(&config)
                .with_context(|| format!("failed to load {}", config.display()))?;
            if addr.is_some() && cfg.listeners.len() > 1 {
                anyhow::bail!("--addr cannot override a config with multiple listeners");
            }
            let explicit_addr = addr;
            let runtime = tokio::runtime::Runtime::new()?;
            let mut listeners = shuttle_rs::gateway::GatewayRuntime::listeners_from_config(
                cfg,
                stl,
                Duration::from_secs(timeout),
            )?;
            if let Some(addr) = explicit_addr {
                if let Some(listener) = listeners.first_mut() {
                    listener.addr = addr;
                }
            }
            runtime.block_on(shuttle_rs::gateway::serve_listeners(listeners))?;
        }
    }
    Ok(())
}