@push.rocks/smartproxy 27.12.7 → 27.13.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 (37) hide show
  1. package/changelog.md +12 -7
  2. package/dist_rust/rustproxy_linux_amd64 +0 -0
  3. package/dist_rust/rustproxy_linux_arm64 +0 -0
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/index.d.ts +1 -1
  6. package/dist_ts/plugins.d.ts +4 -2
  7. package/dist_ts/plugins.js +5 -3
  8. package/dist_ts/proxies/smart-proxy/challenge-provider-relay-server.d.ts +31 -0
  9. package/dist_ts/proxies/smart-proxy/challenge-provider-relay-server.js +297 -0
  10. package/dist_ts/proxies/smart-proxy/models/index.d.ts +1 -1
  11. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +14 -2
  12. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +11 -0
  13. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  14. package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +5 -3
  15. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +2 -1
  16. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +4 -1
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -0
  18. package/dist_ts/proxies/smart-proxy/smart-proxy.js +339 -89
  19. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +3 -0
  20. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +81 -1
  21. package/dist_ts/proxies/smart-proxy/utils/rust-config.d.ts +3 -2
  22. package/dist_ts/proxies/smart-proxy/utils/rust-config.js +20 -4
  23. package/package.json +2 -1
  24. package/readme.hints.md +2 -2
  25. package/readme.md +87 -7
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/index.ts +1 -1
  28. package/ts/plugins.ts +4 -1
  29. package/ts/proxies/smart-proxy/challenge-provider-relay-server.ts +338 -0
  30. package/ts/proxies/smart-proxy/models/index.ts +1 -1
  31. package/ts/proxies/smart-proxy/models/interfaces.ts +17 -2
  32. package/ts/proxies/smart-proxy/models/route-types.ts +17 -0
  33. package/ts/proxies/smart-proxy/models/rust-types.ts +8 -3
  34. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +6 -0
  35. package/ts/proxies/smart-proxy/smart-proxy.ts +400 -94
  36. package/ts/proxies/smart-proxy/utils/route-validator.ts +84 -1
  37. package/ts/proxies/smart-proxy/utils/rust-config.ts +22 -2
@@ -0,0 +1,338 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { logger } from '../../core/utils/logger.js';
3
+
4
+ type TChallengeRelayOperation = 'assess' | 'render' | 'verify';
5
+
6
+ interface IChallengeRelayRequest {
7
+ id?: string;
8
+ operation: TChallengeRelayOperation;
9
+ providerId: string;
10
+ request: unknown;
11
+ }
12
+
13
+ interface IChallengeRelayResponse {
14
+ id?: string;
15
+ success: boolean;
16
+ result?: unknown;
17
+ error?: string;
18
+ }
19
+
20
+ const maxChallengeRelayMessageBytes = 1024 * 1024;
21
+ const maxChallengeRelayIdBytes = 256;
22
+
23
+ export interface IChallengeProviderRelayServerOptions {
24
+ providerTimeoutMs?: number;
25
+ maxActiveProviderOperations?: number;
26
+ maxUnsettledProviderOperations?: number;
27
+ }
28
+
29
+ export class ChallengeProviderRelayServer {
30
+ private providerTimeoutMs: number;
31
+ private maxActiveProviderOperations: number;
32
+ private maxUnsettledProviderOperations: number;
33
+ private activeProviderOperations = 0;
34
+ private unsettledProviderOperations = 0;
35
+ private server: plugins.net.Server | null = null;
36
+ private socketPath: string;
37
+ private activeSockets = new Set<plugins.net.Socket>();
38
+ private startPromise?: Promise<void>;
39
+
40
+ constructor(
41
+ private providers: Map<string, plugins.smartchallenge.IChallengeProvider>,
42
+ optionsArg: IChallengeProviderRelayServerOptions = {},
43
+ ) {
44
+ this.socketPath = `/tmp/smartproxy-challenge-relay-${process.pid}-${plugins.crypto.randomBytes(8).toString('hex')}.sock`;
45
+ this.providerTimeoutMs = Math.max(1, optionsArg.providerTimeoutMs ?? 5_000);
46
+ this.maxActiveProviderOperations = Math.max(1, optionsArg.maxActiveProviderOperations ?? 1024);
47
+ this.maxUnsettledProviderOperations = Math.max(1, optionsArg.maxUnsettledProviderOperations ?? this.maxActiveProviderOperations);
48
+ }
49
+
50
+ public getSocketPath(): string {
51
+ return this.socketPath;
52
+ }
53
+
54
+ public async start(): Promise<void> {
55
+ if (this.server) return;
56
+ if (this.startPromise) return this.startPromise;
57
+ this.startPromise = this.startInternal();
58
+ try {
59
+ await this.startPromise;
60
+ } finally {
61
+ this.startPromise = undefined;
62
+ }
63
+ }
64
+
65
+ private async startInternal(): Promise<void> {
66
+ try {
67
+ await plugins.fs.promises.unlink(this.socketPath);
68
+ } catch {
69
+ // Ignore stale socket cleanup failures for missing files.
70
+ }
71
+
72
+ const server = plugins.net.createServer((socket) => {
73
+ this.activeSockets.add(socket);
74
+ socket.once('close', () => this.activeSockets.delete(socket));
75
+ this.handleConnection(socket);
76
+ });
77
+
78
+ server.on('error', (err) => {
79
+ logger.log('error', `ChallengeProviderRelayServer error: ${err.message}`, { component: 'challenge-provider-relay-server' });
80
+ });
81
+
82
+ await new Promise<void>((resolve, reject) => {
83
+ const handleError = (err: Error) => reject(err);
84
+ server.once('error', handleError);
85
+ server.listen(this.socketPath, () => {
86
+ server.off('error', handleError);
87
+ this.server = server;
88
+ logger.log('info', `ChallengeProviderRelayServer listening on ${this.socketPath}`, { component: 'challenge-provider-relay-server' });
89
+ resolve();
90
+ });
91
+ }).catch((err) => {
92
+ server.close();
93
+ throw err;
94
+ });
95
+ }
96
+
97
+ public async stop(): Promise<void> {
98
+ if (this.startPromise && !this.server) {
99
+ await this.startPromise.catch(() => undefined);
100
+ }
101
+
102
+ for (const socket of this.activeSockets) {
103
+ socket.destroy();
104
+ }
105
+ this.activeSockets.clear();
106
+
107
+ const server = this.server;
108
+ if (!server) return;
109
+ this.server = null;
110
+ await new Promise<void>((resolve) => {
111
+ server.close(() => {
112
+ plugins.fs.unlink(this.socketPath, () => resolve());
113
+ });
114
+ });
115
+ }
116
+
117
+ private handleConnection(socket: plugins.net.Socket): void {
118
+ let buffer: Buffer<ArrayBufferLike> = Buffer.alloc(0);
119
+ socket.setTimeout(Math.max(10_000, this.providerTimeoutMs + 1_000));
120
+ socket.on('timeout', () => socket.destroy());
121
+ socket.on('error', (err) => {
122
+ logger.log('warn', `Challenge relay socket error: ${err.message}`, { component: 'challenge-provider-relay-server' });
123
+ });
124
+
125
+ socket.on('data', (chunk: Buffer) => {
126
+ buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk], buffer.length + chunk.length);
127
+ if (buffer.length > maxChallengeRelayMessageBytes) {
128
+ socket.removeAllListeners('data');
129
+ this.writeResponse(socket, { success: false, error: 'Challenge relay request exceeds maximum size' });
130
+ buffer = Buffer.alloc(0);
131
+ socket.end();
132
+ return;
133
+ }
134
+
135
+ const newlineIndex = buffer.indexOf(0x0a);
136
+ if (newlineIndex === -1) return;
137
+
138
+ const line = buffer.subarray(0, newlineIndex).toString('utf8');
139
+ socket.removeAllListeners('data');
140
+ this.dispatchLine(line)
141
+ .then((response) => this.writeResponse(socket, response))
142
+ .catch((err) => this.writeResponse(socket, { success: false, error: (err as Error).message }))
143
+ .finally(() => socket.end());
144
+ });
145
+ }
146
+
147
+ private async dispatchLine(lineArg: string): Promise<IChallengeRelayResponse> {
148
+ let request: IChallengeRelayRequest;
149
+ try {
150
+ request = JSON.parse(lineArg) as IChallengeRelayRequest;
151
+ } catch {
152
+ return { success: false, error: 'Invalid challenge relay JSON' };
153
+ }
154
+
155
+ const provider = this.providers.get(request.providerId);
156
+ if (!provider) {
157
+ return {
158
+ id: request.id,
159
+ success: false,
160
+ error: `Challenge provider '${request.providerId}' is not registered`,
161
+ };
162
+ }
163
+
164
+ switch (request.operation) {
165
+ case 'assess':
166
+ return { id: request.id, success: true, result: await this.withProviderTimeout(() => provider.assess(request.request as plugins.smartchallenge.IChallengeAssessRequest), 'assess') };
167
+ case 'render':
168
+ return { id: request.id, success: true, result: await this.withProviderTimeout(() => provider.render(request.request as plugins.smartchallenge.IChallengeRenderRequest), 'render') };
169
+ case 'verify':
170
+ return { id: request.id, success: true, result: await this.withProviderTimeout(() => provider.verify(request.request as plugins.smartchallenge.IChallengeVerifyRequest), 'verify') };
171
+ default:
172
+ const operationValue = (request as { operation?: unknown }).operation;
173
+ return { id: request.id, success: false, error: `Unsupported challenge relay operation '${String(operationValue)}'` };
174
+ }
175
+ }
176
+
177
+ private async withProviderTimeout<T>(operationArg: () => Promise<T>, operationNameArg: TChallengeRelayOperation): Promise<T> {
178
+ if (this.activeProviderOperations >= this.maxActiveProviderOperations) {
179
+ throw new Error(`Challenge provider ${operationNameArg} rejected because too many operations are active`);
180
+ }
181
+ if (this.unsettledProviderOperations >= this.maxUnsettledProviderOperations) {
182
+ throw new Error(`Challenge provider ${operationNameArg} rejected because too many timed-out operations are still settling`);
183
+ }
184
+
185
+ let timer: ReturnType<typeof setTimeout> | undefined;
186
+ let activeReleased = false;
187
+ let unsettledReleased = false;
188
+ const releaseActive = () => {
189
+ if (!activeReleased) {
190
+ activeReleased = true;
191
+ this.activeProviderOperations -= 1;
192
+ }
193
+ };
194
+ const releaseUnsettled = () => {
195
+ if (!unsettledReleased) {
196
+ unsettledReleased = true;
197
+ this.unsettledProviderOperations -= 1;
198
+ }
199
+ };
200
+ this.activeProviderOperations += 1;
201
+ this.unsettledProviderOperations += 1;
202
+ const providerPromise = Promise.resolve().then(operationArg);
203
+ const trackedProviderPromise = providerPromise.then(
204
+ (result) => {
205
+ releaseActive();
206
+ releaseUnsettled();
207
+ return result;
208
+ },
209
+ (err) => {
210
+ releaseActive();
211
+ releaseUnsettled();
212
+ throw err;
213
+ },
214
+ );
215
+ trackedProviderPromise.catch(() => undefined);
216
+ try {
217
+ return await Promise.race([
218
+ trackedProviderPromise,
219
+ new Promise<T>((_resolve, reject) => {
220
+ timer = setTimeout(
221
+ () => {
222
+ releaseActive();
223
+ reject(new Error(`Challenge provider ${operationNameArg} timed out after ${this.providerTimeoutMs}ms`));
224
+ },
225
+ this.providerTimeoutMs,
226
+ );
227
+ }),
228
+ ]);
229
+ } finally {
230
+ if (timer) clearTimeout(timer);
231
+ }
232
+ }
233
+
234
+ private writeResponse(socket: plugins.net.Socket, responseArg: IChallengeRelayResponse): void {
235
+ let response = responseArg;
236
+ if (this.estimateJsonByteLength(response, maxChallengeRelayMessageBytes) === undefined) {
237
+ response = this.createErrorResponse(responseArg.id, 'Challenge relay response exceeds maximum size');
238
+ }
239
+
240
+ let serializedResponse: string;
241
+ try {
242
+ serializedResponse = JSON.stringify(response);
243
+ } catch {
244
+ serializedResponse = JSON.stringify(this.createErrorResponse(responseArg.id, 'Challenge relay response is not serializable'));
245
+ }
246
+
247
+ if (Buffer.byteLength(serializedResponse, 'utf8') > maxChallengeRelayMessageBytes) {
248
+ serializedResponse = JSON.stringify({
249
+ success: false,
250
+ error: 'Challenge relay response exceeds maximum size',
251
+ });
252
+ }
253
+
254
+ socket.write(`${serializedResponse}\n`);
255
+ }
256
+
257
+ private createErrorResponse(idArg: string | undefined, errorArg: string): IChallengeRelayResponse {
258
+ const response: IChallengeRelayResponse = {
259
+ success: false,
260
+ error: errorArg,
261
+ };
262
+ if (idArg && Buffer.byteLength(idArg, 'utf8') <= maxChallengeRelayIdBytes) {
263
+ response.id = idArg;
264
+ }
265
+ return response;
266
+ }
267
+
268
+ private estimateJsonByteLength(
269
+ valueArg: unknown,
270
+ maxBytesArg: number,
271
+ seenArg = new Set<object>(),
272
+ ): number | undefined {
273
+ const bytes = this.estimateJsonByteLengthInternal(valueArg, maxBytesArg, seenArg);
274
+ return bytes !== undefined && bytes <= maxBytesArg ? bytes : undefined;
275
+ }
276
+
277
+ private estimateJsonByteLengthInternal(
278
+ valueArg: unknown,
279
+ maxBytesArg: number,
280
+ seenArg: Set<object>,
281
+ ): number | undefined {
282
+ if (valueArg === null) return 4;
283
+ if (typeof valueArg === 'string') return this.estimateJsonStringByteLength(valueArg, maxBytesArg);
284
+ if (typeof valueArg === 'number') return Number.isFinite(valueArg) ? String(valueArg).length : 4;
285
+ if (typeof valueArg === 'boolean') return valueArg ? 4 : 5;
286
+ if (typeof valueArg === 'undefined') return 4;
287
+ if (typeof valueArg === 'bigint' || typeof valueArg === 'symbol' || typeof valueArg === 'function') return undefined;
288
+ if (typeof valueArg !== 'object') return undefined;
289
+ if (seenArg.has(valueArg)) return undefined;
290
+
291
+ seenArg.add(valueArg);
292
+ try {
293
+ let totalBytes = 2;
294
+ if (Array.isArray(valueArg)) {
295
+ for (let i = 0; i < valueArg.length; i++) {
296
+ const childBytes = this.estimateJsonByteLengthInternal(valueArg[i], maxBytesArg, seenArg);
297
+ if (childBytes === undefined) return undefined;
298
+ totalBytes += childBytes + (i > 0 ? 1 : 0);
299
+ if (totalBytes > maxBytesArg) return undefined;
300
+ }
301
+ return totalBytes;
302
+ }
303
+
304
+ let propertyCount = 0;
305
+ for (const [key, value] of Object.entries(valueArg as Record<string, unknown>)) {
306
+ if (typeof value === 'undefined' || typeof value === 'function' || typeof value === 'symbol') continue;
307
+ const keyBytes = this.estimateJsonStringByteLength(key, maxBytesArg);
308
+ const valueBytes = this.estimateJsonByteLengthInternal(value, maxBytesArg, seenArg);
309
+ if (keyBytes === undefined || valueBytes === undefined) return undefined;
310
+ totalBytes += keyBytes + valueBytes + 1 + (propertyCount > 0 ? 1 : 0);
311
+ propertyCount += 1;
312
+ if (totalBytes > maxBytesArg) return undefined;
313
+ }
314
+ return totalBytes;
315
+ } finally {
316
+ seenArg.delete(valueArg);
317
+ }
318
+ }
319
+
320
+ private estimateJsonStringByteLength(valueArg: string, maxBytesArg: number): number | undefined {
321
+ let bytes = Buffer.byteLength(valueArg, 'utf8') + 2;
322
+ if (bytes > maxBytesArg) return undefined;
323
+
324
+ for (let i = 0; i < valueArg.length; i++) {
325
+ const code = valueArg.charCodeAt(i);
326
+ if (code === 0x22 || code === 0x5c) {
327
+ bytes += 1;
328
+ } else if (code === 0x08 || code === 0x09 || code === 0x0a || code === 0x0c || code === 0x0d) {
329
+ bytes += 1;
330
+ } else if (code < 0x20) {
331
+ bytes += 5;
332
+ }
333
+ if (bytes > maxBytesArg) return undefined;
334
+ }
335
+
336
+ return bytes;
337
+ }
338
+ }
@@ -2,6 +2,6 @@
2
2
  * SmartProxy models
3
3
  */
4
4
  // Export everything except IAcmeOptions from interfaces
5
- export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyCertStore, IConnectionRecord, TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent, IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions } from './interfaces.js';
5
+ export type { ISmartProxyOptions, ISmartProxySecurityPolicy, ISmartProxyChallengeOptions, ISmartProxyCertStore, IConnectionRecord, TChallengeProviderRegistry, TSmartProxyCertProvisionObject, ICertProvisionEventComms, ICertificateIssuedEvent, ICertificateFailedEvent, IActiveConnectionSnapshot, IActiveConnectionSnapshotOptions } from './interfaces.js';
6
6
  export * from './route-types.js';
7
7
  export * from './metrics-types.js';
@@ -34,6 +34,17 @@ export interface ISmartProxySecurityPolicy {
34
34
  blockedCidrs?: string[];
35
35
  }
36
36
 
37
+ export interface ISmartProxyChallengeOptions {
38
+ /** Runtime-only cookie signing key. If omitted, SmartProxy generates an ephemeral key. */
39
+ cookieSigningKey?: string;
40
+ pendingCookieName?: string;
41
+ clearanceCookieName?: string;
42
+ reservedPathPrefix?: string;
43
+ relaySocketPath?: string;
44
+ relayTimeoutMs?: number;
45
+ pendingTtlSeconds?: number;
46
+ }
47
+
37
48
  /**
38
49
  * Provision object for static or HTTP-01 certificate
39
50
  */
@@ -112,8 +123,7 @@ export interface ISmartProxyOptions {
112
123
  preserveSourceIP?: boolean; // Preserve client IP when forwarding
113
124
 
114
125
  // PROXY protocol configuration
115
- proxyIPs?: string[]; // List of trusted proxy IPs that can send PROXY protocol
116
- acceptProxyProtocol?: boolean; // Global option to accept PROXY protocol (defaults based on proxyIPs)
126
+ trustedProxyIPs?: string[]; // Global trusted IP/CIDR list for inbound PROXY protocol
117
127
  sendProxyProtocol?: boolean; // Global option to send PROXY protocol to all targets
118
128
 
119
129
  // Global/default settings
@@ -172,6 +182,9 @@ export interface ISmartProxyOptions {
172
182
  connectionRateLimitPerMinute?: number; // Max new connections per minute from a single IP
173
183
  securityPolicy?: ISmartProxySecurityPolicy; // Global ingress block policy, enforced before routing
174
184
 
185
+ /** Runtime-only challenge enforcement settings. Never store this in route configs. */
186
+ challenge?: ISmartProxyChallengeOptions;
187
+
175
188
  // Enhanced keep-alive settings
176
189
  keepAliveTreatment?: 'standard' | 'extended' | 'immortal'; // How to treat keep-alive connections
177
190
  keepAliveInactivityMultiplier?: number; // Multiplier for inactivity timeout for keep-alive connections
@@ -250,6 +263,8 @@ export interface ISmartProxyOptions {
250
263
  rustBinaryPath?: string;
251
264
  }
252
265
 
266
+ export type TChallengeProviderRegistry = Map<string, plugins.smartchallenge.IChallengeProvider>;
267
+
253
268
  /**
254
269
  * Enhanced connection record
255
270
  */
@@ -24,6 +24,16 @@ export type TTlsMode = 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
24
24
  */
25
25
  export type TTransportProtocol = 'tcp' | 'udp' | 'all';
26
26
 
27
+ /**
28
+ * Inbound PROXY protocol handling for a listener.
29
+ */
30
+ export type TInboundProxyProtocolMode = 'reject' | 'optional' | 'required';
31
+
32
+ export interface IInboundProxyProtocolPolicy {
33
+ mode: TInboundProxyProtocolMode;
34
+ trustedProxyIPs?: string[];
35
+ }
36
+
27
37
  /**
28
38
  * Port range specification format.
29
39
  * Supports: single number, array of numbers, array of ranges, or mixed arrays.
@@ -40,6 +50,10 @@ export interface IRouteMatch {
40
50
  // Transport protocol: 'tcp' (default), 'udp', or 'all' (both TCP and UDP)
41
51
  transport?: TTransportProtocol;
42
52
 
53
+ // Inbound PROXY protocol policy for this listener. Routes sharing the same
54
+ // port/transport must declare the same policy.
55
+ inboundProxyProtocol?: IInboundProxyProtocolPolicy;
56
+
43
57
  // Optional domain patterns to match (default: all domains)
44
58
  domains?: string | string[];
45
59
 
@@ -182,6 +196,9 @@ export interface IRouteSecurity {
182
196
  /** Allowed VPN tunnel IPs, kept for compatibility. Prefer allowedClients. */
183
197
  allowedAssignedIps?: string[];
184
198
  };
199
+
200
+ /** Browser-facing challenge policy. SmartProxy enforces this in the HTTP path. */
201
+ challenge?: plugins.smartchallenge.IRouteChallengeConfig;
185
202
  }
186
203
 
187
204
  /**
@@ -1,5 +1,5 @@
1
1
  import type { IProtocolCacheEntry, IProtocolDistribution } from './metrics-types.js';
2
- import type { IAcmeOptions, ISmartProxyOptions, ISmartProxySecurityPolicy } from './interfaces.js';
2
+ import type { IAcmeOptions, ISmartProxyChallengeOptions, ISmartProxyOptions, ISmartProxySecurityPolicy } from './interfaces.js';
3
3
  import type {
4
4
  IRouteAction,
5
5
  IRouteConfig,
@@ -46,11 +46,15 @@ export interface IRustRouteConfig extends Omit<IRouteConfig, 'match' | 'action'>
46
46
 
47
47
  export interface IRustAcmeOptions extends Omit<IAcmeOptions, 'routeForwards'> {}
48
48
 
49
+ export interface IRustChallengeOptions extends Required<Pick<
50
+ ISmartProxyChallengeOptions,
51
+ 'cookieSigningKey' | 'pendingCookieName' | 'clearanceCookieName' | 'reservedPathPrefix' | 'relaySocketPath' | 'relayTimeoutMs' | 'pendingTtlSeconds'
52
+ >> {}
53
+
49
54
  export interface IRustProxyOptions {
50
55
  routes: IRustRouteConfig[];
51
56
  preserveSourceIp?: boolean;
52
- proxyIps?: string[];
53
- acceptProxyProtocol?: boolean;
57
+ trustedProxyIPs?: string[];
54
58
  sendProxyProtocol?: boolean;
55
59
  defaults?: IRustDefaultConfig;
56
60
  connectionTimeout?: number;
@@ -76,6 +80,7 @@ export interface IRustProxyOptions {
76
80
  extendedKeepAliveLifetime?: number;
77
81
  metrics?: ISmartProxyOptions['metrics'];
78
82
  securityPolicy?: ISmartProxySecurityPolicy;
83
+ challenge?: IRustChallengeOptions;
79
84
  acme?: IRustAcmeOptions;
80
85
  }
81
86
 
@@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js';
2
2
  import { logger } from '../../core/utils/logger.js';
3
3
  import type {
4
4
  IRustCertificateStatus,
5
+ IRustChallengeOptions,
5
6
  IRustMetricsSnapshot,
6
7
  IRustProxyOptions,
7
8
  IRustRouteConfig,
@@ -25,6 +26,7 @@ type TSmartProxyCommands = {
25
26
  getCertificateStatus: { params: { routeName: string }; result: IRustCertificateStatus | null };
26
27
  getListeningPorts: { params: Record<string, never>; result: { ports: number[] } };
27
28
  setSocketHandlerRelay: { params: { socketPath: string }; result: void };
29
+ setChallengeProviderRelay: { params: { socketPath: string; options?: IRustChallengeOptions }; result: void };
28
30
  addListeningPort: { params: { port: number }; result: void };
29
31
  removeListeningPort: { params: { port: number }; result: void };
30
32
  loadCertificate: { params: { domain: string; cert: string; key: string; ca?: string }; result: void };
@@ -181,6 +183,10 @@ export class RustProxyBridge extends plugins.EventEmitter {
181
183
  await this.bridge.sendCommand('setSocketHandlerRelay', { socketPath });
182
184
  }
183
185
 
186
+ public async setChallengeProviderRelay(socketPath: string, options?: IRustChallengeOptions): Promise<void> {
187
+ await this.bridge.sendCommand('setChallengeProviderRelay', { socketPath, options });
188
+ }
189
+
184
190
  public async addListeningPort(port: number): Promise<void> {
185
191
  await this.bridge.sendCommand('addListeningPort', { port });
186
192
  }