ACMEOW v1.1.2
ACMEOW v1.1.2
Release Date: May 20, 2026
Version: v1.1.2
Commit: a3d4de6
Production-grade ACME protocol client library for Python.
What's New
External CSR Support
finalize_order() now accepts a csr= argument (PEM or DER-encoded bytes), allowing
bring-your-own-key workflows where the key pair lives on the downstream client:
# Generate key and CSR externally
key = ec.generate_private_key(ec.SECP256R1())
csr_der = (
x509.CertificateSigningRequestBuilder()
.subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "example.com")]))
.add_extension(
x509.SubjectAlternativeName([x509.DNSName("example.com")]),
critical=False,
)
.sign(key, hashes.SHA256())
.public_bytes(serialization.Encoding.DER)
)
# Pass it to ACMEOW — no key is generated or stored
with AcmeClient(...) as client:
client.new_order(["example.com"])
client.complete_challenges()
client.finalize_order(csr=csr_der)
cert_pem, key_pem = client.get_certificate()
# key_pem is None — the key was never held by ACMEOWWhen csr= is provided:
- No private key is generated or saved to disk
get_certificate()returns(cert_pem, None)for the key elementcommon_nameandkey_typearguments tofinalize_order()are ignored
When csr= is omitted, behaviour is unchanged (key generated internally, returned by get_certificate()).
The implementation uses key_path.exists() to determine whether a key was generated
internally, replacing the earlier _external_csr_used flag approach.
Fixes
Fast-fail on HTTP 500 + application/problem+json
A 500 response whose Content-Type is application/problem+json is now treated as a
definitive server-reported error (e.g. "domain not authorized for ACME") rather than a
transient failure. It raises AcmeServerError immediately with the server's detail
message, without consuming any retry budget.
Plain 500 responses (no problem body) continue to be retried as transient failures.
Server detail surfaced on retry exhaustion
When all retries are exhausted on a retryable status code (503, etc.), the last
response is now passed to _handle_error_response, propagating the ACME problem
detail to the caller instead of raising a generic exhaustion message.
Installation
pip install acmeow
pip install acmeow[dns-route53] # AWS Route53
pip install acmeow[all] # All optional dependencies
Requirements
- Python 3.10+
- cryptography >= 41.0.0
- requests >= 2.31.0
- requests[socks] >= 2.31.0
Testing & Quality Metrics
- 433 tests passing (21 new tests added for external CSR and HTTP error-handling)
- Tested on Python 3.10, 3.11, 3.12, 3.13
- Cross-platform support (Linux, macOS, Windows)
License
Apache License 2.0