Files
smartacme/readme.hints.md

4.0 KiB

  • this repo is dependent on letsencrypt and its limits
  • to simpify the outside API, smartacme is stateful, meaning it works with a mongodb and a collection called 'SmartacmeCert'.

Certificate Request Behavior

As of v7.4.0, SmartAcme no longer automatically requests wildcard certificates for all domain requests. This change was made to fix issues with HTTP-01 only configurations which cannot validate wildcard domains.

  • By default, getCertificateForDomain('example.com') only requests a certificate for example.com
  • To request both regular and wildcard certificates, use getCertificateForDomain('example.com', { includeWildcard: true })
  • Wildcard certificates require a DNS-01 challenge handler to be configured
  • Direct wildcard requests like getCertificateForDomain('*.example.com') only request the wildcard certificate

This change ensures HTTP-01 only configurations work properly while still allowing wildcard certificates when needed and supported.

ACME Protocol Implementation

As of v8.1.0, the acme-client npm package has been replaced with a custom OOP implementation under ts/acme/. This uses node:crypto for all cryptographic operations and @peculiar/x509 solely for CSR generation. The implementation follows RFC 8555.

Key files:

  • ts/acme/acme.classes.client.ts — Top-level facade (AcmeClient), accepts optional logger callback
  • ts/acme/acme.classes.crypto.ts — Key gen, JWK, JWS signing, CSR (AcmeCrypto)
  • ts/acme/acme.classes.http-client.ts — JWS-signed HTTP transport with nonce management and logging
  • ts/acme/acme.classes.error.ts — Structured AcmeError with type URN, subproblems, Retry-After, isRetryable/isRateLimited
  • ts/acme/acme.classes.account.ts — Account registration
  • ts/acme/acme.classes.order.ts — Order lifecycle + polling
  • ts/acme/acme.classes.challenge.ts — Key authorization + challenge completion
  • ts/acme/acme.classes.directory.ts — CA directory URL constants (ACME_DIRECTORY_URLS)

Usage in ts/plugins.ts: import * as acme from './acme/index.js' (replaces acme-client)

Concurrency & Rate Limiting (taskbuffer integration)

As of v9.1.0, @push.rocks/lik.InterestMap was replaced with @push.rocks/taskbuffer.TaskManager for coordinating concurrent certificate requests. This provides:

  • Per-domain mutex (cert-domain-mutex): Only one ACME issuance per TLD at a time, with resultSharingMode: 'share-latest' so queued callers get the same result without re-issuing.
  • Global concurrency cap (acme-global-concurrency): Limits total parallel ACME operations (default 5, configurable via maxConcurrentIssuances).
  • Account-level rate limiting (acme-account-rate-limit): Sliding-window rate limit (default 250 orders per 3 hours, configurable via maxOrdersPerWindow/orderWindowMs) to stay under Let's Encrypt limits.
  • Step-based progress: The cert issuance task uses notifyStep() for prepare/authorize/finalize/store phases, observable via smartAcme.certIssuanceEvents.

Key implementation details:

  • A single reusable Task named cert-issuance handles all domains via triggerTaskConstrained() with different inputs.
  • The shouldExecute callback on the domain mutex checks the certmanager cache as a safety net.
  • TaskManager.start() is called in SmartAcme.start() and TaskManager.stop() in SmartAcme.stop().
  • The "no cronjobs specified" log messages during tests come from taskbuffer's internal CronManager polling — harmless noise when no cron tasks are scheduled.

Dependency Notes

  • acme-client was replaced with custom implementation in ts/acme/ + @peculiar/x509 for CSR generation
  • @push.rocks/smartfile, @api.global/typedserver, @push.rocks/smartrequest, @push.rocks/smartpromise were removed as unused dependencies in v8.1.0
  • The @apiclient.xyz/cloudflare convenience namespace is deprecated but still functional. The Dns01Handler accepts an IConvenientDnsProvider interface which remains stable.
  • Test imports use @git.zone/tstest/tapbundle (not @push.rocks/tapbundle)
  • Build uses tsbuild (no flags needed, v4+)