Challenge Handlers

Handlers for completing ACME challenges. The library supports three challenge types: DNS-01, HTTP-01, and TLS-ALPN-01.

ChallengeHandler (Base)

class acmeow.ChallengeHandler[source]

Bases: ABC

Abstract base class for ACME challenge handlers.

Challenge handlers are responsible for deploying and cleaning up challenge responses. Implementations must handle both setup and cleanup to ensure proper resource management.

Example

>>> class MyDnsHandler(ChallengeHandler):
...     def setup(self, domain, token, key_authorization):
...         # Create DNS TXT record
...         pass
...     def cleanup(self, domain, token):
...         # Remove DNS TXT record
...         pass
abstractmethod setup(domain, token, key_authorization)[source]

Deploy the challenge response.

This method is called before notifying the ACME server that the challenge is ready for validation. It should set up whatever resource is needed for the challenge type (DNS record, HTTP file, etc.).

Parameters:
  • domain (str) – The domain being validated.

  • token (str) – The challenge token from the ACME server.

  • key_authorization (str) – The key authorization string (token.thumbprint). For DNS-01, this should be hashed with SHA-256 and base64url encoded. For HTTP-01, this is served directly.

Raises:

Exception – If setup fails and the challenge cannot proceed.

Return type:

None

abstractmethod cleanup(domain, token)[source]

Remove the challenge response.

This method is called after challenge validation completes (whether successful or not) to clean up deployed resources.

Parameters:
  • domain (str) – The domain that was validated.

  • token (str) – The challenge token.

Return type:

None

Note

This method should not raise exceptions even if cleanup fails, as it’s called during error handling paths.

DNS-01 Handlers

CallbackDnsHandler

class acmeow.CallbackDnsHandler[source]

Bases: ChallengeHandler

DNS-01 handler using user-provided callbacks.

This handler delegates DNS record management to user-provided callback functions, allowing integration with any DNS provider.

Parameters:
  • create_record (Callable[[str, str, str], None]) – Callback to create a DNS TXT record. Signature: (domain: str, record_name: str, record_value: str) -> None - domain: The domain being validated (e.g., “example.com”) - record_name: The full record name (e.g., “_acme-challenge.example.com”) - record_value: The TXT record value (base64url SHA-256 hash)

  • delete_record (Callable[[str, str], None]) – Callback to delete a DNS TXT record. Signature: (domain: str, record_name: str) -> None

  • propagation_delay (int) – Seconds to wait after creating record. Default 60.

Example

>>> def create_txt(domain, name, value):
...     dns_api.create_record(name, "TXT", value)
>>> def delete_txt(domain, name):
...     dns_api.delete_record(name, "TXT")
>>> handler = CallbackDnsHandler(create_txt, delete_txt)
__init__(create_record, delete_record, propagation_delay=60)[source]
Parameters:
Return type:

None

property propagation_delay: int

Seconds to wait after creating DNS record for propagation.

setup(domain, token, key_authorization)[source]

Create DNS TXT record for the challenge.

The TXT record value is the base64url-encoded SHA-256 hash of the key authorization, as specified by RFC 8555.

Parameters:
  • domain (str) – The domain being validated.

  • token (str) – The challenge token (not used for DNS record value).

  • key_authorization (str) – The key authorization string to hash.

Return type:

None

cleanup(domain, token)[source]

Remove DNS TXT record.

Parameters:
  • domain (str) – The domain that was validated.

  • token (str) – The challenge token (not used).

Return type:

None

HTTP-01 Handlers

CallbackHttpHandler

class acmeow.CallbackHttpHandler[source]

Bases: ChallengeHandler

HTTP-01 handler using user-provided callbacks.

This handler delegates challenge file management to user-provided callback functions, allowing integration with any HTTP serving method.

Parameters:
  • setup_callback (Callable[[str, str, str], None]) – Callback to deploy the challenge response. Signature: (domain: str, token: str, key_authorization: str) -> None

  • cleanup_callback (Callable[[str, str], None]) – Callback to remove the challenge response. Signature: (domain: str, token: str) -> None

Example

>>> def setup_challenge(domain, token, key_auth):
...     redis.set(f"acme:{token}", key_auth)
>>> def cleanup_challenge(domain, token):
...     redis.delete(f"acme:{token}")
>>> handler = CallbackHttpHandler(setup_challenge, cleanup_challenge)
__init__(setup_callback, cleanup_callback)[source]
Parameters:
Return type:

None

setup(domain, token, key_authorization)[source]

Deploy challenge response using callback.

Parameters:
  • domain (str) – The domain being validated.

  • token (str) – The challenge token.

  • key_authorization (str) – The key authorization string.

Return type:

None

cleanup(domain, token)[source]

Remove challenge response using callback.

Parameters:
  • domain (str) – The domain that was validated.

  • token (str) – The challenge token.

Return type:

None

FileHttpHandler

class acmeow.FileHttpHandler[source]

Bases: ChallengeHandler

HTTP-01 handler that writes challenge files to a webroot directory.

This handler writes challenge response files to the standard .well-known/acme-challenge/ directory structure. The web server must be configured to serve this directory.

Parameters:

webroot (Path) – Path to the web server’s document root. Files will be written to {webroot}/.well-known/acme-challenge/

Example

>>> handler = FileHttpHandler(Path("/var/www/html"))
>>> # Challenge file will be at:
>>> # /var/www/html/.well-known/acme-challenge/{token}
__init__(webroot)[source]
Parameters:

webroot (Path)

Return type:

None

property challenge_dir: Path

Directory where challenge files are written.

setup(domain, token, key_authorization)[source]

Write challenge response file.

Creates the challenge file containing the key authorization at the path expected by the ACME server: {webroot}/.well-known/acme-challenge/{token}

Parameters:
  • domain (str) – The domain being validated (for logging).

  • token (str) – The challenge token (becomes the filename).

  • key_authorization (str) – The key authorization (file content).

Return type:

None

cleanup(domain, token)[source]

Remove challenge response file.

Parameters:
  • domain (str) – The domain that was validated.

  • token (str) – The challenge token (the filename).

Return type:

None

TLS-ALPN-01 Handlers

CallbackTlsAlpnHandler

class acmeow.CallbackTlsAlpnHandler[source]

Bases: ChallengeHandler

TLS-ALPN-01 handler using user-provided callbacks.

This handler delegates certificate deployment to user-provided callback functions, allowing integration with any TLS server.

Parameters:
  • deploy_callback (Callable[[str, bytes, bytes], None]) – Callback to deploy the certificate. Signature: (domain: str, cert_pem: bytes, key_pem: bytes) -> None

  • cleanup_callback (Callable[[str], None]) – Callback to remove the certificate. Signature: (domain: str) -> None

Example

>>> def deploy_cert(domain, cert_pem, key_pem):
...     # Configure TLS server with certificate
...     server.set_certificate(domain, cert_pem, key_pem)
>>> def cleanup_cert(domain):
...     server.remove_certificate(domain)
>>> handler = CallbackTlsAlpnHandler(deploy_cert, cleanup_cert)
__init__(deploy_callback, cleanup_callback)[source]
Parameters:
Return type:

None

setup(domain, token, key_authorization)[source]

Generate and deploy TLS-ALPN-01 certificate.

Parameters:
  • domain (str) – The domain being validated.

  • token (str) – The challenge token (not used for TLS-ALPN-01).

  • key_authorization (str) – The key authorization string.

Return type:

None

cleanup(domain, token)[source]

Remove TLS-ALPN-01 certificate.

Parameters:
  • domain (str) – The domain that was validated.

  • token (str) – The challenge token (not used).

Return type:

None

FileTlsAlpnHandler

class acmeow.FileTlsAlpnHandler[source]

Bases: ChallengeHandler

TLS-ALPN-01 handler that writes certificates to files.

This handler writes the validation certificate and key to files, which can then be loaded by a TLS server. Optionally calls a reload callback to signal the server to reload certificates.

Parameters:
  • cert_dir (Path) – Directory to write certificate files.

  • cert_pattern (str) – Pattern for certificate filename. {domain} is replaced.

  • key_pattern (str) – Pattern for key filename. {domain} is replaced.

  • reload_callback (Callable[[], None] | None) – Optional callback to reload the TLS server. Signature: () -> None

Example

>>> handler = FileTlsAlpnHandler(
...     cert_dir=Path("/etc/tls/acme"),
...     cert_pattern="{domain}.crt",
...     key_pattern="{domain}.key",
...     reload_callback=lambda: subprocess.run(["nginx", "-s", "reload"]),
... )
__init__(cert_dir, cert_pattern='{domain}.alpn.crt', key_pattern='{domain}.alpn.key', reload_callback=None)[source]
Parameters:
  • cert_dir (Path)

  • cert_pattern (str)

  • key_pattern (str)

  • reload_callback (Callable[[], None] | None)

Return type:

None

setup(domain, token, key_authorization)[source]

Generate and write TLS-ALPN-01 certificate files.

Parameters:
  • domain (str) – The domain being validated.

  • token (str) – The challenge token (not used for TLS-ALPN-01).

  • key_authorization (str) – The key authorization string.

Return type:

None

cleanup(domain, token)[source]

Remove TLS-ALPN-01 certificate files.

Parameters:
  • domain (str) – The domain that was validated.

  • token (str) – The challenge token (not used).

Return type:

None

Helper Functions

generate_tls_alpn_certificate

acmeow.generate_tls_alpn_certificate(domain, key_authorization, key=None, validity_days=1)[source]

Generate a TLS-ALPN-01 validation certificate.

Creates a self-signed certificate with the acmeIdentifier extension containing the SHA-256 hash of the key authorization, as required by RFC 8737.

Parameters:
Return type:

tuple[bytes, bytes]

Returns:

Tuple of (certificate_pem, private_key_pem) as bytes.

Example

>>> cert_pem, key_pem = generate_tls_alpn_certificate(
...     "example.com",
...     "token.thumbprint",
... )