clj-commons/aleph: Async HTTP, TCP and UDP for Clojure
Asynchronous streaming communication for Clojure - web server, web client, and raw TCP/UDP
At a glance
- What is it?
- Aleph turns network data into Manifold streams and puts a Ring-compatible server and an async HTTP client in front of Netty. It is aimed at Clojure services that need streaming, HTTP/2 or raw sockets without leaving the JVM ecosystem.
- Who is it for?
- Adopt Aleph if your Clojure service needs a Ring-compatible server that can return Manifold deferreds and streams, or an HTTP client where every request is a deferred rather than a blocking call.
- Can I use it commercially?
- Yes. MIT 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 last received commits 40 days ago.
- What is it written in?
- Mainly Clojure, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Aleph is for in a Clojure service
Aleph exposes data from the network as a Manifold stream. That single sentence from the README is the whole design premise. Instead of handing you a socket and asking you to write a read loop, it hands you a stream you can turn into a java.io.InputStream, a core.async channel, a Clojure sequence, or another byte representation through byte-streams. The library ships default wrappers for HTTP, TCP and UDP, and the README states that it still allows access to the full performance and flexibility of the underlying Netty library.
The audience is narrower than "Clojure developers". It is people building services where the request or response body is not a single string. Server-sent events, chunked responses, proxying, long-lived TCP connections and UDP datagrams are the cases where the stream abstraction earns its keep. If your handler returns a small map and nothing else, Aleph works, but so does any Ring server, and you have taken on Netty and Manifold as dependencies for no gain.
Manifold deferreds and the Ring spec
The server side follows the Ring spec fully and the README claims it can be a drop-in replacement for any existing Ring-compliant server. The extension is that the handler function may return a Manifold deferred to represent an eventual response. That is where the drop-in claim gets complicated: the README itself warns that this feature may not play nicely with synchronous Ring middleware which modifies the response, and suggests reimplementing the middleware with Manifold's let-flow operator. A helper, aleph.http/wrap-ring-async-handler, converts an async 3-arity Ring handler into one Aleph accepts.
The response body may also be a Manifold stream, where each message from the stream is sent as a chunk. That is the mechanism behind streamed responses and server-sent events: you control the chunk boundaries directly rather than buffering a body and hoping the server flushes at the right moment.
On the client side, Aleph models itself after clj-http except that every request immediately returns a deferred representing the response. The README is explicit about the gaps rather than burying them: proxy configuration belongs to the connection pool and per-request proxy setups are not allowed, :proxy-ignore-hosts is not supported, :response-interceptor is not supported, and :cache and :cache-config are not supported as of now. Cookie handling drops the params obsoleted since RFC2965 (comment, comment URL, discard, version). When you pass :debug, :save-request? and :debug-body?, the corresponding request data lands under :aleph/netty-request, :aleph/request and :aleph/request-body keys in the response map. Aleph also adds a :log-activity connection pool key that switches on logging of connection status changes and request/response hex dumps.
Installing Aleph and starting a server
Aleph is published on Clojars. The README gives the coordinates for both Leiningen and deps.edn. The deps.edn form also lists a git coordinate alternative, io.github.clj-commons/aleph, with a :git/sha key, which is useful when you need a commit that has not been released.
;; Leiningen
[aleph "0.9.11"]
;; deps.edn
aleph/aleph {:mvn/version "0.9.11"}A first server is a Ring handler plus one call. The README's example returns a plain map and starts on port 8080. Note the comment in the README: this form is HTTP/1-only.
(require '[aleph.http :as http])
(defn handler [req]
{:status 200
:headers {"content-type" "text/plain"}
:body "hello!"})
(http/start-server handler {:port 8080}) ; HTTP/1-onlyFor the client, the README shows both a blocking deref and a chained call. The deref form is convenient at the REPL; the d/chain form keeps the whole path asynchronous.
(require
'[aleph.http :as http]
'[manifold.deferred :as d]
'[clj-commons.byte-streams :as bs])
(-> @(http/get "https://google.com/")
:body
bs/to-string
prn)
(d/chain (http/get "https://google.com")
:body
bs/to-string
prn)To get HTTP/2 you have to ask for it. On the server, pass :http-versions with [:http2 :http1] and an :ssl-context. On the client, build a connection pool with :connection-options containing the same :http-versions vector and pass it as :pool. The README points to aleph.examples.http2 for details, so expect to read that file rather than the README when wiring TLS.
Where HTTP/2 support stops
Aleph has supported HTTP/2 in both client and server since 0.7.0, and defaults to HTTP/1-only for backwards compatibility. The README lists the boundaries plainly, and they are the most useful part of the document for anyone evaluating the library.
Multipart uploads are not supported under HTTP/2 because Netty does not support them there. The README's advice is to open a new H2 stream per file for new development and to keep existing multipart code on HTTP/1. CONNECT is not supported under HTTP/2 either. Server push will not be supported, since the README calls it deprecated and effectively disabled by Chrome. HTTP/2 trailers, headers arriving after the body, are not supported. Priority information is ignored entirely, with the README arguing that browsers never agreed on interpretation and that back-porting HTTP/3 priority headers is a better aim.
Flow control is the quiet one. Aleph uses Netty's default flow control, a 64 kb window with bytes acknowledged as soon as they are received, and the README says support for adjusting the default window size and strategy is planned for a future release. If you are pushing large responses over HTTP/2 and tuning window sizes, that knob does not exist yet. There is also a migration note: if you used pipeline-transform to alter the underlying Netty pipeline, you need to recheck that usage for HTTP/2, because the new code uses Netty's multiplexed pipeline with a shared connection-level pipeline feeding stream-specific frames.
Aleph versus clj-http and plain Ring servers
The honest comparison is not Aleph against another async framework. It is Aleph against clj-http on the client and against a synchronous Ring server such as Jetty on the server, because those are the tools a Clojure team already has.
clj-http blocks. A call returns a response map and your thread waits. Aleph returns a deferred immediately, so a single thread can hold many in-flight requests, and the README states that Aleph attempts to mimic the clj-http API and capabilities fully. The difference in approach shows up exactly where the mimicry stops: proxy settings move from the request to the connection pool, and three clj-http options (:proxy-ignore-hosts, :response-interceptor, :cache/:cache-config) simply have no Aleph equivalent. If your code passes those keys per request, the migration is not a version bump.
On the server, a synchronous Ring server runs your handler on a thread and writes the reply. Aleph lets the handler return a deferred or a stream, which is what makes server-sent events and chunked proxying natural rather than a fight with the container. The cost is that synchronous middleware which inspects or rewrites the response map may not see a response at all, because at that moment there is only a deferred. The README names let-flow as the fix, which means rewriting the middleware, not configuring it.
Maintenance, licence and upgrade cost
The repository is not archived, and the last push was on 2026-08-24, roughly a month before this writing. That is recent enough to treat the project as live, but the README's own hedging language matters more than the commit date: several HTTP/2 items are described as "not yet", "currently" or "in a future release". Multipart over HTTP/2, CONNECT, trailers, flow-control tuning and priority handling are all open ends, and planning around them means planning around work that has not landed.
Aleph is MIT licensed, which is permissive and imposes no copyleft obligation on your application. That is a statement about the licence text, not legal advice; if you redistribute Aleph or bundle it into a product, read LICENSE.md yourself.
The upgrade cost is concentrated in two places. The first is the Netty dependency, which Aleph wraps closely enough that pipeline-transform users had to re-examine their code for HTTP/2. The second is Manifold: once handlers return deferreds and streams, your middleware and error handling have to speak that language, and unwinding that decision later is a rewrite rather than a config change. The README does not document a rollback path for either, so treat the async handler style as a one-way door within a service.
Editorial conclusion
Adopt Aleph if your Clojure service needs a Ring-compatible server that can return Manifold deferreds and streams, or an HTTP client where every request is a deferred rather than a blocking call. Skip it if you rely on synchronous Ring middleware that rewrites response maps, if you need HTTP/2 multipart uploads or CONNECT, or if you want a client whose API matches clj-http option for option, because :proxy-ignore-hosts, :response-interceptor, :cache and :cache-config are documented as unsupported. Before committing, check the README's HTTP/2 caveat list against your own feature set and confirm whether you need per-request proxy configuration, which Aleph only allows at connection-pool setup time.
Frequently asked questions
How do I install Aleph in a Clojure project?
Add it from Clojars, either as [aleph "0.9.11"] under Leiningen or as aleph/aleph {:mvn/version "0.9.11"} in deps.edn. The README also lists a git coordinate, io.github.clj-commons/aleph, with a :git/sha key.
Does Aleph support HTTP/2 on both the client and the server?
Yes, since 0.7.0, but Aleph defaults to HTTP/1-only for backwards compatibility. You enable it by passing :http-versions [:http2 :http1] with an :ssl-context on the server, or by building a connection pool with :connection-options and passing it as :pool on the client.
Can Aleph replace an existing Ring server without changes?
The README states Aleph follows the Ring spec fully and can be a drop-in replacement for any Ring-compliant server. The caveat is that if your handler returns a Manifold deferred, synchronous Ring middleware that modifies the response may not behave correctly, and the README suggests reimplementing that middleware with let-flow.
What clj-http options does Aleph not support?
The README lists :proxy-ignore-hosts and :response-interceptor as unsupported, and :cache and :cache-config as not supported for now. Per-request proxy setups are also not allowed, since proxy configuration is set on the connection pool instead.
What does Aleph mean?
The name comes from the project itself, clj-commons/aleph; the README does not explain the choice of name or give any other meaning for it.
Official sources
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.
[](https://hysenlabs.com/projects/clj-commons-aleph)