@push.rocks/smartacme 8.0.0 → 9.0.0

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.
Files changed (50) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/acme/acme.classes.account.d.ts +20 -0
  3. package/dist_ts/acme/acme.classes.account.js +37 -0
  4. package/dist_ts/acme/acme.classes.challenge.d.ts +22 -0
  5. package/dist_ts/acme/acme.classes.challenge.js +36 -0
  6. package/dist_ts/acme/acme.classes.client.d.ts +68 -0
  7. package/dist_ts/acme/acme.classes.client.js +71 -0
  8. package/dist_ts/acme/acme.classes.crypto.d.ts +50 -0
  9. package/dist_ts/acme/acme.classes.crypto.js +180 -0
  10. package/dist_ts/acme/acme.classes.directory.d.ts +13 -0
  11. package/dist_ts/acme/acme.classes.directory.js +14 -0
  12. package/dist_ts/acme/acme.classes.error.d.ts +44 -0
  13. package/dist_ts/acme/acme.classes.error.js +41 -0
  14. package/dist_ts/acme/acme.classes.http-client.d.ts +36 -0
  15. package/dist_ts/acme/acme.classes.http-client.js +201 -0
  16. package/dist_ts/acme/acme.classes.order.d.ts +40 -0
  17. package/dist_ts/acme/acme.classes.order.js +100 -0
  18. package/dist_ts/acme/acme.interfaces.d.ts +64 -0
  19. package/dist_ts/acme/acme.interfaces.js +5 -0
  20. package/dist_ts/acme/index.d.ts +5 -0
  21. package/dist_ts/acme/index.js +5 -0
  22. package/dist_ts/certmanagers/mongo.js +1 -2
  23. package/dist_ts/plugins.d.ts +2 -7
  24. package/dist_ts/plugins.js +5 -11
  25. package/dist_ts/smartacme.classes.smartacme.d.ts +3 -1
  26. package/dist_ts/smartacme.classes.smartacme.js +40 -13
  27. package/npmextra.json +12 -6
  28. package/package.json +23 -24
  29. package/readme.hints.md +27 -3
  30. package/readme.md +201 -263
  31. package/ts/00_commitinfo_data.ts +1 -1
  32. package/ts/acme/acme.classes.account.ts +45 -0
  33. package/ts/acme/acme.classes.challenge.ts +45 -0
  34. package/ts/acme/acme.classes.client.ts +99 -0
  35. package/ts/acme/acme.classes.crypto.ts +220 -0
  36. package/ts/acme/acme.classes.directory.ts +13 -0
  37. package/ts/acme/acme.classes.error.ts +55 -0
  38. package/ts/acme/acme.classes.http-client.ts +236 -0
  39. package/ts/acme/acme.classes.order.ts +125 -0
  40. package/ts/acme/acme.interfaces.ts +74 -0
  41. package/ts/acme/index.ts +16 -0
  42. package/ts/certmanagers/mongo.ts +0 -1
  43. package/ts/plugins.ts +3 -14
  44. package/ts/smartacme.classes.smartacme.ts +41 -16
  45. package/dist_ts/certmanagers.d.ts +0 -42
  46. package/dist_ts/certmanagers.js +0 -86
  47. package/dist_ts/smartacme.classes.certmanager.d.ts +0 -43
  48. package/dist_ts/smartacme.classes.certmanager.js +0 -92
  49. package/dist_ts/smartacme.plugins.d.ts +0 -21
  50. package/dist_ts/smartacme.plugins.js +0 -28
@@ -0,0 +1,36 @@
1
+ import type { IAcmeDirectory, IAcmeHttpResponse } from './acme.interfaces.js';
2
+ export type TAcmeLogger = (level: string, message: string, data?: any) => void;
3
+ /**
4
+ * JWS-signed HTTP transport for ACME protocol.
5
+ * Handles nonce management, bad-nonce retries, and signed requests.
6
+ */
7
+ export declare class AcmeHttpClient {
8
+ private directoryUrl;
9
+ private accountKeyPem;
10
+ private directory;
11
+ private nonce;
12
+ kid: string | null;
13
+ private logger?;
14
+ constructor(directoryUrl: string, accountKeyPem: string, logger?: TAcmeLogger);
15
+ private log;
16
+ /**
17
+ * GET the ACME directory (cached after first call)
18
+ */
19
+ getDirectory(): Promise<IAcmeDirectory>;
20
+ /**
21
+ * Fetch a fresh nonce via HEAD to newNonce
22
+ */
23
+ getNonce(): Promise<string>;
24
+ /**
25
+ * Send a JWS-signed POST request to an ACME endpoint.
26
+ * Handles nonce rotation and bad-nonce retries (up to 5).
27
+ * payload=null means POST-as-GET.
28
+ */
29
+ signedRequest(url: string, payload: any | null, options?: {
30
+ useJwk?: boolean;
31
+ }): Promise<IAcmeHttpResponse>;
32
+ /**
33
+ * Raw HTTP request using native node:https
34
+ */
35
+ private httpRequest;
36
+ }
@@ -0,0 +1,201 @@
1
+ import * as https from 'node:https';
2
+ import * as http from 'node:http';
3
+ import { AcmeCrypto } from './acme.classes.crypto.js';
4
+ import { AcmeError } from './acme.classes.error.js';
5
+ /**
6
+ * JWS-signed HTTP transport for ACME protocol.
7
+ * Handles nonce management, bad-nonce retries, and signed requests.
8
+ */
9
+ export class AcmeHttpClient {
10
+ constructor(directoryUrl, accountKeyPem, logger) {
11
+ this.directory = null;
12
+ this.nonce = null;
13
+ this.kid = null;
14
+ this.directoryUrl = directoryUrl;
15
+ this.accountKeyPem = accountKeyPem;
16
+ this.logger = logger;
17
+ }
18
+ log(level, message, data) {
19
+ if (this.logger) {
20
+ this.logger(level, message, data);
21
+ }
22
+ }
23
+ /**
24
+ * GET the ACME directory (cached after first call)
25
+ */
26
+ async getDirectory() {
27
+ if (this.directory)
28
+ return this.directory;
29
+ const response = await this.httpRequest(this.directoryUrl, 'GET');
30
+ if (response.status !== 200) {
31
+ throw new AcmeError({
32
+ status: response.status,
33
+ type: response.data?.type || '',
34
+ detail: `Failed to fetch ACME directory`,
35
+ url: this.directoryUrl,
36
+ });
37
+ }
38
+ this.directory = response.data;
39
+ return this.directory;
40
+ }
41
+ /**
42
+ * Fetch a fresh nonce via HEAD to newNonce
43
+ */
44
+ async getNonce() {
45
+ if (this.nonce) {
46
+ const n = this.nonce;
47
+ this.nonce = null;
48
+ return n;
49
+ }
50
+ const dir = await this.getDirectory();
51
+ const response = await this.httpRequest(dir.newNonce, 'HEAD');
52
+ const nonce = response.headers['replay-nonce'];
53
+ if (!nonce) {
54
+ throw new Error('No replay-nonce header in newNonce response');
55
+ }
56
+ return nonce;
57
+ }
58
+ /**
59
+ * Send a JWS-signed POST request to an ACME endpoint.
60
+ * Handles nonce rotation and bad-nonce retries (up to 5).
61
+ * payload=null means POST-as-GET.
62
+ */
63
+ async signedRequest(url, payload, options) {
64
+ const maxRetries = 5;
65
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
66
+ const nonce = await this.getNonce();
67
+ const jwsOptions = { nonce };
68
+ if (options?.useJwk) {
69
+ jwsOptions.jwk = AcmeCrypto.getJwk(this.accountKeyPem);
70
+ }
71
+ else if (this.kid) {
72
+ jwsOptions.kid = this.kid;
73
+ }
74
+ else {
75
+ jwsOptions.jwk = AcmeCrypto.getJwk(this.accountKeyPem);
76
+ }
77
+ const jws = AcmeCrypto.createJws(this.accountKeyPem, url, payload, jwsOptions);
78
+ const body = JSON.stringify(jws);
79
+ const response = await this.httpRequest(url, 'POST', body, {
80
+ 'Content-Type': 'application/jose+json',
81
+ });
82
+ // Save nonce from response for reuse
83
+ if (response.headers['replay-nonce']) {
84
+ this.nonce = response.headers['replay-nonce'];
85
+ }
86
+ this.log('debug', `ACME request: POST ${url} → ${response.status}`);
87
+ // Retry on bad-nonce
88
+ if (response.status === 400 &&
89
+ response.data?.type === 'urn:ietf:params:acme:error:badNonce') {
90
+ this.log('debug', `Bad nonce on attempt ${attempt + 1}, retrying`);
91
+ if (attempt < maxRetries) {
92
+ this.nonce = null; // Force fresh nonce
93
+ continue;
94
+ }
95
+ }
96
+ // Throw on error responses
97
+ if (response.status >= 400) {
98
+ const retryAfterRaw = response.headers['retry-after'];
99
+ let retryAfter = 0;
100
+ if (retryAfterRaw) {
101
+ const parsed = parseInt(retryAfterRaw, 10);
102
+ if (!isNaN(parsed)) {
103
+ retryAfter = parsed;
104
+ }
105
+ }
106
+ const acmeError = new AcmeError({
107
+ status: response.status,
108
+ type: response.data?.type || '',
109
+ detail: response.data?.detail || JSON.stringify(response.data),
110
+ subproblems: response.data?.subproblems,
111
+ url,
112
+ retryAfter,
113
+ });
114
+ if (acmeError.isRateLimited) {
115
+ this.log('warn', `RATE LIMITED: ${url} (HTTP ${response.status}), Retry-After: ${retryAfter}s`, {
116
+ type: acmeError.type,
117
+ detail: acmeError.detail,
118
+ retryAfter,
119
+ });
120
+ }
121
+ else {
122
+ this.log('warn', `ACME error: ${url} (HTTP ${response.status})`, {
123
+ type: acmeError.type,
124
+ detail: acmeError.detail,
125
+ });
126
+ }
127
+ throw acmeError;
128
+ }
129
+ return response;
130
+ }
131
+ throw new Error('Max bad-nonce retries exceeded');
132
+ }
133
+ /**
134
+ * Raw HTTP request using native node:https
135
+ */
136
+ httpRequest(url, method, body, headers) {
137
+ return new Promise((resolve, reject) => {
138
+ const urlObj = new URL(url);
139
+ const isHttps = urlObj.protocol === 'https:';
140
+ const lib = isHttps ? https : http;
141
+ const requestHeaders = {
142
+ ...headers,
143
+ 'User-Agent': 'smartacme-acme-client/1.0',
144
+ };
145
+ if (body) {
146
+ requestHeaders['Content-Length'] = Buffer.byteLength(body);
147
+ }
148
+ const options = {
149
+ hostname: urlObj.hostname,
150
+ port: urlObj.port || (isHttps ? 443 : 80),
151
+ path: urlObj.pathname + urlObj.search,
152
+ method,
153
+ headers: requestHeaders,
154
+ };
155
+ const req = lib.request(options, (res) => {
156
+ const chunks = [];
157
+ res.on('data', (chunk) => chunks.push(chunk));
158
+ res.on('end', () => {
159
+ const responseBody = Buffer.concat(chunks).toString('utf-8');
160
+ // Normalize headers to lowercase single-value
161
+ const responseHeaders = {};
162
+ for (const [key, value] of Object.entries(res.headers)) {
163
+ if (typeof value === 'string') {
164
+ responseHeaders[key.toLowerCase()] = value;
165
+ }
166
+ else if (Array.isArray(value)) {
167
+ responseHeaders[key.toLowerCase()] = value[0];
168
+ }
169
+ }
170
+ // Parse JSON if applicable, otherwise return raw string
171
+ let data;
172
+ const contentType = responseHeaders['content-type'] || '';
173
+ if (contentType.includes('json')) {
174
+ try {
175
+ data = JSON.parse(responseBody);
176
+ }
177
+ catch {
178
+ data = responseBody;
179
+ }
180
+ }
181
+ else {
182
+ data = responseBody;
183
+ }
184
+ resolve({
185
+ status: res.statusCode || 0,
186
+ headers: responseHeaders,
187
+ data,
188
+ });
189
+ });
190
+ });
191
+ req.on('error', reject);
192
+ req.setTimeout(30000, () => {
193
+ req.destroy(new Error('Request timeout'));
194
+ });
195
+ if (body)
196
+ req.write(body);
197
+ req.end();
198
+ });
199
+ }
200
+ }
201
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmh0dHAtY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvYWNtZS9hY21lLmNsYXNzZXMuaHR0cC1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEtBQUssTUFBTSxZQUFZLENBQUM7QUFDcEMsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUtwRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sY0FBYztJQVF6QixZQUFZLFlBQW9CLEVBQUUsYUFBcUIsRUFBRSxNQUFvQjtRQUxyRSxjQUFTLEdBQTBCLElBQUksQ0FBQztRQUN4QyxVQUFLLEdBQWtCLElBQUksQ0FBQztRQUM3QixRQUFHLEdBQWtCLElBQUksQ0FBQztRQUkvQixJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsYUFBYSxHQUFHLGFBQWEsQ0FBQztRQUNuQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztJQUN2QixDQUFDO0lBRU8sR0FBRyxDQUFDLEtBQWEsRUFBRSxPQUFlLEVBQUUsSUFBVTtRQUNwRCxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDcEMsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxZQUFZO1FBQ2hCLElBQUksSUFBSSxDQUFDLFNBQVM7WUFBRSxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7UUFDMUMsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsS0FBSyxDQUFDLENBQUM7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
@@ -0,0 +1,40 @@
1
+ import type { AcmeHttpClient } from './acme.classes.http-client.js';
2
+ import type { IAcmeAuthorization, IAcmeIdentifier, IAcmeOrder } from './acme.interfaces.js';
3
+ /**
4
+ * ACME order lifecycle management.
5
+ * Handles order creation, authorization retrieval, finalization, and certificate download.
6
+ */
7
+ export declare class AcmeOrderManager {
8
+ private httpClient;
9
+ constructor(httpClient: AcmeHttpClient);
10
+ /**
11
+ * Create a new ACME order for the given identifiers
12
+ */
13
+ create(opts: {
14
+ identifiers: IAcmeIdentifier[];
15
+ }): Promise<IAcmeOrder>;
16
+ /**
17
+ * Retrieve all authorizations for an order (POST-as-GET each authorization URL)
18
+ */
19
+ getAuthorizations(order: IAcmeOrder): Promise<IAcmeAuthorization[]>;
20
+ /**
21
+ * Finalize an order by submitting the CSR.
22
+ * Waits for the order to reach 'valid' status.
23
+ * Mutates the order object with updated status and certificate URL.
24
+ */
25
+ finalize(order: IAcmeOrder, csrPem: string): Promise<void>;
26
+ /**
27
+ * Download the certificate chain (PEM) from the order's certificate URL
28
+ */
29
+ getCertificate(order: IAcmeOrder): Promise<string>;
30
+ /**
31
+ * Poll an ACME resource (order or challenge) until it reaches 'valid' or 'ready' status.
32
+ * Uses exponential backoff with Retry-After header support.
33
+ */
34
+ waitForValidStatus(item: {
35
+ url: string;
36
+ }, opts?: {
37
+ maxAttempts?: number;
38
+ initialDelayMs?: number;
39
+ }): Promise<any>;
40
+ }
@@ -0,0 +1,100 @@
1
+ import { AcmeCrypto } from './acme.classes.crypto.js';
2
+ import { AcmeError } from './acme.classes.error.js';
3
+ /**
4
+ * ACME order lifecycle management.
5
+ * Handles order creation, authorization retrieval, finalization, and certificate download.
6
+ */
7
+ export class AcmeOrderManager {
8
+ constructor(httpClient) {
9
+ this.httpClient = httpClient;
10
+ }
11
+ /**
12
+ * Create a new ACME order for the given identifiers
13
+ */
14
+ async create(opts) {
15
+ const dir = await this.httpClient.getDirectory();
16
+ const response = await this.httpClient.signedRequest(dir.newOrder, {
17
+ identifiers: opts.identifiers,
18
+ });
19
+ const order = response.data;
20
+ // Capture order URL from Location header
21
+ order.url = response.headers['location'] || '';
22
+ return order;
23
+ }
24
+ /**
25
+ * Retrieve all authorizations for an order (POST-as-GET each authorization URL)
26
+ */
27
+ async getAuthorizations(order) {
28
+ const authorizations = [];
29
+ for (const authzUrl of order.authorizations) {
30
+ const response = await this.httpClient.signedRequest(authzUrl, null);
31
+ authorizations.push(response.data);
32
+ }
33
+ return authorizations;
34
+ }
35
+ /**
36
+ * Finalize an order by submitting the CSR.
37
+ * Waits for the order to reach 'valid' status.
38
+ * Mutates the order object with updated status and certificate URL.
39
+ */
40
+ async finalize(order, csrPem) {
41
+ // Convert PEM CSR to base64url DER for ACME
42
+ const csrDer = AcmeCrypto.pemToBuffer(csrPem);
43
+ const csrB64url = csrDer.toString('base64url');
44
+ const response = await this.httpClient.signedRequest(order.finalize, { csr: csrB64url });
45
+ // Update order with response data
46
+ const updatedOrder = response.data;
47
+ order.status = updatedOrder.status;
48
+ if (updatedOrder.certificate) {
49
+ order.certificate = updatedOrder.certificate;
50
+ }
51
+ // If not yet valid, poll until it is
52
+ if (order.status !== 'valid' && order.url) {
53
+ const finalOrder = await this.waitForValidStatus({ url: order.url });
54
+ order.status = finalOrder.status;
55
+ order.certificate = finalOrder.certificate;
56
+ }
57
+ }
58
+ /**
59
+ * Download the certificate chain (PEM) from the order's certificate URL
60
+ */
61
+ async getCertificate(order) {
62
+ if (!order.certificate) {
63
+ throw new Error('Order does not have a certificate URL - finalize first');
64
+ }
65
+ const response = await this.httpClient.signedRequest(order.certificate, null);
66
+ // Certificate chain is returned as PEM text
67
+ return typeof response.data === 'string' ? response.data : response.data.toString();
68
+ }
69
+ /**
70
+ * Poll an ACME resource (order or challenge) until it reaches 'valid' or 'ready' status.
71
+ * Uses exponential backoff with Retry-After header support.
72
+ */
73
+ async waitForValidStatus(item, opts) {
74
+ const maxAttempts = opts?.maxAttempts ?? 30;
75
+ const initialDelay = opts?.initialDelayMs ?? 1000;
76
+ for (let i = 0; i < maxAttempts; i++) {
77
+ const response = await this.httpClient.signedRequest(item.url, null);
78
+ const body = response.data;
79
+ if (body.status === 'valid' || body.status === 'ready') {
80
+ return body;
81
+ }
82
+ if (body.status === 'invalid') {
83
+ const challengeError = body.challenges?.find((c) => c.error)?.error;
84
+ throw new AcmeError({
85
+ status: 0,
86
+ type: challengeError?.type || 'urn:ietf:params:acme:error:rejectedIdentifier',
87
+ detail: challengeError?.detail || JSON.stringify(body),
88
+ subproblems: challengeError?.subproblems,
89
+ url: item.url,
90
+ });
91
+ }
92
+ // Respect Retry-After header, otherwise exponential backoff
93
+ const retryAfter = parseInt(response.headers['retry-after'] || '0', 10);
94
+ const delay = retryAfter > 0 ? retryAfter * 1000 : Math.min(initialDelay * Math.pow(2, i), 30000);
95
+ await new Promise((resolve) => setTimeout(resolve, delay));
96
+ }
97
+ throw new Error(`Timeout waiting for valid status after ${maxAttempts} attempts`);
98
+ }
99
+ }
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLm9yZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvYWNtZS9hY21lLmNsYXNzZXMub3JkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQVFwRDs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sZ0JBQWdCO0lBRzNCLFlBQVksVUFBMEI7UUFDcEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7SUFDL0IsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUF3QztRQUNuRCxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDakQsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFO1lBQ2pFLFdBQVcsRUFBRSxJQUFJLENBQUMsV0FBVztTQUM5QixDQUFDLENBQUM7UUFFSCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsSUFBa0IsQ0FBQztRQUMxQyx5Q0FBeUM7UUFDekMsS0FBSyxDQUFDLEdBQUcsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUMvQyxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxLQUFpQjtRQUN2QyxNQUFNLGNBQWMsR0FBeUIsRUFBRSxDQUFDO1FBQ2hELEtBQUssTUFBTSxRQUFRLElBQUksS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQzVDLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsUUFBUSxFQUFFLElBQUksQ0FB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
@@ -0,0 +1,64 @@
1
+ /**
2
+ * ACME Protocol interfaces per RFC 8555
3
+ */
4
+ export interface IAcmeDirectory {
5
+ newNonce: string;
6
+ newAccount: string;
7
+ newOrder: string;
8
+ newAuthz?: string;
9
+ revokeCert?: string;
10
+ keyChange?: string;
11
+ meta?: IAcmeDirectoryMeta;
12
+ }
13
+ export interface IAcmeDirectoryMeta {
14
+ termsOfService?: string;
15
+ website?: string;
16
+ caaIdentities?: string[];
17
+ externalAccountRequired?: boolean;
18
+ }
19
+ export interface IAcmeIdentifier {
20
+ type: 'dns';
21
+ value: string;
22
+ }
23
+ export interface IAcmeAccount {
24
+ status: string;
25
+ contact?: string[];
26
+ termsOfServiceAgreed?: boolean;
27
+ orders?: string;
28
+ }
29
+ export interface IAcmeAccountCreateRequest {
30
+ termsOfServiceAgreed: boolean;
31
+ contact?: string[];
32
+ }
33
+ export interface IAcmeOrder {
34
+ url: string;
35
+ status: string;
36
+ expires?: string;
37
+ identifiers: IAcmeIdentifier[];
38
+ authorizations: string[];
39
+ finalize: string;
40
+ certificate?: string;
41
+ }
42
+ export interface IAcmeAuthorization {
43
+ identifier: IAcmeIdentifier;
44
+ status: string;
45
+ expires?: string;
46
+ challenges: IAcmeChallenge[];
47
+ wildcard?: boolean;
48
+ }
49
+ export interface IAcmeChallenge {
50
+ type: string;
51
+ url: string;
52
+ status: string;
53
+ token: string;
54
+ validated?: string;
55
+ }
56
+ export interface IAcmeCsrOptions {
57
+ commonName: string;
58
+ altNames?: string[];
59
+ }
60
+ export interface IAcmeHttpResponse {
61
+ status: number;
62
+ headers: Record<string, string>;
63
+ data: any;
64
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * ACME Protocol interfaces per RFC 8555
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5pbnRlcmZhY2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvYWNtZS9hY21lLmludGVyZmFjZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUcifQ==
@@ -0,0 +1,5 @@
1
+ export { AcmeClient, type IAcmeClientOptions } from './acme.classes.client.js';
2
+ export { AcmeCrypto } from './acme.classes.crypto.js';
3
+ export { AcmeError } from './acme.classes.error.js';
4
+ export { ACME_DIRECTORY_URLS } from './acme.classes.directory.js';
5
+ export type { IAcmeDirectory, IAcmeDirectoryMeta, IAcmeIdentifier, IAcmeAccount, IAcmeAccountCreateRequest, IAcmeOrder, IAcmeAuthorization, IAcmeChallenge, IAcmeCsrOptions, IAcmeHttpResponse, } from './acme.interfaces.js';
@@ -0,0 +1,5 @@
1
+ export { AcmeClient } from './acme.classes.client.js';
2
+ export { AcmeCrypto } from './acme.classes.crypto.js';
3
+ export { AcmeError } from './acme.classes.error.js';
4
+ export { ACME_DIRECTORY_URLS } from './acme.classes.directory.js';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9hY21lL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQTJCLE1BQU0sMEJBQTBCLENBQUM7QUFDL0UsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQyJ9
@@ -9,7 +9,6 @@ export class MongoCertManager {
9
9
  */
10
10
  constructor(mongoDescriptor) {
11
11
  this.db = new plugins.smartdata.SmartdataDb(mongoDescriptor);
12
- // Use a single EasyStore document to hold all certs keyed by domainName
13
12
  this.store = new plugins.smartdata.EasyStore('smartacme-certs', this.db);
14
13
  }
15
14
  async init() {
@@ -37,4 +36,4 @@ export class MongoCertManager {
37
36
  await this.store.wipe();
38
37
  }
39
38
  }
40
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ28uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jZXJ0bWFuYWdlcnMvbW9uZ28udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRTdEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQUkzQjs7T0FFRztJQUNILFlBQVksZUFBbUQ7UUFDN0QsSUFBSSxDQUFDLEVBQUUsR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQzdELHdFQUF3RTtRQUN4RSxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQzFDLGlCQUFpQixFQUNqQixJQUFJLENBQUMsRUFBRSxDQUNSLENBQUM7SUFDSixDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUk7UUFDZixNQUFNLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDdkIsQ0FBQztJQUVNLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxVQUFrQjtRQUNqRCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2xELE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQy9DLENBQUM7SUFFTSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsSUFBbUI7UUFDL0MscUNBQXFDO1FBQ3JDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUMxRCxDQUFDO0lBRU0sS0FBSyxDQUFDLGlCQUFpQixDQUFDLFVBQWtCO1FBQy9DLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUVNLEtBQUssQ0FBQyxLQUFLO1FBQ2hCLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUN4QixDQUFDO0lBQ0Q7O09BRUc7SUFDSSxLQUFLLENBQUMsSUFBSTtRQUNmLG1DQUFtQztRQUNuQyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDMUIsQ0FBQztDQUNGIn0=
39
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ28uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jZXJ0bWFuYWdlcnMvbW9uZ28udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFFekMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRTdEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQUkzQjs7T0FFRztJQUNILFlBQVksZUFBbUQ7UUFDN0QsSUFBSSxDQUFDLEVBQUUsR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQzdELElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FDMUMsaUJBQWlCLEVBQ2pCLElBQUksQ0FBQyxFQUFFLENBQ1IsQ0FBQztJQUNKLENBQUM7SUFFTSxLQUFLLENBQUMsSUFBSTtRQUNmLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN2QixDQUFDO0lBRU0sS0FBSyxDQUFDLG1CQUFtQixDQUFDLFVBQWtCO1FBQ2pELE1BQU0sSUFBSSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEQsT0FBTyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksYUFBYSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDL0MsQ0FBQztJQUVNLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFtQjtRQUMvQyxxQ0FBcUM7UUFDckMsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzFELENBQUM7SUFFTSxLQUFLLENBQUMsaUJBQWlCLENBQUMsVUFBa0I7UUFDL0MsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUs7UUFDaEIsTUFBTSxJQUFJLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFDRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsbUNBQW1DO1FBQ25DLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUMxQixDQUFDO0NBQ0YifQ==
@@ -3,24 +3,19 @@ import * as path from 'path';
3
3
  export { fs, path };
4
4
  import * as cloudflare from '@apiclient.xyz/cloudflare';
5
5
  export { cloudflare };
6
- import * as typedserver from '@api.global/typedserver';
7
- export { typedserver };
8
6
  import * as lik from '@push.rocks/lik';
9
7
  import * as smartdata from '@push.rocks/smartdata';
10
8
  import * as smartdelay from '@push.rocks/smartdelay';
11
9
  import * as smartdnsClient from '@push.rocks/smartdns/client';
12
- import * as smartfile from '@push.rocks/smartfile';
13
10
  import * as smartlog from '@push.rocks/smartlog';
14
11
  import * as smartnetwork from '@push.rocks/smartnetwork';
15
- import * as smartpromise from '@push.rocks/smartpromise';
16
- import * as smartrequest from '@push.rocks/smartrequest';
17
12
  import * as smartunique from '@push.rocks/smartunique';
18
13
  import * as smartstring from '@push.rocks/smartstring';
19
14
  import * as smarttime from '@push.rocks/smarttime';
20
- export { lik, smartdata, smartdelay, smartdnsClient, smartfile, smartlog, smartnetwork, smartpromise, smartrequest, smartunique, smartstring, smarttime, };
15
+ export { lik, smartdata, smartdelay, smartdnsClient, smartlog, smartnetwork, smartunique, smartstring, smarttime, };
21
16
  import * as tsclass from '@tsclass/tsclass';
22
17
  export { tsclass };
23
- import * as acme from 'acme-client';
18
+ import * as acme from './acme/index.js';
24
19
  export { acme };
25
20
  import * as handlers from './handlers/index.js';
26
21
  export { handlers };
@@ -5,30 +5,24 @@ export { fs, path };
5
5
  // @apiclient.xyz scope
6
6
  import * as cloudflare from '@apiclient.xyz/cloudflare';
7
7
  export { cloudflare };
8
- // @apiglobal scope
9
- import * as typedserver from '@api.global/typedserver';
10
- export { typedserver };
11
- // @pushrocks scope
8
+ // @push.rocks scope
12
9
  import * as lik from '@push.rocks/lik';
13
10
  import * as smartdata from '@push.rocks/smartdata';
14
11
  import * as smartdelay from '@push.rocks/smartdelay';
15
12
  import * as smartdnsClient from '@push.rocks/smartdns/client';
16
- import * as smartfile from '@push.rocks/smartfile';
17
13
  import * as smartlog from '@push.rocks/smartlog';
18
14
  import * as smartnetwork from '@push.rocks/smartnetwork';
19
- import * as smartpromise from '@push.rocks/smartpromise';
20
- import * as smartrequest from '@push.rocks/smartrequest';
21
15
  import * as smartunique from '@push.rocks/smartunique';
22
16
  import * as smartstring from '@push.rocks/smartstring';
23
17
  import * as smarttime from '@push.rocks/smarttime';
24
- export { lik, smartdata, smartdelay, smartdnsClient, smartfile, smartlog, smartnetwork, smartpromise, smartrequest, smartunique, smartstring, smarttime, };
18
+ export { lik, smartdata, smartdelay, smartdnsClient, smartlog, smartnetwork, smartunique, smartstring, smarttime, };
25
19
  // @tsclass scope
26
20
  import * as tsclass from '@tsclass/tsclass';
27
21
  export { tsclass };
28
- // third party scope
29
- import * as acme from 'acme-client';
22
+ // acme protocol (custom implementation)
23
+ import * as acme from './acme/index.js';
30
24
  export { acme };
31
25
  // local handlers for challenge types
32
26
  import * as handlers from './handlers/index.js';
33
27
  export { handlers };
34
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYztBQUNkLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFFcEIsdUJBQXVCO0FBQ3ZCLE9BQU8sS0FBSyxVQUFVLE1BQU0sMkJBQTJCLENBQUM7QUFFeEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDO0FBRXRCLG1CQUFtQjtBQUNuQixPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBRXZELE9BQU8sRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUV2QixtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQztBQUN2QyxPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxLQUFLLGNBQWMsTUFBTSw2QkFBNkIsQ0FBQztBQUM5RCxPQUFPLEtBQUssU0FBUyxNQUFNLHVCQUF1QixDQUFDO0FBQ25ELE9BQU8sS0FBSyxRQUFRLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLFdBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFFbkQsT0FBTyxFQUNMLEdBQUcsRUFDSCxTQUFTLEVBQ1QsVUFBVSxFQUNWLGNBQWMsRUFDZCxTQUFTLEVBQ1QsUUFBUSxFQUNSLFlBQVksRUFDWixZQUFZLEVBQ1osWUFBWSxFQUNaLFdBQVcsRUFDWCxXQUFXLEVBQ1gsU0FBUyxHQUNWLENBQUM7QUFFRixpQkFBaUI7QUFDakIsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQUUsT0FBTyxFQUFFLENBQUM7QUFFbkIsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sYUFBYSxDQUFDO0FBRXBDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQztBQUNoQixxQ0FBcUM7QUFDckMsT0FBTyxLQUFLLFFBQVEsTUFBTSxxQkFBcUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsUUFBUSxFQUFFLENBQUMifQ==
28
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYztBQUNkLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFFcEIsdUJBQXVCO0FBQ3ZCLE9BQU8sS0FBSyxVQUFVLE1BQU0sMkJBQTJCLENBQUM7QUFFeEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDO0FBRXRCLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssR0FBRyxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFDbkQsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEtBQUssY0FBYyxNQUFNLDZCQUE2QixDQUFDO0FBQzlELE9BQU8sS0FBSyxRQUFRLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFDdkQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUVuRCxPQUFPLEVBQ0wsR0FBRyxFQUNILFNBQVMsRUFDVCxVQUFVLEVBQ1YsY0FBYyxFQUNkLFFBQVEsRUFDUixZQUFZLEVBQ1osV0FBVyxFQUNYLFdBQVcsRUFDWCxTQUFTLEdBQ1YsQ0FBQztBQUVGLGlCQUFpQjtBQUNqQixPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQztBQUVuQix3Q0FBd0M7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxpQkFBaUIsQ0FBQztBQUV4QyxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDaEIscUNBQXFDO0FBQ3JDLE9BQU8sS0FBSyxRQUFRLE1BQU0scUJBQXFCLENBQUM7QUFDaEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxDQUFDIn0=
@@ -58,6 +58,8 @@ export declare class SmartAcme {
58
58
  private pendingChallenges;
59
59
  private challengePriority;
60
60
  private interestMap;
61
+ private boundSigintHandler;
62
+ private boundSigtermHandler;
61
63
  constructor(optionsArg: ISmartAcmeOptions);
62
64
  /**
63
65
  * starts the instance
@@ -70,7 +72,7 @@ export declare class SmartAcme {
70
72
  * Stops the SmartAcme instance and closes certificate store connections.
71
73
  */
72
74
  stop(): Promise<void>;
73
- /** Retry helper with exponential backoff */
75
+ /** Retry helper with exponential backoff and AcmeError awareness */
74
76
  private retry;
75
77
  /** Clean up pending challenges and shut down */
76
78
  private handleShutdown;