Skip to main content

Crate mailrs_acme

Crate mailrs_acme 

Source
Expand description

§mailrs-acme

Crates.io docs.rs License

High-level ACME (RFC 8555 / Let’s Encrypt) orchestration. Wraps the low-level instant-acme client with the pieces every server needs:

  • account load-or-create (persisted under <acme_dir>/account.json)
  • HTTP-01 cert provisioning with a shared token store
  • expiry monitoring via x509 parsing
  • periodic renewal task that swaps new certs into mailrs-tls-reload atomically (no in-flight handshakes dropped)
  • optional bundled axum HTTP-01 challenge server (feature flag)

If you only want the protocol pieces, use instant-acme directly. If you want “give me a TlsState that stays renewed forever,” use this.

§Quickstart

use mailrs_acme::{init, new_challenge_tokens, spawn_challenge_server, spawn_renewal_task, RenewalConfig};
use std::path::Path;
use std::net::SocketAddr;
use tokio::sync::watch;

let (shutdown_tx, shutdown_rx) = watch::channel(false);
let tokens = new_challenge_tokens();

// Start the HTTP-01 challenge server on port 80 (or your reverse-proxy
// target). Skip this and serve the route from your own HTTP stack if
// you prefer — read tokens from the same map.
spawn_challenge_server(
    tokens.clone(),
    SocketAddr::from(([0, 0, 0, 0], 80)),
    shutdown_rx.clone(),
);

// Init: load existing cert if valid, else provision via HTTP-01.
let (tls_state, account) = init(
    "ops@example.com",
    &["example.com".into(), "www.example.com".into()],
    Path::new("/var/acme"),
    false,                    // production (not staging)
    &tokens,
).await?;

// Spawn the renewal task (12h check interval, renew at ≤30 days).
spawn_renewal_task(
    account,
    tokens.clone(),
    tls_state.clone(),
    RenewalConfig {
        domains: vec!["example.com".into(), "www.example.com".into()],
        acme_dir: "/var/acme".into(),
        ..Default::default()
    },
    shutdown_rx,
);

// Use `tls_state.acceptor()` in your TLS accept loop. It always
// returns the current (post-renewal) config.

§What this crate does

  • init(email, domains, acme_dir, staging, tokens) -> (TlsState, Account) — one-shot bootstrapper. Loads or provisions a cert; returns the things you need to plug into your server.
  • load_or_create_account(email, staging, acme_dir) — just the account step.
  • provision_cert(account, domains, tokens) — just the cert flow.
  • cert_days_remaining(pem_bytes) -> i64 — x509 expiry helper.
  • save_cert(acme_dir, cert, key) / build_server_config(cert, key) — file + rustls glue.
  • spawn_renewal_task(account, tokens, tls_state, config, shutdown) — periodic check + auto-swap.
  • spawn_challenge_server(tokens, addr, shutdown) — bundled axum HTTP-01 server (feature-gated axum-http, on by default).
  • new_challenge_tokens() / ChallengeTokens type — shared token-store between provisioner + challenge server.

§What this crate does not

  • Not an ACME protocol implementation. That’s instant-acme — this crate calls into it.
  • No DNS-01 challenge. HTTP-01 only in 1.0. (DNS-01 needs DNS provider integration which is per-provider; a future mailrs-acme-dns01 could provide the orchestration layer for caller-supplied DNS clients.)
  • No TLS-ALPN-01 challenge. Same reasoning — fits a future separate crate if needed.
  • No multi-account / wildcard / EAB-protected CA support. All reachable via instant-acme directly; this crate is for the common “one account, one cert per cert-renewing server, public CA” path.
  • No file watcher for external renewals. If certbot writes new PEMs to your acme_dir, you’d call tls_state.swap(new_config) from your own file-change handler.

§Features

FeatureDefaultWhat it adds
axum-httpThe bundled spawn_challenge_server (axum)

Disable default-features = false if you serve the challenge from your own HTTP stack (actix-web, warp, hyper, …). The ChallengeTokens API stays available regardless.

§License

Apache-2.0 OR MIT.

Structs§

RenewalConfig
Configuration for spawn_renewal_task.

Functions§

build_server_config
Convenience: build a rustls::ServerConfig from in-memory PEM certificate and private key. Used internally after a successful provisioning; exposed for callers who want to build configs from cert bytes they got elsewhere (e.g. dump from a secret manager).
cert_days_remaining
Parse a PEM-encoded certificate and return the number of days until notAfter. Negative if already expired.
init
High-level init: load existing cert if valid, else provision a new one. Returns the constructed TlsState (ready for your TLS listeners) and the Account (hand to spawn_renewal_task).
load_or_create_account
Load an existing ACME account from account.json under acme_dir, or create a new one (against Let’s Encrypt staging or production) and persist it.
new_challenge_tokens
Construct an empty challenge token store.
provision_cert
Provision a certificate for domains via the HTTP-01 challenge.
save_cert
Write cert.pem + key.pem into acme_dir. Creates the directory if missing.
spawn_challenge_server
Bundled HTTP-01 challenge server (axum-based). Binds the given addr and serves /.well-known/acme-challenge/{token} from the shared ChallengeTokens map.
spawn_renewal_task
Spawn a background task that periodically checks acme_dir/cert.pem expiry and renews via HTTP-01 when it’s getting close.

Type Aliases§

ChallengeTokens
Shared map of token → key_authorization used by the HTTP-01 challenge server. The ACME orchestration writes to this map when a challenge is set up; the HTTP server reads it when ACME’s CA hits /.well-known/acme-challenge/{token}.