Igropyr

A web server where crashes heal themselves, code hot-swaps, faults speak a protocol, and dialogues are processes.

Pure Chez Scheme · Erlang-style actors · libuv event loop · MIT

Get the code

Read the manual

Let It Crash

Every request runs in a supervised worker pool. Handlers don't defend — they crash, and the system recovers.

Crashes heal themselves

A crashed worker is replaced instantly; the task is retried on a fresh worker, up to 3 times, before the client sees an error.

A worker stuck longer than 30 s — even a CPU-spinning loop — is killed and replaced: preemptive scheduling means nothing can freeze the server.

A half-sent request parks only its own reader process and is reaped by timeout. Other connections never notice.

Write the happy path. The supervisor owns the sad one.

Hot code swapping

Replace the handler — or a single route — on a running server. The listener, open connections and the worker pool stay up; in-flight requests finish on the old code.

Deploy without a restart

Routes live in a mutable registry behind the pool. Re-registering a path replaces it atomically for the next request; http-swap! replaces the whole handler the same way.

Combined with graceful shutdown (http-shutdown! drains in-flight work) and SO_REUSEPORT multi-process listening, zero-downtime operation is the default, not a project.

Faults speak a protocol

When retries are exhausted or a stuck worker is killed, Igropyr doesn't just throw a 500 — it can tell the client exactly what happened, on a connection that stays open.

Killed first, told after

The on-failure hook answers a structured fault after the stuck worker is dead — so when the client hears stuck, there is no execution left in flight. The state is definite.

crash — retries exhausted; resubmit with changed parameters, or compensate.

stuck — killed mid-flight; resubmit carrying state, or roll back.

Keep-alive survives the fault, so the client resubmits on the same connection and gets a fresh retry round. Shorten stuck-ms and a user who once stared at a spinner for 30 s now rings through several informed retries in the same time — failures become invisible at the UI.

Dialogues are processes

A multi-request dialogue — a wizard, a booking, a transfer — runs as one green process. Its local bindings are the conversation state, including things a session store can never hold: an open database transaction, spanning rounds.

Control flow is program text

"The user is at the confirm step" means the process is parked at that line. A step order the code cannot express cannot happen — no state machine to get wrong, no replay to defend against.

The gone guarantee: death for any reason — crash, TTL, completion — unregisters the process, and a later resume answers gone. Dead process = dropped connection = the database itself rolled back: gone proves nothing committed. Together with the fault codes above, the client always knows the definite server state — a complete remote transaction ring.

Communicate in S-expressions

When the client is Scheme too, requests and replies are s-expressions — there is no codec to design. (igropyr sexpr) is the safe parser; app-rpc dispatches one datum per message, over HTTP, WebSocket or SSE.

No codec on the wire

Exact ratios and bignums cross the wire intact — no floating-point JSON approximation anywhere. (rpc "/rpc" '(add 1 2 1/2)) comes back (ok 7/2), the ratio preserved.

The peer is Goeteia, a Scheme compiler that runs in WebAssembly; its (web rpc) / (web ws) / (web sse) speak the same wire format.

Goeteia

This site itself is written in pure Scheme, and compiled to HTML and CSS — the honeycomb fire above is compiled in real time by Goeteia.

Goeteia

Igropyr

Igropyr

Goeteia

Goeteia

What it stands on

Pure Chez Scheme

Every line is Scheme — R6RS libraries in .sc, no C shim. libuv, zlib and the crypto for MySQL auth are reached through Chez's FFI directly. Whole-program compilation folds the framework and your app into one optimized binary.

Erlang-style actors

Green processes with spawn / send / receive, link and monitor, a process registry, gen-server and PubSub. One OS thread, preemptive scheduling, pure message passing — no shared state, no locks.

Async on libuv

One event loop feeds thousands of parked processes. DNS, file reads and database round-trips park the calling process, never the thread. Non-blocking HTTP/WebSocket clients and Redis/MySQL drivers included.

Goeteia

Built on the shoulders of others

Igropyr is built on Chez Scheme — the fastest Scheme compiler, with a first-class FFI that reaches libuv directly. With deep gratitude for Kent Dybvig's life work, and to Cisco for open-sourcing it.

Kent Dybvig

The primary inspirations: Node.js is the event-loop server on libuv, and the lean core / optional-framework split that Node and Express made the norm. The actor model, the supervisor, and Let It Crash come from Erlang/OTP; Swish — a Chez Scheme system built on those ideas — was the concrete blueprint for the scheduler, the receive macro, and the supervisor. The conversation model is the actor-native take on web programming with continuations — a great idea from the Scheme and functional-programming community.

Chez Scheme

Chez Scheme

Cisco

Cisco

Node.js

Node.js

Erlang

Erlang

Swish

Swish