Skip to content

Repository files navigation

@postalsys/certs

Manage Let's Encrypt SSL/TLS certificates with automatic acquisition, renewal, and storage via the ACME protocol. Certificates and ACME account data are stored in Redis. Supports ACME HTTP-01 challenges.

Installation

npm install @postalsys/certs

Requirements: Node.js 15+ (for CAA record validation), Redis

Usage

constRedis=require('ioredis');constexpress=require('express');const{ Certs }=require('@postalsys/certs');constredis=newRedis();constapp=express();constcerts=newCerts({
redis,namespace: 'myapp',acme: {// Use 'production' and the production directory URL for real certificatesenvironment: 'production',directoryUrl: 'https://acme-v02.api.letsencrypt.org/directory',email: 'admin@example.com'},// Optional: encrypt private keys before storing in RedisencryptFn: async(value)=>{// your encryption logicreturnencryptedValue;},decryptFn: async(value)=>{// your decryption logicreturndecryptedValue;}});// Retrieve or acquire a certificateconstcertData=awaitcerts.getCertificate('example.com');// certData.cert - PEM certificate// certData.privateKey - PEM private key// certData.ca - array of CA chain certificates// certData.validTo - expiration date// ACME HTTP-01 challenge handlerapp.get('/.well-known/acme-challenge/:token',(req,res)=>{consttoken=req.params.token;constdomain=req.get('host');certs.routeHandler(domain,token).then(challenge=>{res.status(200).set('content-type','text/plain').send(challenge);}).catch(err=>{res.status(err.responseCode||500).send({error: err.message,code: err.code});});});

Constructor Options

OptionTypeDefaultDescription
redisObjectrequiredioredis (or compatible) client instance
namespaceStringundefinedKey prefix for Redis storage
encryptFnFunctionidentityAsync function to encrypt private keys before storage
decryptFnFunctionidentityAsync function to decrypt private keys after retrieval
acme.environmentString'development''development' (staging) or 'production'
acme.directoryUrlStringLE staging URLACME directory URL
acme.emailStringSubscriber email for the ACME account
acme.caaDomainsArray['letsencrypt.org']Allowed CAA record domains
acme.keyBitsNumber2048RSA key size for ACME account key
acme.keyExponentNumber65537RSA public exponent for ACME account key
keyBitsNumber2048RSA key size for domain certificates
keyExponentNumber65537RSA public exponent for domain certificates
loggerObjectpino instanceLogger (pino-compatible)

API

Certs.create(options)

Static factory method. Returns a new Certs instance.

getCertificate(domain, skipAcquire?)

Returns stored certificate data for the domain. If the certificate is missing or expired, automatically acquires a new one via ACME unless skipAcquire is true.

Returns an object with cert, privateKey, ca, validFrom, validTo, altNames, serialNumber, fingerprint, status, and lastError, or false if no certificate exists.

acquireCert(domain)

Forces certificate acquisition or renewal for the domain. Validates the domain name and CAA records, obtains a distributed lock, generates a CSR, and requests a certificate via ACME HTTP-01 challenge. Falls back to existing certificate data on error.

routeHandler(domain, token)

Resolves an ACME HTTP-01 challenge. Use this as the handler for GET /.well-known/acme-challenge/:token requests. Returns the keyAuthorization string on success or throws with a responseCode property on failure.

listCertificateDomains()

Returns a sorted array of all domain names that have certificate records.

deleteCertificateData(domain)

Removes all stored certificate data for the domain.

Automatic Renewal

Certificates are automatically renewed when retrieved via getCertificate() if they expire within 30 days. After a failed renewal attempt, a short safety lock prevents repeated retries.

License

ISC

About

Manage certificates

Resources

Security policy

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages