Framework
guenchi/Igropyr avatar
guenchi/Igropyr

Igropyr: A Chez Scheme Web Framework That Treats Failure as a Protocol

A distributed, fault-tolerant, high-concurrency backend framework with continuation-based web programming and a remote retry ring, built on Chez Scheme.

174 stars21 forksSchemeApache-2.0

At a glance

What is it?
Igropyr is a distributed backend framework for Chez Scheme that combines continuation-based green processes with a remote retry ring, aiming to make fault tolerance a first-class part of the HTTP contract. The core is small and framework-agnostic, but the real substance is in its conversation model and its explicit failure outcomes.
Who is it for?
Adopt Igropyr if you are building a stateful, multi-step web dialogue in Chez Scheme and want failure handling that speaks a structured protocol rather than a bare 500, and if you can live with a single OS thread and a preemptive scheduler that still needs a cost ceiling for password hashing. Do not adopt it if you expect drop-in Erlang/OTP semantics, if you need preemptive scheduling across multiple cores, or if you want a framework with a large ecosystem.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Scheme, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • guenchi/Igropyr174 ★ · +22
Star history
Repository2026-08-212026-10-22
guenchi/Igropyr152174

22 Stars in the last 62 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What Igropyr Actually Solves

Igropyr targets a specific pain in backend development: stateful, multi-request dialogues that must survive crashes and retries without corrupting business logic. Think of a booking wizard or a fund transfer that spans several HTTP requests. Most frameworks force you to serialize dialogue state to a database or a session store, and then you hand-roll retry logic. Igropyr instead runs each dialogue as a green process that holds live state, even an open database transaction, across requests. It also changes what a failure means: instead of a plain 500 after retries are exhausted, an optional on-failure handler returns a structured JSON fault that tells the client exactly what happened and what to do next. The intended user is a Scheme developer who wants Erlang-style supervision and message passing but also wants continuations to model conversations, and who is willing to build on Chez Scheme's runtime.

Green Processes and the Scheduler

The core scheduling model is a single OS thread running thousands of green processes. Context switching is continuation-based with preemption, so a CPU-spinning handler cannot freeze the whole server. That is a deliberate trade-off: you get concurrency without threads, but you also get a single core for compute. The README is explicit that the scheduler is single-threaded, and that matters for anything CPU-bound. The password hashing library includes a verify-time cost ceiling precisely to keep a crafted stored hash from stalling that one thread. So the framework is honest about its constraint. For I/O-bound workloads, the model works: callers park their green process while the OS thread keeps serving, and the non-blocking Redis, MySQL, and PostgreSQL clients all follow that pattern. The supervisor maintains a fixed worker pool, replaces crashed workers, and retries tasks up to three times before returning a 500 or invoking the failure hook.

Conversations: Continuations with a Failure Protocol

The conversation feature is the most distinctive part of Igropyr. A dialogue runs as one green process across multiple requests. The suspend! procedure answers and parks the process, and conversation-resume! continues with a token that names the reply it answers. That token prevents a repeat request from taking a step nobody asked for. The failure handling is even more specific: six outcomes, each with its own evidence and remedy. The answer itself when the conversation finished, retry only on 'gone' because the rollback provably ran, replay on 'stale', reconcile on 'unknown' and 'unreachable', and come back later on 'overloaded'. The record outlives the process, so a restart does not turn history into 'unknown'. This is a protocol, not just an error code. It forces the client to understand what state the server believes it is in. That is powerful, but it also means your client must implement replay, reconciliation, and backoff logic. A plain HTTP client that just retries on any 5xx will misbehave against this protocol.

The Remote Retry Ring and Failure Hook

The remote retry ring is the mechanism that turns a failed task into a structured conversation. When retries are exhausted or a stuck worker is killed, an optional on-failure handler answers a JSON fault on the same keep-alive connection. The kill happens first, so no execution is in flight when the fault is sent. The client then resubmits with changed parameters and carried state, and gets a fresh retry round. If the hook is unset, the plain 500 remains. This design inverts the usual retry model: instead of the server silently retrying internally until it gives up, the server tells the client exactly why it failed and invites a corrected resubmission. That is a real difference from a typical load balancer retry or a client-side exponential backoff. The trade-off is that the client must be aware of the ring and must be able to change parameters or carry state. A naive client that just re-sends the same request will likely loop.

Getting It Running: Core and Express Layer

The README shows a core entry point: (http-listen port (lambda (req res) ...)). That is the Node-and-Express analogy: the core is one function, and the bundled (igropyr express) layer is optional. You can build an alternative framework on the same core. The express layer provides create-app, app-get, send-json!, and middleware. Installation is not described in detail in the provided material, but the repository is on GitHub and the homepage is igropyr.dev, with a manual at igropyr.dev/manual.html. The license is MIT. To run a basic server, you would load the core and call http-listen with a port and a handler. For the full framework, you would use the express layer. The README does not give a complete installation command, so you would need to check the manual for exact steps. The key point is that the core is deliberately minimal, and the express layer is just one possible interface.

WebSocket, Streaming, and Hot Code Swapping

WebSocket upgrades happen on the same port as HTTP, and each socket is its own green process, so server push is just a message send. Upgrades can be guarded before the handshake with an auth guard, so an unauthenticated peer never gets a socket. Streaming responses use res-begin!, res-write!, and res-end! for chunked bodies, with SSE helpers on top. Hot code swapping lets you replace the handler or individual routes on a live server without dropping connections or the worker pool; in-flight requests finish on the old code. That is a strong operational feature, but it also implies a deployment process that can manage code versions across a running process. The README does not describe how to orchestrate that in a cluster, only that the mechanism exists.

Authentication Shapes: A Trap for the Unwary

The README goes out of its way to warn that the auth shapes differ and none is interchangeable. The auth middleware and token-guard take a token verifier (lambda (token) ...). The app-ws and app-rpc take a request guard (lambda (req) ...). The app-use and admin-listen's auth option take middleware (lambda (req res next)). All are procedures, so the wrong one is installed without complaint and fails on the first request that reaches it. Also, these are separate doors: guarding one does not guard another, and none of it reaches an app you built with a second app-listen or a raw http-listen. That is a genuine footgun. The session-guard takes an origins allow-list because the same-origin policy does not cover WebSockets; the browser attaches the session cookie whatever page opened the socket, so the allow-list is what keeps a hostile page from holding an authenticated session. Browser origins fail closed until the application configures the list, while clients without an Origin header remain supported. This is a thoughtful security design, but it requires careful reading of the manual to avoid misconfiguration.

Client Libraries and the Single-Threaded Constraint

Igropyr ships non-blocking clients for Redis, MySQL, and PostgreSQL, all pure Scheme on the same event loop. The PostgreSQL client authenticates with SCRAM-SHA-256, supports optional TLS with certificate verification and channel binding, and offers both simple and extended query protocols with server-side parameter binding. The S3 client uses AWS Signature V4. These clients are not just wrappers; they are part of the same parking model, so a database call does not block the scheduler. The constraint is that all of this runs on one OS thread. If you need to use multiple cores, you would have to run multiple OS processes and coordinate externally. The framework does not appear to provide distribution across processes; it is distributed in the sense of fault tolerance and remote retry, not in the sense of multi-node clustering. The README does not describe a node discovery or cluster membership mechanism.

Maintenance, Upgrade Cost, and Alternatives

The project is actively maintained, with a release 1.2.8 on 2026-08-13, and the repository is not archived. The MIT license is permissive. The upgrade cost depends on how much you rely on the conversation protocol and the auth shapes, which are stable but intricate. The manual is the primary documentation. A real alternative is Erlang/OTP, which also gives you green processes, supervision, and message passing, but with a mature distribution model across nodes and a battle-tested web stack like Cowboy. The difference is that Erlang does not have continuations built in, so you would model conversations with explicit state machines or process dictionaries. Igropyr's continuation-based approach is more direct for linear dialogues, but it ties you to Chez Scheme and to this framework's specific failure protocol. Another alternative is to use a conventional framework like Node.js with a session store and a retry queue, but that lacks the process-per-dialogue model and the structured failure outcomes.

Editorial conclusion

Adopt Igropyr if you are building a stateful, multi-step web dialogue in Chez Scheme and want failure handling that speaks a structured protocol rather than a bare 500, and if you can live with a single OS thread and a preemptive scheduler that still needs a cost ceiling for password hashing. Do not adopt it if you expect drop-in Erlang/OTP semantics, if you need preemptive scheduling across multiple cores, or if you want a framework with a large ecosystem. Verify first that the six-outcome conversation protocol matches your client's ability to replay, reconcile, and back off, and that your operational tooling can handle hot-swapped code and the 30-second stuck-worker kill. The remote retry ring is the differentiator; it is also the part most likely to surprise you if your clients are not built for it.

Frequently asked questions

What happens when a request handler crashes in Igropyr?

The worker is replaced and the task is retried, at most three times, after which the client gets a 500. Workers stuck for more than 30 seconds are killed and replaced, and a slow or half-sent request only blocks its own reader process.

What does the on-failure hook in Igropyr do?

It is an optional handler that answers a structured JSON fault instead of the plain 500, on the same keep-alive connection, when retries are exhausted or a stuck worker is killed. The client resubmits with changed parameters and gets a fresh retry round. Unset, the plain 500 remains.

How does Igropyr keep a multi-request dialogue alive?

The dialogue runs as one green process holding live state, which can include an open database transaction. `suspend!` answers and parks, and `conversation-resume!` continues with a token naming the reply it answers, so a repeated request replays that answer rather than taking an unasked-for step.

Why does Igropyr need an origins allow-list for WebSockets?

Because the same-origin policy does not cover WebSockets and the browser attaches the session cookie whatever page opened the socket. `session-guard` takes the `origins` list for that reason, and browser origins fail closed until the application configures it, while clients with no Origin header remain supported.

Can Igropyr connect to an outbound secure WebSocket?

No. Outbound `wss://` is not supported, though `http-get`, `http-post` and `ws-connect` exist with async DNS, and `https://` works through the optional `(igropyr tls)` library so the core keeps no OpenSSL dependency.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/guenchi-igropyr.svg)](https://hysenlabs.com/projects/guenchi-igropyr)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/guenchi-igropyr.svg)](https://hysenlabs.com/projects/guenchi-igropyr)