@small-tech/auto-encrypt 4.3.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,28 @@
1
1
  # Auto Encrypt
2
2
 
3
- Automatically provisions and renews [Let’s Encrypt](https://letsencrypt.org) TLS certificates on [Node.js](https://nodejs.org) [https](https://nodejs.org/dist/latest-v12.x/docs/api/https.html) servers (including [Kitten](https://kitten.small-web.org), [Polka](https://github.com/lukeed/polka/tree/next), [Express.js](https://expressjs.com/), etc.)
3
+ Automatically provisions and renews [Let’s Encrypt](https://letsencrypt.org) TLS certificates on [Node.js](https://nodejs.org) [https](https://nodejs.org/docs/latest-v24.x/api/https.html) servers. As used in [@small-tech/https](https://codeberg.org/small-tech/https) and [Kitten](https://kitten.small-web.org).
4
4
 
5
5
  Implements the subset of RFC 8555 – Automatic Certificate Management Environment (ACME) – necessary for a client to support TLS certificate provisioning from Let’s Encrypt using HTTP-01 challenges.
6
6
 
7
7
  ## How it works
8
8
 
9
- The first time your web site is hit, it takes a couple of seconds to load as your Let’s Encrypt TLS certificates are automatically provisioned for you. From there on, your certificates are seamlessly renewed 30 days before their expiry date.
9
+ Let’s Encrypt TLS certificates are automatically provisioned for you before your server starts.
10
+
11
+ Auto Encrypt uses the new Let’s Encrypt `shortlived` profile *exclusively*, which means certificates are valid for (and renewed before) 160 hours. This should occur at around the 80 hour mark of the certificate’s lifespan, in accordance with data returned by ACME Renewal Information (ARI).
12
+
13
+ Auto Encrypt also implements automatic support for obtaining certificates on IPv4 and IPv6 addresses to enable the use of [Web Numbers](https://ar.al/2025/06/25/web-numbers/) on the Small Web.
10
14
 
11
15
  When not provisioning certificates, Auto Encrypt also forwards HTTP calls to HTTPS on your server.
12
16
 
13
17
  ## Compatibility
14
18
 
15
- Node.js 18.20.0+.
19
+ Node.js 18.20.0+ on Linux and macOS.
20
+
21
+ All tests pass on Node.js LTS (version 24).
16
22
 
17
- All tests pass on Node.js LTS (version 22).
23
+ > 💡 The module was tested to run on Windows 10 & 11 but it is no longer supported on Windows as [Microsoft is complicit in Israel’s genocide of the Palestinian people](https://www.bdsmovement.net/microsoft) and [Small Technology Foundation](https://small-tech.org/) stands in solidarity with the [Boycott, Divestment, and Sanctions (BDS) movement](https://www.bdsmovement.net/). **Windows is an ad-infested and surveillance-ridden dumpster fire of an operating system and, alongside supporting genocide, you are putting both yourself and others at risk by using it.**
24
+ >
25
+ > 🇵🇸 To support families facing genocide in Gaza, consider [donating to them via Gaza Verified](https://gaza-verified.org/donate/).
18
26
 
19
27
  ## Installation
20
28
 
@@ -27,46 +35,53 @@ npm i @small-tech/auto-encrypt
27
35
  ### Instructions
28
36
 
29
37
  1. Import the module:
30
-
38
+
31
39
  ```js
32
40
  import AutoEncrypt from '@small-tech/auto-encrypt'
33
41
  ```
34
42
 
35
- 2. Prefix your server creation code with a reference to the Auto Encrypt class:
36
-
37
- ```js
38
- // const server = https.createServer(…) becomes
39
- const server = AutoEncrypt.https.createServer(…)
40
- ```
43
+ 2. Create your server:
41
44
 
42
- 3. When done, close your server as you would normally:
43
-
44
- ```js
45
- server.close(() => {
46
- console.log('The server is now closed.')
47
- })
48
- ```
45
+ ```js
46
+ // const server = https.createServer(…) becomes
47
+ const server = await AutoEncrypt.https.createServer(…)
48
+ ```
49
+
50
+ > 💡 Auto Encrypt supports _one_ server per Node process. If you call `createServer()` again, any arguments you pass will be ignored and your will get the same server back. To create another server in the same process (e.g., when testing), call `await server.close()` first (see below) and then create the new server.
51
+
52
+ 3. When done, close your server:
53
+
54
+ ```js
55
+ await server.close()
56
+ console.info ('The server is now closed.')
57
+ ```
49
58
 
50
59
  ### Example
51
60
 
52
- The following code creates an HTTPS server running on port 443 that automatically provisions and renews TLS certificates from [Let’s Encrypt](https://letsencrypt.org) for the domains _<hostname>_ and _www.<hostname>_.
61
+ The following code creates an HTTPS server running on port 443 that automatically provisions and renews TLS certificates from [Let’s Encrypt](https://letsencrypt.org) for the domains _<hostname>_.
53
62
 
54
63
  ```js
55
64
  import AutoEncrypt from '@small-tech/auto-encrypt'
56
65
 
57
- const server = AutoEncrypt.https.createServer((request, response) => {
66
+ const server = await AutoEncrypt.https.createServer((_request, response) => {
58
67
  response.end('Hello, world')
59
68
  })
60
69
 
61
- server.listen(() => {
62
- console.log(`Auto-encrypted HTTPS server is running at ${os.hostname()} and www.${os.hostname()}.`)
63
- })
70
+ await server.listen()
64
71
 
65
- // Later…
72
+ const domain = os.hostname()
73
+ console.info(`Auto-encrypted HTTPS server is running at ${domain}.`)
66
74
 
67
- server.close(() => {
68
- console.log('The server is now closed.')
69
- })
75
+ try {
76
+ const response = await fetch(`https://${domain}/`)
77
+ const responseText = await response.text()
78
+ console.info('Server says:', responseText)
79
+ } catch (error) {
80
+ console.error(error)
81
+ }
82
+
83
+ await server.close()
84
+ console.info ('The server is now closed.')
70
85
  ```
71
86
 
72
87
  Note that on Linux, ports 80 and 443 require special privileges. Please see [A note on Linux and the security farce that is “privileged ports”](#a-note-on-linux-and-the-security-farce-that-is-priviliged-ports). If you just need a [Small Web](https://ar.al/2024/06/24/small-web-computer-science-colloquium-at-university-of-groningen/) server that handles all that and more for you (or to see how to implement privilege escalation seamlessly in your own servers, see [Kitten](https://kitten.small-web.org)).
@@ -77,6 +92,12 @@ You can customise the default configuration by adding Auto Encrypt-specific opti
77
92
 
78
93
  You can specify the domains you want the certificate to support, whether the Let’s Encrypt staging server or a local [Pebble](https://github.com/letsencrypt/pebble) server should be used instead of the default production server (useful during development and testing), and to specify a custom settings path for your Let’s Encrypt account and certificate information to be stored in.
79
94
 
95
+ You can also set the `ipv4` flag to `true` to make Auto Encrypt auto-detect your external IPv4 address (using https://ip.small-web.org/) and provision a certificate for it.
96
+
97
+ Similarly, you can set the `ipv6` flag to `true` to make Auto Encrypt auto-detect stable IPv6 addresses on your machine (this is done locally) and provision certificates for them.
98
+
99
+ If you pass `ipOnly: true`, then certificates will only be provisioned for IP addresses. This turns off the automatic provisioning of certificates for the machine’s hostname and overrides any domains you may have passed in the `domains` array.
100
+
80
101
  (Auto Encrypt uses [Node Pebble](https://codeberg.org/small-tech/node-pebble) to enable testing with a local Pebble server from Node.js.)
81
102
 
82
103
  ### Example
@@ -88,13 +109,14 @@ const options = {
88
109
  // Regular HTTPS server and TLS server options, if any, go here.
89
110
 
90
111
  // Optional Auto Encrypt options:
91
- domains: ['first.domain', 'second.domain', /* … */],
112
+ ipv4: true,
113
+ ipOnly: true,
92
114
  server: AutoEncrypt.server.STAGING,
93
115
  settingsPath: '/custom/settings/path'
94
116
  }
95
117
 
96
118
  // Pass the options object to https.createServer()
97
- const server = AutoEncrypt.https.createServer(options, listener)
119
+ const server = await AutoEncrypt.https.createServer(options, listener)
98
120
 
99
121
  // …
100
122
  ```
@@ -106,38 +128,19 @@ Here is the full list of Auto Encrypt options (all optional) and their defaults:
106
128
  - `domains`: the hostname of the current computer and the www subdomain at that hostname.
107
129
  - `server`: it will use the production server (which has [rate limits](https://letsencrypt.org/docs/rate-limits/)). Valid values are `AutoEncrypt.server.(PEBBLE|STAGING|PRODUCTION)`.
108
130
  - `settingsPath`: _~/.small-tech.org/auto-encrypt/_
131
+ - `ipv4`: true/false – whether or not to automatically detect external IPv4 address and add it to the TLS certificate being provisioned.
132
+ - `ipv6`: true/false – whether or not to automatically detect stable IPv6 addresses and add them to the TLS certificate being provisioned.
133
+ - `ipOnly`: When true, the machines hostname is not added to the TLS certificate and neither are the domains passed in the `domains` array, if any.
109
134
 
110
135
  ## Making a graceful exit
111
136
 
112
- When you’re ready to exit your app, just call the `server.close()` method as you would normally. This will allow Auto Encrypt to perform housekeeping like shutting own the HTTP server that is used for answering Let’s Encrypt challenges as well as performing HTTP to HTTPS redirections and to destroy its certificate check update interval which, if not destroyed, will prevent your app from exiting.
137
+ When you’re ready to exit your app, just call the `await server.close()` method. This will allow Auto Encrypt to perform housekeeping like shutting own the HTTP server that is used for answering Let’s Encrypt challenges as well as performing HTTP to HTTPS redirections and to destroy its certificate check update interval which, if not destroyed, will prevent your app from exiting.
113
138
 
114
139
  ## Developer documentation
115
140
 
116
141
  If you want to help improve Auto Encrypt or better understand how it is structured and operates, please see the [developer documentation](https://codeberg.org/small-tech/auto-encrypt/src/branch/main/developer-documentation.md).
117
142
 
118
- ## Examples
119
-
120
- ### Regular https
121
-
122
- ```js
123
- import AutoEncrypt from '@small-tech/auto-encrypt'
124
-
125
- const server = AutoEncrypt.https.createServer({ domains: ['dev.ar.al'] }, (request, response) => {
126
- response.end('Hello, world!')
127
- })
128
-
129
- server.listen(() => {
130
- console.log('Auto-encrypted HTTPS server is running at https://dev.ar.al')
131
- })
132
-
133
- // Later…
134
-
135
- server.close(() => {
136
- console.log('The server is now closed.')
137
- })
138
- ```
139
-
140
- ### Express.js
143
+ ### Express.js example
141
144
 
142
145
  ```js
143
146
  const express = require('express')
@@ -148,21 +151,18 @@ app.get('/', (request, response) => {
148
151
  response.end('Hello, world!')
149
152
  })
150
153
 
151
- const server = AutoEncrypt.https.createServer(
152
- autoEncrypt({ domains: ['dev.ar.al'] }),
154
+ const myDomain = 'dev.ar.al'
155
+ const server = await AutoEncrypt.https.createServer(
156
+ { domains: [myDomain] },
153
157
  app
154
158
  )
155
159
 
156
- server.listen(() => {
157
- console.log('Auto-encrypted Express server is running at https://dev.ar.al')
158
- })
159
-
160
+ await server.listen()
161
+ console.log(`Auto-encrypted Express server is running at https://${myDomain}`)
160
162
 
161
163
  // Later…
162
-
163
- server.close(() => {
164
- console.log('The server is now closed.')
165
- })
164
+ await server.close()
165
+ console.info ('The server is now closed.')
166
166
  ```
167
167
 
168
168
  ## Like this? Fund us!
@@ -226,7 +226,7 @@ A [💕 Small Web](https://ar.al/2020/08/07/what-is-the-small-web/) development
226
226
 
227
227
  ## Tests and coverage
228
228
 
229
- This project aims for > 80% coverage. At a recent check, coverage was at 97.42% (statements), 92.64% (branch), 91.49% (functions), 97.42% (lines).
229
+ This project aims for > 80% coverage and currently has > 90%.
230
230
 
231
231
  To see the current state of code coverage, run `npm run coverage`.
232
232
 
package/index.d.ts ADDED
@@ -0,0 +1,152 @@
1
+ /**
2
+ Automatically provisions and renews Let’s Encrypt™ TLS certificates for Node.js® https servers (as used in @small-tech/https and Kitten.)
3
+
4
+ Implements the subset of RFC 8555 – Automatic Certificate Management Environment (ACME) – necessary for a Node.js https server to provision TLS certificates from Let’s Encrypt using the HTTP-01 challenge.
5
+
6
+ The certificates are provisioned, if necessary, at time of server creation, prior to server start with hourly renewal checks and automatic renewals.
7
+
8
+ Auto Encrypt uses the new Let’s Encrypt `shortlived` profile *exclusively*, which means certificates are valid for (and renewed before) 160 hours. This should occur at around the 80 hour mark of the certificate’s lifespan, in accordance with data returned by ACME Renewal Information (ARI).
9
+
10
+ Auto Encrypt also implements automatic support for obtaining certificates on IPv4 and IPv6 addresses to enable the use of Web Numbers on the Small Web (see https://ar.al/2025/06/25/web-numbers/).
11
+
12
+ Remember that Auto Encrypt is for the Small Web (peer-to-peer Web). You might find it useful for everyday web purposes also but it is specifically focused for Small Web use cases and explicitly does not aim to be a generic or exhaustive ACME client.
13
+
14
+ @module @small-tech/auto-encrypt
15
+ @copyright © 2020-present Aral Balkan, Small Technology Foundation.
16
+ @license AGPLv3 or later.
17
+ */
18
+
19
+ import { Server, ServerOptions } from 'node:https'
20
+ import { SecureContext } from 'node:tls'
21
+ import { IncomingMessage, ServerResponse } from 'node:http'
22
+
23
+ /**
24
+ Options for creating an AutoEncrypt server. Extends Node’s https.ServerOptions.
25
+ */
26
+ export interface AutoEncryptOptions extends ServerOptions {
27
+ /**
28
+ The domains for which to provision and renew certificates.
29
+ */
30
+ domains?: string[]
31
+
32
+ /**
33
+ Type of Let’s Encrypt server to use. Use AutoEncrypt.serverType constants.
34
+ */
35
+ serverType?: number
36
+
37
+ /**
38
+ Path where settings and certificates will be stored.
39
+ */
40
+ settingsPath?: string
41
+
42
+ /**
43
+ Whether to automatically detect and use the machine’s IPv4 address.
44
+ */
45
+ ipv4?: boolean
46
+
47
+ /**
48
+ Whether to automatically detect and use the machine’s stable IPv6 addresses.
49
+ */
50
+ ipv6?: boolean
51
+
52
+ /**
53
+ If true, only IP addresses (IPv4/IPv6 as requested) are used, ignoring the hostname.
54
+ */
55
+ ipOnly?: boolean
56
+ }
57
+
58
+ /**
59
+ Request listener function signature.
60
+ */
61
+ export type RequestListener = (req: IncomingMessage, res: ServerResponse) => void
62
+
63
+ /**
64
+ Enumeration of Let’s Encrypt server types.
65
+ */
66
+ export interface ServerType {
67
+ PRODUCTION: 0
68
+ STAGING: 1
69
+ PEBBLE: 2
70
+ MOCK: 3
71
+ }
72
+
73
+ /**
74
+ Represents a Let’s Encrypt TLS certificate.
75
+ */
76
+ export interface Certificate {
77
+ getSecureContext(): Promise<SecureContext | null>
78
+ checkForRenewal(): Promise<void>
79
+ stopCheckingForRenewal(): void
80
+ readonly isProvisioned: boolean
81
+ readonly pem: string | Buffer | Array<string | Buffer> | undefined
82
+ }
83
+
84
+ /**
85
+ Represents a Let’s Encrypt server instance.
86
+ */
87
+ export interface LetsEncryptServer {
88
+ readonly type: number
89
+ readonly name: string
90
+ readonly endpoint: string
91
+ }
92
+
93
+ /**
94
+ The monkey-patched https.Server instance returned by AutoEncrypt.
95
+ */
96
+ export interface AutoEncryptedServer extends Server {
97
+ listen(...args:any[]): Promise<AutoEncryptedServer>
98
+ async close(): Promise<void>
99
+ }
100
+
101
+ /**
102
+ Auto Encrypt is a static class. Please do not instantiate.
103
+
104
+ Use: AutoEncrypt.https.createServer(…)
105
+
106
+ @alias module:@small-tech/auto-encrypt
107
+ @hideconstructor
108
+ */
109
+ export default class AutoEncrypt {
110
+ static letsEncryptServer: LetsEncryptServer | null
111
+ static defaultDomains : string[] | null
112
+ static domains : string[] | null
113
+ static settingsPath : string | null
114
+ static listener : RequestListener | null
115
+ static certificate : Certificate | null
116
+
117
+ /**
118
+ Enumeration.
119
+
120
+ @static
121
+ @readonly
122
+ */
123
+ static readonly serverType: ServerType
124
+
125
+ /**
126
+ By aliasing the https property to the AutoEncrypt static class itself, we enable people to add AutoEncrypt to their existing apps by requiring the module and prefixing their https.createServer(…) line with AutoEncrypt:
127
+
128
+ @example import AutoEncrypt from '@small-tech/auto-encrypt'
129
+ const server = AutoEncrypt.https.createServer()
130
+
131
+ @static
132
+ */
133
+ static readonly https: typeof AutoEncrypt
134
+
135
+ /**
136
+ Automatically manages Let’s Encrypt certificate provisioning and renewal for Node.js https servers using the HTTP-01 challenge on first hit of an HTTPS route via use of the Server Name Indication (SNI) callback.
137
+
138
+ @static
139
+
140
+ @param {AutoEncryptOptions | RequestListener} [options] Configuration options or a request listener.
141
+ @param {RequestListener} [listener] Optional request listener if options were provided.
142
+ @returns {Promise<AutoEncryptedServer>} The server instance returned by Node’s https.createServer() method.
143
+ */
144
+ static async createServer(options?: AutoEncryptOptions | RequestListener, listener?: RequestListener): Promise<AutoEncryptedServer>
145
+
146
+ /**
147
+ Shut Auto Encrypt down. Do this before app exit. Performs necessary clean-up and removes any references that might cause the app to not exit.
148
+ */
149
+ static async shutdown(): Promise<void>
150
+
151
+ private constructor()
152
+ }