LetsEncryptTls class
A TlsProvider that provisions and renews certificates automatically via
Let's Encrypt (ACME HTTP-01), backed by package:shelf_letsencrypt.
OmnyHub owns the server lifecycle, so this adapter does not bind its own
listeners (unlike LetsEncrypt.startServer). Instead it exposes:
- challengeMiddleware — the hub mounts this first on its plaintext (port
- transport so the ACME CA's HTTP-01 validation requests are answered;
- provision — requests certificates for any seed domains that lack a valid one, then builds their SecurityContexts;
- contextFor — the SNI resolver: returns the certificate for a requested host, and (in on-demand mode) kicks off provisioning for allowed hosts it does not yet have;
- maybeRenew — renews certificates near expiry.
Dynamic / on-demand domains
Passing an allowDomain policy (plus an onDemandEmail) enables on-demand
issuance: any host the policy allows gets a certificate provisioned the
first time it is seen, without the developer listing it in code. Because
certificates are served via SNI from a live cache, foo.example.com and
bar.example.com "just work" as they start being used:
final tls = LetsEncryptTls(
onDemandEmail: 'ops@example.com',
allowDomain: (host) => host.endsWith('.example.com'),
cacheDir: '/var/lib/omnyhub/certs',
production: true,
);
final hub = OmnyHub(transports: [
HttpTransport.http(port: 80), // ACME HTTP-01 challenges
HttpTransport.https(port: 443, tls: tls), // SNI, on-demand certs
]);
The first TLS handshake for an allowed, not-yet-provisioned host triggers background issuance (validated via the challenge on port 80); the certificate is served on the next connection. You can also pre-provision a host at any time with obtain (e.g. at tenant sign-up).
Ports: ACME HTTP-01 requires a reachable port 80; add an
HttpTransport.http(port: 80). Renewal self-checks use securePort.Staging vs production: production defaults to
false(Let's Encrypt staging, whose certificates are browser-invalid but avoid the strict production rate limits). Set it totrueonly once issuance works.
- Implemented types
Constructors
-
LetsEncryptTls({List<
Domain> domains = const [], DomainPolicy? allowDomain, String? onDemandEmail, EmailResolver? onDemandEmailResolver, required String cacheDir, bool production = false, bool autoIssue = true, Duration renewBefore = const Duration(days: 5), int challengePort = 80, int securePort = 443}) -
Creates an ACME provider.
factory
- LetsEncryptTls.forDomain(String domain, String email, {required String cacheDir, bool production = false, bool autoIssue = true, Duration renewBefore = const Duration(days: 5), int challengePort = 80, int securePort = 443})
-
Convenience constructor for a single fixed
domain/email.factory -
LetsEncryptTls.onDemand({required DomainPolicy allowDomain, required String cacheDir, String? email, EmailResolver? emailResolver, List<
Domain> domains = const [], bool production = false, bool autoIssue = true, Duration renewBefore = const Duration(days: 5), int challengePort = 80, int securePort = 443}) -
Convenience constructor for on-demand issuance of any host matching
allowDomain.factory
Properties
- allowDomain → DomainPolicy?
-
Policy allowing on-demand certificates for hosts it returns
truefor.nulldisables on-demand issuance (only domains are served).final - autoIssue → bool
-
Whether a missing certificate may be requested from the CA.
final
- cacheDir → String
-
The directory where account keys and certificates are cached.
final
- challengeMiddleware → Middleware
-
A middleware that answers ACME HTTP-01 challenges, mounted first in the
pipeline of the plaintext transport.
nullfor providers that need no challenge handling (e.g. StaticTls).no setteroverride - defaultContext → SecurityContext?
-
The fallback context served when contextFor returns
null(e.g. a client that sent no SNI).nullmeans connections for unknown hosts are dropped until a certificate exists.no setter -
domains
→ List<
Domain> -
Seed domains provisioned up front on provision.
final
- effectiveRenewBefore → Duration
-
The renewal threshold as the underlying ACME client sees it — renewBefore
is only a safety margin if it actually reaches
shelf_letsencrypt.no setter - hashCode → int
-
The hash code for this object.
no setterinherited
- hotReloadable → bool
-
Whether the securityContext may change at runtime (ACME renewal), in
which case the hub rebinds the HTTPS transport after maybeRenew reports
a refresh.
no setteroverride
- isOnDemand → bool
-
Whether on-demand issuance is enabled.
no setter
- onDemandEmail → String?
-
The fixed contact email used for on-demand certificates.
nullwhen an onDemandEmailResolver is supplied instead.final - onDemandEmailResolver → EmailResolver?
-
Resolves the contact email per host for on-demand certificates, allowing a
different email for each domain. Takes precedence over onDemandEmail.
final
- production → bool
-
Whether to use the production ACME endpoint (
false= staging).final - renewBefore → Duration
-
How much validity a certificate must have left to be kept; below this it
is renewed by maybeRenew. Defaults to 5 days.
final
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
- securePort → int
-
The public HTTPS port, used by renewal self-checks.
final
- supportsSni → bool
-
Whether the transport should use the SNI binding path for this provider.
no setter
Methods
-
contextFor(
String? host) → SecurityContext? -
The certificate context for
host(the SNI hostname), ornullif none is available yet. Called synchronously during the TLS handshake, so it must not block; dynamic providers may kick off background provisioning and returnnull(or a default) for now. -
isAllowed(
String host) → Future< bool> -
Whether
hostmay be served (a seed domain or allowed by the policy). -
maybeRenew(
) → Future< bool> -
Renews certificates if they are near expiry. Returns
trueif the securityContext changed and the listener should be rebound. A no-op (returningfalse) for static providers.override -
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
obtain(
String host) → Future< bool> -
Ensures a certificate exists for
host(provisioning it if allowed and missing) and caches its SecurityContext. Returns whether a certificate is available afterwards. Concurrent calls for the same host are de-duplicated. -
provision(
) → Future< void> -
Ensures certificates exist, provisioning them if necessary. A no-op for
static providers.
override
-
securityContext(
) → SecurityContext -
The current SecurityContext to bind the listener with.
override
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited