### TLS-ALPN-01 Setup Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Illustrates the setup for the TLS-ALPN-01 challenge type, which requires incoming connections to be handled by the listener. ```rust config.challenge_type(UseChallenge::TlsAlpn01) .incoming(listener.incoming(), vec![]) // No special ALPN needed ``` -------------------------------- ### Example: Using certificate() Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Example of downloading the certificate after an order has been validated. Assumes `order` is initialized and its status is `Valid`. ```rust if let OrderStatus::Valid { certificate: cert_url } = order.status { let cert_pem = account.certificate(&client_config, cert_url).await?; println!("Certificate:\n{}", cert_pem); } ``` -------------------------------- ### Example: Using auth() Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Example of how to use the `auth` function to retrieve authorization details and print them. Assumes `account`, `client_config`, and `order` are already initialized. ```rust let auth = account.auth(&client_config, &order.authorizations[0]).await?; println!("Domain: {:?}", auth.identifier); println!("Status: {:?}", auth.status); println!("Challenges: {:?}", auth.challenges); ``` -------------------------------- ### Example: Using order() Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Example of polling the order status and reacting to different states like Pending, Ready, Processing, Valid, or Invalid. Assumes `account`, `client_config`, and `order_url` are initialized. ```rust let updated_order = account.order(&client_config, &order_url).await?; match updated_order.status { OrderStatus::Pending => println!("Waiting for challenges"), OrderStatus::Ready => println!("Can finalize"), OrderStatus::Processing => println!("Processing"), OrderStatus::Valid { certificate: cert_url } => println!("Approved: {}", cert_url), OrderStatus::Invalid => println!("Failed"), } ``` -------------------------------- ### Example: Using challenge() Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Example of how to use the `challenge` function to notify the ACME server after preparing a challenge. Assumes `auth` is initialized. ```rust let challenge = &auth.challenges[0]; account.challenge(&client_config, &challenge.url).await?; ``` -------------------------------- ### AcmeState Integration Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Demonstrates the typical workflow for using AcmeState to manage ACME account creation, order processing, and certificate finalization. This example shows the sequence of calls for discovering a directory, creating an account, initiating a new order, handling authorizations and challenges, finalizing the order, and downloading the certificate. ```rust // AcmeState does this: let directory = Directory::discover(&client_config, &directory_url).await?; let account = Account::create_with_keypair(&client_config, directory, &contact, &key_pair).await?; // Then manages the order process let (order_url, order) = account.new_order(&client_config, domains.clone()).await?; // For each authorization let auth = account.auth(&client_config, &auth_url).await?; // Set up challenge response account.challenge(&client_config, &challenge.url).await?; // Poll until valid let auth = account.auth(&client_config, &auth_url).await?; // Finalize let order = account.finalize(&client_config, &order.finalize, &csr).await?; // Download let cert_pem = account.certificate(&client_config, &cert_url).await?; ``` -------------------------------- ### Example: Using finalize() Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Example of generating a CSR using `rcgen` and then finalizing the order with the generated CSR. Assumes `account`, `client_config`, and `order` are initialized. ```rust use rcgen::CertificateParams; let mut params = CertificateParams::new(vec!["example.com".to_string()])?; let key_pair = rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256)?; let csr = params.serialize_request(&key_pair)?; let order = account.finalize(&client_config, &order.finalize, &csr.der()).await?; ``` -------------------------------- ### Multi-Domain rustls-acme Setup with Logging Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Configures AcmeConfig for multiple domains, including multiple contact emails and a cache directory. This example uses staging for testing. ```rust use rustls_acme::{AcmeConfig, caches::DirCache}; let config = AcmeConfig::new([ "example.com", "www.example.com", "api.example.com", ]) .contact_push("mailto:admin@example.com") .contact_push("mailto:ops@example.com") .cache(DirCache::new("./cache")) .directory_lets_encrypt(false); // Staging for testing ``` -------------------------------- ### Example: Initiate Certificate Order Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Demonstrates creating a new certificate order for a domain using the `account.new_order` method and printing the initial status and authorization details. ```rust let (order_url, order) = account.new_order(&client_config, vec!["example.com".to_string()]).await?; println!("Order status: {:?}", order.status); println!("Authorizations: {:?}", order.authorizations); ``` -------------------------------- ### Example: Create Account with Contact Info Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Shows how to discover the ACME directory and then create a new account using the `Account::create` method with a list of contact email addresses. ```rust use rustls_acme::acme::{Account, Directory}; use std::sync::Arc; use futures_rustls::rustls::ClientConfig; let directory = Directory::discover(&client_config, directory_url).await?; let account = Account::create(&client_config, directory, &["mailto:admin@example.com"]).await?; println!("Account ID: {}", account.kid); ``` -------------------------------- ### Example: Generate and Cache Key Pair Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Demonstrates generating a key pair using `Account::generate_key_pair` and storing it, likely for later use in account creation. ```rust use rustls_acme::acme::Account; let key_bytes = Account::generate_key_pair(); cache.store_account(&key_bytes).await.ok(); ``` -------------------------------- ### Order Struct Usage Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/types.md Demonstrates how to initiate a new order and process the returned order object, including iterating through authorizations to complete challenges. ```rust let (order_url, order) = account.new_order(&client_config, domains).await?; for auth_url in order.authorizations { let auth = account.auth(&client_config, &auth_url).await?; // Complete challenges } ``` -------------------------------- ### Start with Staging Environment Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Begin by configuring Rustls-ACME to use the Let's Encrypt staging environment for testing. Switch to the production directory only after thorough testing. ```rust // Test with staging first config.directory_lets_encrypt(false) // Switch to production only after testing config.directory_lets_encrypt(true) ``` -------------------------------- ### Minimal rustls-acme Staging Setup Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Sets up a minimal AcmeConfig for staging. All other settings default to staging, with no cache and the TLS-ALPN-01 challenge type. ```rust use rustls_acme::AcmeConfig; let config = AcmeConfig::new(["example.com"]); ``` -------------------------------- ### Example: Restore Account from Cache Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Illustrates loading an account key from a cache and using `Account::create_with_keypair` to restore the account. If the key is not found, it falls back to creating a new account. ```rust // Load from cache let cached_key = cache.load_account().await?; if let Some(key_bytes) = cached_key { let account = Account::create_with_keypair(&client_config, directory, &["mailto:admin@example.com"], &key_bytes).await?; } else { let account = Account::create(&client_config, directory, &["mailto:admin@example.com"]).await?; } ``` -------------------------------- ### Actix-web Integration Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Demonstrates integrating rustls-acme with the Actix-web framework. This example configures Actix-web's `HttpServer` to use rustls-acme for TLS. ```rust use actix_web::{web, App, HttpServer, HttpResponse}; use rustls_acme::{AcmeConfig, caches::DirCache}; #[actix_web::main] async fn main() { let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./cache")); let state = config.state(); let rustls_config = state.default_rustls_config(); HttpServer::new(|| { App::new() .route("/", web::get().to(|| async { HttpResponse::Ok().finish() })) }) .bind_rustls("0.0.0.0:443", rustls_config.as_ref().clone()) .unwrap() .run() .await .unwrap(); } ``` -------------------------------- ### Challenge Struct Usage Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/types.md Illustrates how to access challenge details, specifically checking for the TlsAlpn01 challenge type and obtaining the necessary data for it. ```rust let challenge = &auth.challenges[0]; if challenge.typ == ChallengeType::TlsAlpn01 { let (challenge, cert) = account.tls_alpn_01(&auth.challenges, domain)?; resolver.set_tls_alpn_01_challenge_data(domain, Arc::new(cert)); } ``` -------------------------------- ### AcmeState::new Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Creates a new AcmeState instance, initializing certificate and account loading futures, and starting the event polling process. ```APIDOC ## AcmeState::new ### Description Create a new `AcmeState` from an `AcmeConfig`. ### Method `pub fn new(config: AcmeConfig) -> Self` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **config** (`AcmeConfig`) - Required - Configuration with domains, contacts, cache, and directory ### Return Type `AcmeState` ### Example ```rust use rustls_acme::AcmeConfig; use rustls_acme::caches::DirCache; let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./cache")); let mut state = AcmeState::new(config); while let Some(event) = state.next().await { match event { Ok(ok) => println!("Event: {:?}", ok), Err(err) => eprintln!("Error: {:?}", err), } } ``` ``` -------------------------------- ### Example: Establishing Regular TLS Connection Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Shows how to obtain and use the default `rustls::ServerConfig` for establishing a standard TLS connection. It configures the acceptor with the default TLS settings. ```rust let state = config.state(); let tls_config = state.default_rustls_config(); let start_handshake = LazyConfigAcceptor::new(Default::default(), tcp).await.unwrap(); let tls = start_handshake.into_stream(tls_config).await.unwrap(); ``` -------------------------------- ### AcmeConfig::new Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-config.md Create a new AcmeConfig with default settings and add contact information and a cache. This is useful for setting up automatic certificate management for specified domains. ```rust use rustls_acme::AcmeConfig; use rustls_acme::caches::DirCache; let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./rustls_acme_cache")); ``` -------------------------------- ### AcmeConfig::new_with_client_config Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-config.md Initialize AcmeConfig with a custom rustls ClientConfig. This allows for advanced TLS configurations such as custom root certificates or protocol versions. ```rust use std::sync::Arc; use futures_rustls::rustls::ClientConfig; use rustls_acme::AcmeConfig; let client_config = Arc::new( ClientConfig::builder_with_provider(provider) .with_safe_default_protocol_versions() .unwrap() .with_root_certificates(root_store) .with_no_client_auth(), ); let config = AcmeConfig::new_with_client_config(["example.com"], client_config); ``` -------------------------------- ### Example Polling Flow Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/incoming.md Demonstrates how to iterate over an Incoming stream to handle TLS streams or TCP connection errors. Certificate management occurs in the background. ```rust let mut tls_incoming = config.incoming(tcp_listener.incoming(), vec![b"h2".to_vec()]); // First poll: loads cached cert if available while let Some(result) = tls_incoming.next().await { match result { Ok(tls_stream) => { // Handle TLS stream - it's ready to read/write // Certificate management continues in background } Err(tcp_error) => { // Handle TCP connection error } } } ``` -------------------------------- ### Auth Struct Usage Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/types.md Shows how to retrieve an authorization object and inspect its identifier and challenges. This is useful for determining the domain being validated and the available challenge types. ```rust let auth = account.auth(&client_config, &auth_url).await?; if let Identifier::Dns(domain) = auth.identifier { println!("Validating: {}", domain); } for challenge in auth.challenges { if challenge.typ == ChallengeType::TlsAlpn01 { // This server supports TLS-ALPN-01 } } ``` -------------------------------- ### Account::create Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Creates a new ACME account with a newly generated key pair. This method is suitable for initial account setup. ```APIDOC ## Account::create ### Description Create a new ACME account with a generated key pair. ### Method ```rust pub async fn create<'a, S, I>( client_config: &Arc, directory: Directory, contact: I ) -> Result where S: AsRef + 'a, I: IntoIterator, ``` ### Parameters #### Path Parameters - `client_config` (`&Arc`) - Rustls client config for ACME API calls - `directory` (`Directory`) - ACME directory from discovery - `contact` (`I: IntoIterator`) - Contact list (e.g., email addresses with `mailto:` prefix) ### Return Type `Result` ### Errors - `AcmeError::Io` — Network error - `AcmeError::Jose` — Signature error - `AcmeError::Json` — Response parsing error - `AcmeError::MissingHeader` — ACME response missing Location header ### Example ```rust use rustls_acme::acme::{Account, Directory}; use std::sync::Arc; use futures_rustls::rustls::ClientConfig; let directory = Directory::discover(&client_config, directory_url).await?; let account = Account::create(&client_config, directory, &["mailto:admin@example.com"]).await?; println!("Account ID: {}", account.kid); ``` ``` -------------------------------- ### Example with Error Handling for Cache Directory Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Demonstrates conditional cache configuration based on the existence of a cache directory. If the directory doesn't exist or is not writable, it falls back to an ephemeral cache. ```rust use std::path::Path; let cache_dir = "/var/cache/rustls_acme"; if Path::new(cache_dir).exists() { config.cache(DirCache::new(cache_dir)) } else { eprintln!("Cache directory not writable; using ephemeral cache"); config.cache(NoCache::default()) } ``` -------------------------------- ### Generate Cache Key with SHA256 Hash Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/cache-trait.md This example demonstrates how to create a consistent cache key by hashing domain and contact lists using SHA256 and Base64 encoding. This ensures that the same set of domains and contacts always results in the same cache key. ```rust let hash = format!("cached_cert_{}", base64::encode(sha256(&domains.join("\0")))) ``` -------------------------------- ### ACME Directory Discovery and TLS ALPN Check Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Example usage for discovering ACME directories for staging and production environments, and checking for the TLS-ALPN-01 challenge. ```rust // Staging (safe for development, untrusted CA) let directory = Directory::discover(&client_config, LETS_ENCRYPT_STAGING_DIRECTORY).await?; // Production (valid CA, rate-limited) let directory = Directory::discover(&client_config, LETS_ENCRYPT_PRODUCTION_DIRECTORY).await?; // Check if challenge is TLS-ALPN-01 if is_tls_alpn_challenge(&client_hello) { // Handle acme-tls/1 protocol } ``` -------------------------------- ### Example: Handling TLS ALPN Challenge Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Demonstrates how to use `challenge_rustls_config` to handle TLS-ALPN-01 challenges. It checks if the connection is an ACME challenge and applies the specific configuration if it is. ```rust use rustls_acme::is_tls_alpn_challenge; use futures_rustls::LazyConfigAcceptor; let state = config.state(); let challenge_config = state.challenge_rustls_config(); let start_handshake = LazyConfigAcceptor::new(Default::default(), tcp).await.unwrap(); if is_tls_alpn_challenge(&start_handshake.client_hello()) { let tls = start_handshake.into_stream(challenge_config).await.unwrap(); } else { // Handle regular TLS } ``` -------------------------------- ### HTTP-01 Challenge Setup with rustls-acme Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Configures AcmeConfig to use the HTTP-01 challenge type and sets up the necessary HTTP service. This requires an HTTP endpoint at /.well-known/acme-challenge/. ```rust use rustls_acme::{AcmeConfig, UseChallenge, caches::DirCache}; let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./cache")) .challenge_type(UseChallenge::Http01) .directory_lets_encrypt(true); // Then set up HTTP endpoint at /.well-known/acme-challenge/ let state = config.state(); let http_service = state.http01_challenge_tower_service(); ``` -------------------------------- ### rustls-acme Production Setup with Caching Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Configures AcmeConfig for production use with a specified cache directory. Sets the ACME directory to production and enables caching. ```rust use rustls_acme::{AcmeConfig, caches::DirCache}; let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("/var/cache/rustls_acme")) .directory_lets_encrypt(true); // Production ``` -------------------------------- ### Axum Framework Integration Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Demonstrates integrating rustls-acme with Axum using its built-in acceptor support. This example shows how to configure Axum to use rustls-acme for TLS. ```rust #[cfg(feature = "axum")] use rustls_acme::axum::AxumAcceptor; let state = config.state(); let rustls_config = state.default_rustls_config(); let acceptor = state.axum_acceptor(rustls_config); let app = Router::new() .route("/", get(handler)) .into_make_service_with_connect_info::(); axum_server::bind_rustls("0.0.0.0:443".parse().unwrap(), rustls_config) .accept(acceptor) .serve(app) .await .unwrap(); ``` -------------------------------- ### Initialize NoCache Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/cache-trait.md Use NoCache for environments where persistence is not needed, such as testing or ephemeral setups. It performs no actual caching, leading to new requests for every certificate acquisition. ```rust use rustls_acme::AcmeConfig; use rustls_acme::caches::NoCache; type EC = std::io::Error; type EA = EC; let config: AcmeConfig = AcmeConfig::new(["example.com"]) .cache(NoCache::default()); ``` -------------------------------- ### AcmeState::new Constructor Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Creates a new AcmeState instance. Use this to initialize certificate and account loading, and start the certificate acquisition event polling. ```rust pub fn new(config: AcmeConfig) -> Self ``` ```rust use rustls_acme::AcmeConfig; use rustls_acme::caches::DirCache; let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./cache")); let mut state = AcmeState::new(config); while let Some(event) = state.next().await { match event { Ok(ok) => println!("Event: {:?}", ok), Err(err) => eprintln!("Error: {:?}", err), } } ``` -------------------------------- ### Example with Boxed Error Cache Configuration Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Shows how to use cache_with_boxed_err to ensure that different cache implementations result in the same `AcmeConfig` type, enabling them to be stored in the same collection. ```rust // Both these configs have the same type! let config1: AcmeConfig> = AcmeConfig::new(["a.com"]) .cache_with_boxed_err(DirCache::new("./a")); let config2: AcmeConfig> = AcmeConfig::new(["b.com"]) .cache_with_boxed_err(DirCache::new("./b")); let configs = vec![config1, config2]; // Can store in same collection ``` -------------------------------- ### EventOk Enum Usage Example Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/types.md Demonstrates how to handle successful events from AcmeState using a match statement. This pattern is useful for reacting to different stages of certificate management. ```rust while let Some(event) = state.next().await { match event { Ok(EventOk::DeployedNewCert) => println!("New certificate acquired!"), Ok(EventOk::DeployedCachedCert) => println!("Using cached certificate"), Ok(EventOk::CertCacheStore) => println!("Certificate cached"), Ok(EventOk::AccountCacheStore) => println!("Account cached"), Err(err) => eprintln!("Error: {}", err), } } ``` -------------------------------- ### Consuming AcmeState Events Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md This example demonstrates how to consume events from the AcmeState stream. It continuously awaits the next event, printing success messages for certificate events or error messages for failures. The loop is designed to run indefinitely as the stream never terminates. ```rust use futures::StreamExt; let mut state = config.state(); loop { match state.next().await { Some(Ok(ok)) => println!("Certificate event: {:?}", ok), Some(Err(err)) => eprintln!("Certificate error: {:?}", err), None => unreachable!(), // Stream never ends } } ``` -------------------------------- ### AcmeConfig::new Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-config.md Creates a new AcmeConfig with Web PKI root certificates and default settings. It takes an iterator of domain names as input and returns an AcmeConfig with Infallible error types. This is useful for basic setups where no custom cache error handling is needed initially. ```APIDOC ## AcmeConfig::new ### Description Create a new `AcmeConfig` with Web PKI root certificates and default settings. ### Signature ```rust pub fn new(domains: impl IntoIterator>) -> AcmeConfig ``` ### Parameters #### Path Parameters - **domains** (`IntoIterator>`) - Required - Domain names to acquire certificates for ### Return Type `AcmeConfig` An `AcmeConfig` with `Infallible` error types (no cache errors possible until a cache is added). Defaults to Let's Encrypt staging environment and TLS-ALPN-01 challenge type. ### Example ```rust use rustls_acme::AcmeConfig; use rustls_acme::caches::DirCache; let config = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./rustls_acme_cache")); ``` ``` -------------------------------- ### Deprecated acceptor Method Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md This method is deprecated and should not be used. Use `incoming()` instead for the high-level API or refer to updated low-level examples. ```rust #[deprecated(note = "please use high-level API via `AcmeState::incoming()` instead")] pub fn acceptor(&self) -> AcmeAcceptor ``` -------------------------------- ### resolver Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Get the certificate resolver used by this state. The resolver dynamically provides certificates to TLS connections based on SNI and challenge type. ```APIDOC ## resolver ### Description Get the certificate resolver used by this state. The resolver dynamically provides certificates to TLS connections based on SNI and challenge type. ### Method GET ### Endpoint /state/resolver ### Return Type `Arc` ``` -------------------------------- ### Configure rustls-acme with Environment Variables Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Demonstrates how to read configuration from environment variables to set up AcmeConfig. Supports domains, email, production flag, and cache directory. ```rust use std::env; let domains: Vec = env::var("DOMAINS") .unwrap_or("example.com".to_string()) .split(',') .map(|s| s.to_string()) .collect(); let email = env::var("ACME_EMAIL").unwrap_or_default(); let production = env::var("ACME_PRODUCTION") .map(|v| v == "true") .unwrap_or(false); let cache_dir = env::var("CACHE_DIR").ok(); let config = AcmeConfig::new(domains) .contact_push(format!("mailto:{}", email)) .directory_lets_encrypt(production) .cache_option(cache_dir.map(DirCache::new)); ``` -------------------------------- ### Use Let's Encrypt Staging or Production Directory Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md A shorthand for setting the Let's Encrypt directory URL. Use staging (false) for testing to avoid rate limits. ```rust config.directory_lets_encrypt(false) // Staging ``` ```rust config.directory_lets_encrypt(true) // Production ``` -------------------------------- ### Incoming::new Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/incoming.md Creates a new Incoming stream. This is a low-level constructor, and typically `AcmeConfig::incoming()` or `AcmeConfig::tokio_incoming()` should be used instead. ```APIDOC ## Incoming::new ### Description Create a new `Incoming` stream from an `AcmeState`. ### Signature ```rust pub fn new( tcp_incoming: ITCP, state: AcmeState, acceptor: AcmeAcceptor, alpn_protocols: Vec>, ) -> Self ``` ### Parameters - **tcp_incoming** (`ITCP: Stream>`) - Required - Stream of incoming TCP connections - **state** (`AcmeState`) - Required - Certificate management state - **acceptor** (`AcmeAcceptor`) - Required - TLS acceptor for challenge handling - **alpn_protocols** (`Vec>`) - Required - ALPN protocols to advertise (empty for none) ``` -------------------------------- ### Integration with Low-Level TLS Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/resolver.md Demonstrates how to manually integrate the resolver with low-level TLS configurations using `rustls`. ```APIDOC ### With Low-Level TLS When using low-level TLS APIs: ```rust let state = config.state(); let resolver = state.resolver(); // Build rustls config manually let config = ServerConfig::builder() .with_cert_resolver(resolver) .build(); // Use with futures-rustls or tokio-rustls let acceptor = LazyConfigAcceptor::new(Default::default(), tcp); let handshake = acceptor.await?; let tls = handshake.into_stream(Arc::new(config)).await?; ``` ``` -------------------------------- ### Get Certificate Resolver Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Retrieves the certificate resolver used by the ACME state. This resolver dynamically provides certificates based on SNI and challenge type. ```rust pub fn resolver(&self) -> Arc ``` -------------------------------- ### Configure File-Based Cache Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Sets up a file-based cache for certificates and accounts using DirCache. The directory will be created if it does not exist. ```rust use rustls_acme::caches::{DirCache, NoCache, CompositeCache, BoxedErrCache}; // File-based cache config.cache(DirCache::new("/var/cache/rustls_acme")) ``` -------------------------------- ### Configure Rustls-Acme for Let's Encrypt Production Directory Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Use the production Let's Encrypt directory for issuing live certificates. It has stricter rate limits, so thorough testing with the staging directory is recommended before using this. ```rust config.directory_lets_encrypt(true) ``` -------------------------------- ### Configure Separate Certificates per Domain Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Set up individual configurations for different domains to manage separate certificates. This approach allows for distinct caching directories for each domain's certificate. ```rust // Domain A let config_a = AcmeConfig::new(["a.example.com"]) .cache(DirCache::new("./cache_a")); // Domain B let config_b = AcmeConfig::new(["b.example.com"]) .cache(DirCache::new("./cache_b")); // Mount both let mut incoming_a = config_a.incoming(listener_a.incoming(), Vec::new()); let mut incoming_b = config_b.incoming(listener_b.incoming(), Vec::new()); loop { tokio::select! { Some(result) = incoming_a.next() => { let tls = result?; // Handle a.example.com connection } Some(result) = incoming_b.next() => { let tls = result?; // Handle b.example.com connection } } } ``` -------------------------------- ### Manual HTTP Server for HTTP-01 Challenge Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Implement a manual HTTP server handler for HTTP-01 challenges. This function takes an incoming request, extracts the challenge token, and returns the corresponding key authorization if found. ```rust use rustls_acme::ResolvesServerCertAcme; async fn handle_http( req: http::Request, resolver: Arc, ) -> Result, Box> { let path = req.uri().path(); if let Some((_, token)) = path.rsplit_once('/') { if let Some(key_auth) = resolver.get_http_01_key_auth(token) { return Ok(http::Response::builder() .status(200) .header("Content-Type", "text/plain") .body(Body::from(key_auth))?); } } Ok(http::Response::builder() .status(404) .body(Body::from("Not found"))?) } ``` -------------------------------- ### Use Let's Encrypt Directory (Production or Staging) Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-config.md Conveniently set the ACME directory to either Let's Encrypt's production or staging environment based on the boolean flag. ```rust pub fn directory_lets_encrypt(mut self, production: bool) -> Self ``` ```rust config.directory_lets_encrypt(true) // Production ``` ```rust config.directory_lets_encrypt(false) // Staging (safer for development) ``` -------------------------------- ### Incoming::new_with_provider Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/incoming.md Creates a new Incoming stream with a specific crypto provider. This is a low-level constructor. ```APIDOC ## Incoming::new_with_provider ### Description Create a new `Incoming` with a specific crypto provider. ### Signature ```rust pub fn new_with_provider( tcp_incoming: ITCP, state: AcmeState, acceptor: AcmeAcceptor, alpn_protocols: Vec>, provider: Arc, ) -> Self ``` ### Parameters - **tcp_incoming** (`ITCP`) - Required - Stream of incoming TCP connections - **state** (`AcmeState`) - Required - Certificate management state - **acceptor** (`AcmeAcceptor`) - Required - TLS acceptor - **alpn_protocols** (`Vec>`) - Required - ALPN protocols - **provider** (`Arc`) - Required - Crypto provider (ring or aws-lc-rs) ``` -------------------------------- ### Convert SEC1 Private Key to PKCS#8 Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/errors.md The InvalidPrivateKey error indicates an unsupported private key type. This example shows how to convert a SEC1 formatted private key to the required PKCS#8 format using OpenSSL. ```bash # Convert SEC1 to PKCS#8 openssl pkeyutl -in key_sec1.pem -out key_pkcs8.pem ``` -------------------------------- ### Create New Certificate Order Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Initiates a new certificate order with the ACME server for a specified list of domains. Returns the order URL and the initial order object. ```rust pub async fn new_order( &self, client_config: &Arc, domains: Vec ) -> Result<(String, Order), AcmeError> ``` -------------------------------- ### Configure Multi-Domain Certificate Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Use this configuration to include multiple domains in a single certificate. Note Let's Encrypt has limits on the number of domains per certificate. ```rust let config = AcmeConfig::new([ "example.com", "www.example.com", "api.example.com", "*.internal.example.com", ]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./cache")); ``` -------------------------------- ### Incoming::new_with_provider Constructor Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/incoming.md Creates a new Incoming stream with a specified crypto provider. This allows using different crypto backends like ring or aws-lc-rs. ```rust pub fn new_with_provider( tcp_incoming: ITCP, state: AcmeState, acceptor: AcmeAcceptor, alpn_protocols: Vec>, provider: Arc, ) -> Self ``` -------------------------------- ### Configure Multi-Domain Certificate Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/README.md Use this pattern to request a certificate for multiple domain names simultaneously. ```rust AcmeConfig::new([ "example.com", "www.example.com", "api.example.com", ]) ``` -------------------------------- ### Create AcmeState from Configuration Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-config.md Create an AcmeState from this configuration for low-level control over certificate acquisition. Poll this state for certificate events. ```rust pub fn state(self) -> AcmeState ``` ```rust let mut state = config.state(); loop { match state.next().await { Some(Ok(event)) => println!("Event: {:?}", event), Some(Err(err)) => eprintln!("Error: {:?}", err), None => break, } } ``` -------------------------------- ### Set up HTTP-01 Challenge Handler with Axum Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/README.md Integrate the HTTP-01 challenge handler into an Axum application. This requires obtaining the challenge service from the configuration state and nesting it within the router. ```rust let state = config.state(); let http01_service = state.http01_challenge_tower_service(); let app = Router::new() .nest("/.well-known/acme-challenge", http01_service) .fallback(your_app); ``` -------------------------------- ### Implement Custom CertCache and AccountCache Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/cache-trait.md Implement the `CertCache` and `AccountCache` traits to integrate your custom storage solution. Define your error type and handle loading/storing logic for certificates and account data. ```rust use async_trait::async_trait; use rustls_acme::{CertCache, AccountCache}; use std::fmt::Debug; pub struct MyCustomCache { // Configuration } #[async_trait] impl CertCache for MyCustomCache { type EC = MyError; // Your error type async fn load_cert( &self, domains: &[String], directory_url: &str, ) -> Result>, Self::EC> { // Query your backend // Return Ok(Some(cert_pem)) if found // Return Ok(None) if not found // Return Err(e) on error todo!() } async fn store_cert( &self, domains: &[String], directory_url: &str, cert: &[u8], ) -> Result<(), Self::EC> { // Persist to your backend todo!() } } #[async_trait] impl AccountCache for MyCustomCache { type EA = MyError; async fn load_account( &self, contact: &[String], directory_url: &str, ) -> Result>, Self::EA> { todo!() } async fn store_account( &self, contact: &[String], directory_url: &str, account: &[u8], ) -> Result<(), Self::EA> { todo!() } } // Now use it: let config = AcmeConfig::new(["example.com"]) .cache(MyCustomCache { /* ... */ }); ``` -------------------------------- ### Configure Separate Certificate and Account Caches Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Sets up distinct cache implementations for certificates and accounts using the cache_compose method. This is useful for managing different storage strategies. ```rust let config = AcmeConfig::new(["example.com"]) .cache_compose( DirCache::new("/mnt/shared/certs"), // Network share DirCache::new("/var/cache/accounts"), // Local cache ); ``` -------------------------------- ### Fetch Authorization Details Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Fetches authorization details for a given challenge URL. Requires a client configuration and the authorization URL obtained from an order. ```rust pub async fn auth( &self, client_config: &Arc, url: impl AsRef ) -> Result ``` -------------------------------- ### directory_lets_encrypt Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-config.md Use Let's Encrypt staging or production directory. ```APIDOC ## directory_lets_encrypt ### Description Conveniently sets the ACME directory URL to either the Let's Encrypt production or staging environment. ### Method `directory_lets_encrypt(production: bool) -> Self` ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - None ### Request Example ```rust config.directory_lets_encrypt(true) // Use production directory config.directory_lets_encrypt(false) // Use staging directory (safer for development) ``` ### Response #### Success Response - `Self`: Returns the modified `AcmeConfig` instance. #### Response Example ```rust // No direct response example, as it returns `Self` for chaining. ``` ``` -------------------------------- ### High-Level API for Automatic TLS Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Use `AcmeConfig::incoming()` for automatic TLS handling with a TCP listener. Certificate management is handled in the background. ```rust let tcp_listener = TcpListener::bind("0.0.0.0:443").await?; let mut tls_incoming = AcmeConfig::new(["example.com"]) .contact_push("mailto:admin@example.com") .cache(DirCache::new("./cache")) .incoming(tcp_listener.incoming(), Vec::new()); while let Some(tls) = tls_incoming.next().await { let mut tls = tls?; // tls is a completed TLS stream, ready to use // Certificate is automatically managed in background } ``` -------------------------------- ### Account::new_order Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Creates a new certificate order for the specified domains. This is the first step in obtaining a certificate. ```APIDOC ## Account::new_order ### Description Create a certificate order for specified domains. ### Method ```rust pub async fn new_order( &self, client_config: &Arc, domains: Vec ) -> Result<(String, Order), AcmeError> ``` ### Parameters #### Path Parameters - `client_config` (`&Arc`) - Rustls client config - `domains` (`Vec`) - Domains to include in certificate ### Return Type `Result<(String, Order), AcmeError>` - `(order_url, order)` — URL and initial order object with status `Pending` ### Example ```rust let (order_url, order) = account.new_order(&client_config, vec!["example.com".to_string()]).await?; println!("Order status: {:?}", order.status); println!("Authorizations: {:?}", order.authorizations); ``` ``` -------------------------------- ### Configure TLS-ALPN-01 Challenge Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Sets the challenge type to TLS-ALPN-01, which is the default and recommended method. It challenges on port 443 and requires ALPN negotiation. ```rust use rustls_acme::UseChallenge; // TLS-ALPN-01 (recommended, challenges on port 443) config.challenge_type(UseChallenge::TlsAlpn01) ``` -------------------------------- ### Integrate with Axum using Tower Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/resolver.md Demonstrates how to use the `http01_challenge_tower_service` with Axum for handling ACME HTTP-01 challenges. This service should be nested under the appropriate path. ```rust use rustls_acme::tower::TowerHttp01ChallengeService; // AcmeState provides a ready-made Tower service: let state = config.state(); let service = state.http01_challenge_tower_service(); // Use with Axum: let app = Router::new() .nest("/.well-known/acme-challenge", service) .fallback(your_app); ``` -------------------------------- ### HTTP-01 Challenge with Tower Service Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/resolver.md Illustrates using the http01_challenge_tower_service for HTTP-01 challenges. This service automatically uses the resolver to fetch the key authorization. ```rust let state = config.state(); let http01_service = state.http01_challenge_tower_service(); // The service automatically uses the resolver to fetch key auth let response = http01_service.call(request).await?; ``` -------------------------------- ### Initialize CompositeCache Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/cache-trait.md Combine separate certificate and account cache implementations using CompositeCache. This allows for granular control over different caching strategies. ```rust use rustls_acme::caches::{DirCache, CompositeCache}; let cert_cache = DirCache::new("/var/cache/certs"); let account_cache = DirCache::new("/var/cache/accounts"); let composite = CompositeCache::new(cert_cache, account_cache); let config = AcmeConfig::new(["example.com"]) .cache(composite); ``` -------------------------------- ### Account::create_with_keypair Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-account.md Creates an ACME account using an existing key pair. This is useful for restoring a cached account. ```APIDOC ## Account::create_with_keypair ### Description Create an account with an existing key pair. ### Method ```rust pub async fn create_with_keypair<'a, S, I>( client_config: &Arc, directory: Directory, contact: I, key_pair: &[u8], ) -> Result where S: AsRef + 'a, I: IntoIterator, ``` ### Parameters #### Path Parameters - `client_config` (`&Arc`) - Rustls client config - `directory` (`Directory`) - ACME directory - `contact` (`I`) - Contact list - `key_pair` (`&[u8]`) - PKCS#8-encoded key pair bytes (from cache or `generate_key_pair()`) ### Return Type `Result` ### Use when You're restoring a cached account key to avoid creating duplicate accounts ### Example ```rust // Load from cache let cached_key = cache.load_account().await?; if let Some(key_bytes) = cached_key { let account = Account::create_with_keypair(&client_config, directory, &["mailto:admin@example.com"], &key_bytes).await?; } else { let account = Account::create(&client_config, directory, &["mailto:admin@example.com"]).await?; } ``` ``` -------------------------------- ### Create Challenge TLS Config with Provider Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Creates a challenge `rustls::ServerConfig` using a specified crypto provider. This allows for using alternative providers like ring or aws-lc-rs. ```rust pub fn challenge_rustls_config_with_provider( &self, provider: Arc ) -> Arc ``` -------------------------------- ### Incoming Stream Integration Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/resolver.md Demonstrates how the Incoming stream automatically uses the resolver. The ServerConfig is configured with the resolver from AcmeState, and each TLS connection invokes resolver.resolve() via ClientHello. ```rust let tls_incoming = config.incoming(tcp_listener.incoming(), alpn_protocols); // Internally: // - Creates Incoming with resolver from AcmeState // - ServerConfig uses resolver via with_cert_resolver() // - Each TLS connection calls resolver.resolve() via ClientHello ``` -------------------------------- ### tokio_incoming Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Provides a Tokio-compatible wrapper for incoming TLS connection streams, facilitating integration with Tokio-based applications. ```APIDOC ## tokio_incoming ### Description Tokio-compatible incoming stream wrapper. ### Method Signature ```rust #[cfg(feature = "tokio")] pub fn tokio_incoming(self, tcp_incoming: TokioITCP, alpn_protocols: Vec>) -> crate::tokio::TokioIncoming<...> ``` ### Parameters #### Path Parameters * None #### Query Parameters * None #### Request Body * None ### Parameters Table | Parameter | Type | Default | Description | |---|---|---|---| | tcp_incoming | `TokioITCP` | — | Tokio TCP listener stream | | alpn_protocols | `Vec>` | — | ALPN protocol names | ### Return Type `crate::tokio::TokioIncoming<...>` ``` -------------------------------- ### Initialize DirCache Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/cache-trait.md Use DirCache for file-based caching. It creates the cache directory if it doesn't exist and uses SHA256 hashing for filenames. Suitable for single-server deployments. ```rust use rustls_acme::caches::DirCache; let config = AcmeConfig::new(["example.com"]) .cache(DirCache::new("/var/cache/rustls_acme")); ``` -------------------------------- ### Configure HTTP-01 Challenge Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/configuration.md Sets the challenge type to HTTP-01, which challenges on port 80. This requires setting up an HTTP endpoint to serve the challenge files. ```rust use rustls_acme::UseChallenge; // HTTP-01 (challenges on port 80) config.challenge_type(UseChallenge::Http01) ``` -------------------------------- ### Configure HTTP-01 Challenge Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/integration-guide.md Configure the HTTP-01 challenge type only when port 80 is accessible and a reverse proxy or load balancer is set up to forward HTTP requests. ```rust // HTTP-01 only if: // - Port 80 is accessible // - Reverse proxy/load balancer handles HTTP forwarding config.challenge_type(UseChallenge::Http01) ``` -------------------------------- ### Create Challenge TLS Config Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/acme-state.md Generates a `rustls::ServerConfig` for TLS-ALPN-01 challenge connections. Use this when `is_tls_alpn_challenge()` returns true. It configures ALPN with the ACME challenge protocol name. ```rust pub fn challenge_rustls_config(&self) -> Arc ``` -------------------------------- ### Low-Level TLS Configuration Source: https://github.com/florianuekermann/rustls-acme/blob/main/_autodocs/api-reference/resolver.md Shows how to manually build a rustls ServerConfig using the resolver obtained from the AcmeState. This configuration can then be used with futures-rustls or tokio-rustls. ```rust let state = config.state(); let resolver = state.resolver(); // Build rustls config manually let config = ServerConfig::builder() .with_cert_resolver(resolver) .build(); // Use with futures-rustls or tokio-rustls let acceptor = LazyConfigAcceptor::new(Default::default(), tcp); let handshake = acceptor.await?; let tls = handshake.into_stream(Arc::new(config)).await?; ```