Files
smartacme/readme.hints.md

5.8 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.

ACME Directory Server (ts_server/)

As of v9.2.0, a built-in ACME Directory Server lives under ts_server/. This is a full RFC 8555-compliant CA server that allows running your own Certificate Authority.

Key files:

  • ts_server/server.classes.acmeserver.ts — Top-level AcmeServer facade (start/stop/config)
  • ts_server/server.classes.ca.ts — Self-signed root CA generation + certificate signing via @peculiar/x509
  • ts_server/server.classes.jws.verifier.ts — JWS signature verification (inverse of AcmeCrypto.createJws)
  • ts_server/server.classes.router.ts — Minimal HTTP router with :param support using raw node:http
  • ts_server/server.classes.nonce.ts — Single-use replay nonce management
  • ts_server/server.classes.challenge.verifier.ts — HTTP-01/DNS-01 verification (with bypass mode)
  • ts_server/server.classes.account.store.ts — In-memory account storage
  • ts_server/server.classes.order.store.ts — In-memory order/authz/challenge/cert storage
  • ts_server/server.handlers.*.ts — Route handlers for each ACME endpoint

Design decisions:

  • Uses raw node:http (no framework dependency — @api.global/typedserver was explicitly removed in v8.1.0)
  • Zero new dependencies: uses node:crypto, @peculiar/x509, and existing project deps
  • Reuses AcmeCrypto for JWK thumbprint/base64url, ACME interfaces for response types, AcmeError patterns
  • AcmeCrypto.getAlg() was made public (was private) for use by the JWS verifier
  • Storage interfaces (IServerAccountStore, IServerOrderStore) are pluggable, with in-memory defaults
  • challengeVerification: false option auto-approves challenges for testing
  • tsbuild tsfolders automatically compiles ts_server/ to dist_ts_server/

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 tsfolders (v4.3.0+) — auto-discovers and compiles ts/ and ts_server/ directories