ol.clave

Automatic HTTPS certificate management and renewal via ACME, implemented in pure Clojure with minimal dependencies

doc status: experimental nixbot

ol.clave is an ACME client for Clojure.

It gives you a lower-level API for implementing the ACME protocol itself when you need that much control. If you want to work directly with accounts, orders, authorizations, challenges, and certificate issuance, you can do that.

And if you do not want to live at RFC altitude all day, it also gives you a higher-level API for provisioning certificates from application code, managing them over time, and keeping them up to date. That is the part most people actually want. Ask for a cert. Renew the cert. Repeat.

ol.clave also includes adapters for wiring ACME into Jetty and Aleph applications, plus built-in HTTP-01, TLS-ALPN-01, and optional Protocol53-backed DNS-01 Challenge Solvers.

One of the design goals for ol.clave was as few dependencies as possible and avoid dragging in a bunch of Java ecosystem baggage.

At runtime there are exactly two hard dependencies. The first is babashka/json, which is a BYO JSON-library-library. On the JVM it will use a JSON provider from your classpath. In many applications that will just work because you already have one. If you care which provider gets picked, or want to force one, go read that project’s docs.

The second runtime dep is Peter Taoussanis’s Trove, which gives ol.clave a very lightweight way to emit signals and logging without forcing a backend on you. Same story there: wire it into whatever telemetry or logging stack you already use after reading Peter’s docs.

The no-dependencies choice has consequences. I did not want to pull in Bouncy Castle as a runtime dependency. It is large, it is heavy, and it is the kind of library that likes to start version fights when two things on your classpath want different releases. But ACME certificate provisioning still needs some plumbing that the JDK does not hand you directly (despite being distributed as part of the keytool util in every JDK package), especially around DER de/encoding, CSR generation, and a few X.509-adjacent details.

To be clear, ol.clave is not implementing any cryptography itself. The actual crypto primitives (RSA, ECC, etc) still come from the JVM. What ol.clave implements in pure Clojure is the narrow slice of encoding, decoding, and certificate-request machinery needed to make ACME work without the heavyweight runtime dependencies. That problem space is small enough to specify, small enough to test thoroughly and it isn’t a general-purpose PKI toolkit.

Project status: Experimental.

Installation

Use deps.edn git coordinates:

;; deps.edn
{:deps {com.outskirtslabs/clave
        {:git/url "https://github.com/outskirtslabs/clave.git"
         :git/sha "8ae16bc3d6431e6a03d96a6a041e64c24e76376a"}}}

Aleph remains an optional dependency. Add the tested Aleph version alongside Clave when using ol.clave.ext.aleph:

{:deps {aleph/aleph {:mvn/version "0.9.10"}}}

Quick Start

Start with one of the runnable examples in this repository:

These examples use Pebble for local ACME testing.

Trust Pebble’s local CA with the repository PKCS12 trust store when running an example:

clj -J-Djavax.net.ssl.trustStore=test/fixtures/pebble-truststore.p12 \
    -J-Djavax.net.ssl.trustStorePassword=changeit \
    -J-Djavax.net.ssl.trustStoreType=PKCS12 \
    -A:dev -M -m certificate

Replace certificate with the example namespace you want to run.

ol.protocol53 is Outskirts Labs' provider-independent Clojure API for managing DNS records. ACME DNS-01 validation needs temporary TXT records, which Protocol53 can manage across its supported DNS providers. Clave’s optional DNS-01 Challenge Solver integrates directly with Protocol53.

Security

See Security for security reporting and policy links.

License

Copyright © 2025-2026 Casey Link

Distributed under the EUPL-1.2.

Some files included in this project are from third-party sources and retain their original licenses as indicated in NOTICE.

Special thanks to Michiel Borkent (@borkdude) for the use of babashka/json.