A comprehensive SSL certificate management library with Let's Encrypt ACME v2 support for Erlang/OTP applications.
- Full ACME v2 protocol support
- Let's Encrypt certificate automation
- DNS and HTTP-01 challenge support
- Certificate lifecycle management
- Cryptographic operations for certificate handling
- Comprehensive validation and state management
- Modern HTTP/2 client using gun
Add this library to your rebar.config
dependencies:
{deps, [
{ssl_cert, {git, "https://github.com/permaweb/ssl_cert.git", {branch, "main"}}}
]}.
Configure the SSL certificate device with the required options:
%% Configuration for SSL certificate requests
Opts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>, <<"www.example.com">>],
<<"email">> => <<"[email protected]">>,
<<"environment">> => <<"staging">> % Use "production" for live certificates
}
}.
%% Initiate certificate request - returns DNS challenges
{ok, Response} = dev_ssl_cert:request(undefined, undefined, Opts),
#{<<"body">> := #{
<<"challenges">> := Challenges,
<<"message">> := <<"Create DNS TXT records for the following challenges, then call finalize">>
}} = Response.
Based on the returned challenges, create DNS TXT records:
_acme-challenge.example.com. TXT "challenge_token_here"
_acme-challenge.www.example.com. TXT "challenge_token_here"
%% After DNS records are set, finalize the certificate
{ok, FinalResponse} = dev_ssl_cert:finalize(undefined, undefined, Opts),
#{<<"body">> := #{
<<"certificate_pem">> := CertPem,
<<"key_pem">> := KeyPem,
<<"domains">> := Domains
}} = FinalResponse.
%% Renew existing certificate
RenewOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>, <<"www.example.com">>],
<<"email">> => <<"[email protected]">>,
<<"environment">> => <<"production">>
}
},
{ok, RenewResponse} = dev_ssl_cert:renew(undefined, undefined, RenewOpts).
%% Delete stored certificate
DeleteOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>, <<"www.example.com">>]
}
},
{ok, DeleteResponse} = dev_ssl_cert:delete(undefined, undefined, DeleteOpts).
StagingOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"test.example.com">>],
<<"email">> => <<"[email protected]">>,
<<"environment">> => <<"staging">>
}
}.
ProductionOpts = #{
<<"ssl_opts">> => #{
<<"domains">> => [<<"example.com">>],
<<"email">> => <<"[email protected]">>,
<<"environment">> => <<"production">>
}
}.
For advanced use cases, you can call the underlying modules directly:
%% Validate request parameters
{ok, ValidatedParams} = ssl_cert_validation:validate_request_params(
[<<"example.com">>], <<"[email protected]">>, <<"staging">>),
%% Process certificate request
{ok, ProcessResponse} = ssl_cert_ops:process_certificate_request(ValidatedParams, Wallet),
%% Validate DNS challenges
{ok, ValidationResponse} = ssl_cert_challenge:validate_dns_challenges_state(RequestState, PrivateKey),
%% Generate CSR
{ok, {CsrDer, PrivateKey}} = acme_csr:generate_csr([<<"example.com">>], #{}).
acme_client
- Main ACME client APIssl_cert_ops
- High-level certificate operationsacme_protocol
- Core ACME protocol implementationacme_crypto
- Cryptographic operations and JWSacme_csr
- Certificate Signing Request generationssl_cert_challenge
- Challenge handling and validationssl_cert_validation
- Certificate validation utilitiesssl_cert_state
- State management utilitiesssl_utils
- Utility functions and HTTP client
gun
- Modern HTTP/2 client for ACME communicationcrypto
- Cryptographic operationspublic_key
- Public key operationsssl
- SSL/TLS supportinets
- Additional HTTP utilities
Current test coverage across all modules:
Module | Coverage |
---|---|
Core Modules | |
acme_client |
25% |
acme_crypto |
65% |
acme_csr |
81% |
acme_http |
49% |
acme_protocol |
26% |
acme_url |
100% |
ssl_cert_challenge |
18% |
ssl_cert_ops |
24% |
ssl_cert_state |
65% |
ssl_cert_validation |
95% |
ssl_utils |
29% |
Test Modules | |
acme_client_tests |
91% |
acme_crypto_tests |
100% |
acme_csr_tests |
91% |
acme_http_tests |
100% |
acme_protocol_tests |
91% |
acme_url_tests |
100% |
ssl_cert_challenge_tests |
100% |
ssl_cert_integration_tests |
100% |
ssl_cert_ops_tests |
100% |
ssl_cert_state_tests |
100% |
ssl_cert_test_suite |
10% |
ssl_cert_validation_tests |
100% |
ssl_utils_tests |
100% |
Total Coverage | 68% |
- High Coverage (80%+):
acme_csr
,acme_url
,ssl_cert_validation
- Medium Coverage (50-79%):
acme_crypto
,ssl_cert_state
- Low Coverage (<50%):
acme_client
,acme_http
,acme_protocol
,ssl_cert_challenge
,ssl_cert_ops
,ssl_utils
# Format all Erlang files
rebar3 fmt
# Check if files need formatting (don't modify)
rebar3 fmt --check
# Run linter to check code quality
rebar3 lint
# Compile and run all tests
rebar3 as test eunit
# Run specific test module
rebar3 as test eunit --module=my_module_tests
# Run tests with coverage analysis
rebar3 cover
# Generate coverage reports
rebar3 covertool generate
# Full test and coverage workflow
rebar3 as test eunit && rebar3 cover && rebar3 covertool generate
# Generate HTML documentation
rebar3 ex_doc
# Authenticate with Hex (one-time setup)
rebar3 hex user auth
# Publish to Hex
rebar3 hex publish
# Complete quality check before commit
rebar3 clean
rebar3 fmt --check
rebar3 lint
rebar3 as test compile
rebar3 as test eunit
rebar3 cover
rebar3 covertool generate