Cameleeringsimoom

simoom / Use cases / CLI and desktop apps

Inside CLIs, desktop apps and daemons

A local HTTP endpoint without turning your program async

A command-line tool needs a callback for an OAuth login. A desktop app wants to show a live dashboard in the browser. A daemon needs a health and metrics endpoint. None of them should have to adopt an async runtime or a web framework to get there. With simoom you write plain functions, call start(), and keep the rest of your program as it is.

0async runtimes to set up: services are plain functions
3operating systems: Windows (IOCP), Linux (epoll, io_uring), macOS (kqueue)
0.94 msfrom process start to the first answer, default build on Linux; first of 21 servers in campaign 4
5.1 MBfor 1,000 idle connections on a Windows workstation, against 15.2 MB for the thread-per-connection server it replaced

How it sits in your program

start() runs the server on its own threads and returns a handle. Your main thread stays yours: keep running the UI, the CLI or the daemon, then stop() and join() when you are done.

Fast work on the loop

A service runs on the event loop thread and returns an Outcome. Reading state, building a small JSON answer or serving a page from memory is fast enough to do there.

Slow work elsewhere

Anything that waits (a file, a database, a network call) goes to the bounded offload pool with blocking, or to your own threads with Reply::defer. The loop never waits.

use simoom::{Outcome, Reply, Request, Response, Server};

let server = Server::builder()
    .listen("127.0.0.1:8080")
    .service(|req: &mut Request<'_>, reply: Reply<'_>| -> Outcome {
        let owned = req.to_owned_request();
        let responder = reply.defer();
        std::thread::spawn(move || {
            responder.send(Response::text(format!("done: {}", owned.path_str())));
        });
        Outcome::Done
    })
    .start()
    .unwrap();

Bind to 127.0.0.1 for an endpoint only the local machine can reach. More in services and replies and slow work.

Live updates to the browser

A dashboard needs pushes, not polling. simoom has WebSocket (also over HTTP/2 and HTTP/3, with permessage-deflate) and server-sent events, both without async code.

use simoom::{Reply, Request, Router, WebSocket, WebSocketHandler, WsMessage};

struct Echo;
impl WebSocketHandler for Echo {
    fn on_message(&mut self, ws: &mut WebSocket<'_>, msg: WsMessage<'_>) {
        match msg {
            WsMessage::Text(t) => ws.send_text(t),
            WsMessage::Binary(b) => ws.send_binary(b),
        }
    }
}

let router = Router::new()
    .get("/ws", |_: &mut Request<'_>, r: Reply<'_>| r.websocket(Echo));

Guides: WebSocket and streaming and server-sent events. Calling other services from the same program? simoom also has an HTTP client.

Windows, Linux and macOS

platformback endstatus
WindowsIOCPtest suite runs (777 tests); cold start and memory measured
Linuxepoll, io_uringtest suite runs (791 tests on WSL2); cloud benchmarks
macOSkqueuecompile-checked only

On a Windows 11 workstation, against the server it replaced (Cameleer's old thread-per-connection server), cold start was 8.04 ms against 9.19 ms, and 1,000 idle connections used 5.1 MB of private memory against 15.2 MB. Windows start-up is measured until the server prints that it is ready, because a connect attempt to a port nobody listens on yet takes about 200 ms to fail there. Source: docs/STATUS.md, 2026-11-09. The Linux start-up figure is from campaign 4, 2026-11-12, on a cloud droplet.

Common questions

How do I embed an HTTP server in a Rust app?

Add simoom, build a Server with Server::builder(), give it a service or a Router, and call start(). It runs on its own threads and returns a handle you can stop() and join(), so the rest of your program keeps running.

Can I add a WebSocket server to a Rust CLI without tokio?

Yes. Implement WebSocketHandler and upgrade a route with Reply::websocket. It works without async, also over HTTP/2 and HTTP/3.

Will a slow handler freeze the server?

It must not run on the loop thread. Send slow work to the offload pool with simoom::blocking, or answer from your own thread with Reply::defer. A stall watchdog also moves a route whose inline handler turns out slow to the offload pool.

Does it run on Windows and macOS?

Yes: IOCP on Windows and kqueue on macOS, epoll or io_uring on Linux. The test suite runs on Windows and Linux; macOS is compile-checked only.

Can simoom serve the files of my web UI?

It has no static-file server. Serve embedded assets from memory with a route, or put the files behind a server such as Caddy if you need directory serving.

Add it to your program

One crate, one function, one call to start().

cargo add simoom