Skip to content

Commit 01fa6f5

Browse files
Merge branch 'feat/matrix-explicit-proxy'
2 parents 35d2fa9 + 86e34d5 commit 01fa6f5

File tree

9 files changed

+69
-13
lines changed

9 files changed

+69
-13
lines changed

extensions/matrix/src/config-schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const MatrixConfigSchema = z.object({
5151
markdown: MarkdownConfigSchema,
5252
homeserver: z.string().optional(),
5353
allowPrivateNetwork: z.boolean().optional(),
54+
proxy: z.string().optional(),
5455
userId: z.string().optional(),
5556
accessToken: z.string().optional(),
5657
password: buildSecretInputSchema().optional(),

extensions/matrix/src/matrix/client/config.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
12
import {
23
requiresExplicitMatrixDefaultAccount,
34
resolveMatrixDefaultOrOnlyAccountId,
@@ -77,13 +78,19 @@ const MATRIX_HTTP_HOMESERVER_ERROR =
7778

7879
function buildMatrixNetworkFields(
7980
allowPrivateNetwork: boolean | undefined,
80-
): Pick<MatrixResolvedConfig, "allowPrivateNetwork" | "ssrfPolicy"> {
81-
if (!allowPrivateNetwork) {
81+
proxy?: string,
82+
): Pick<MatrixResolvedConfig, "allowPrivateNetwork" | "ssrfPolicy" | "dispatcherPolicy"> {
83+
const dispatcherPolicy: PinnedDispatcherPolicy | undefined = proxy
84+
? { mode: "explicit-proxy", proxyUrl: proxy }
85+
: undefined;
86+
if (!allowPrivateNetwork && !dispatcherPolicy) {
8287
return {};
8388
}
8489
return {
85-
allowPrivateNetwork: true,
86-
ssrfPolicy: ssrfPolicyFromAllowPrivateNetwork(true),
90+
...(allowPrivateNetwork
91+
? { allowPrivateNetwork: true, ssrfPolicy: ssrfPolicyFromAllowPrivateNetwork(true) }
92+
: {}),
93+
...(dispatcherPolicy ? { dispatcherPolicy } : {}),
8794
};
8895
}
8996

@@ -265,7 +272,7 @@ export function resolveMatrixConfig(
265272
deviceName: resolvedStrings.deviceName || undefined,
266273
initialSyncLimit,
267274
encryption,
268-
...buildMatrixNetworkFields(allowPrivateNetwork),
275+
...buildMatrixNetworkFields(allowPrivateNetwork, matrix.proxy),
269276
};
270277
}
271278

@@ -320,7 +327,7 @@ export function resolveMatrixConfigForAccount(
320327
deviceName: resolvedStrings.deviceName || undefined,
321328
initialSyncLimit,
322329
encryption,
323-
...buildMatrixNetworkFields(allowPrivateNetwork),
330+
...buildMatrixNetworkFields(allowPrivateNetwork, account.proxy ?? matrix.proxy),
324331
};
325332
}
326333

@@ -412,6 +419,7 @@ export async function resolveMatrixAuth(params?: {
412419
ensureMatrixSdkLoggingConfigured();
413420
const tempClient = new MatrixClient(homeserver, resolved.accessToken, undefined, undefined, {
414421
ssrfPolicy: resolved.ssrfPolicy,
422+
dispatcherPolicy: resolved.dispatcherPolicy,
415423
});
416424
const whoami = (await tempClient.doRequest("GET", "/_matrix/client/v3/account/whoami")) as {
417425
user_id?: string;
@@ -460,7 +468,12 @@ export async function resolveMatrixAuth(params?: {
460468
deviceName: resolved.deviceName,
461469
initialSyncLimit: resolved.initialSyncLimit,
462470
encryption: resolved.encryption,
463-
...buildMatrixNetworkFields(resolved.allowPrivateNetwork),
471+
...buildMatrixNetworkFields(
472+
resolved.allowPrivateNetwork,
473+
resolved.dispatcherPolicy?.mode === "explicit-proxy"
474+
? resolved.dispatcherPolicy.proxyUrl
475+
: undefined,
476+
),
464477
};
465478
}
466479

@@ -477,7 +490,12 @@ export async function resolveMatrixAuth(params?: {
477490
deviceName: resolved.deviceName,
478491
initialSyncLimit: resolved.initialSyncLimit,
479492
encryption: resolved.encryption,
480-
...buildMatrixNetworkFields(resolved.allowPrivateNetwork),
493+
...buildMatrixNetworkFields(
494+
resolved.allowPrivateNetwork,
495+
resolved.dispatcherPolicy?.mode === "explicit-proxy"
496+
? resolved.dispatcherPolicy.proxyUrl
497+
: undefined,
498+
),
481499
};
482500
}
483501

@@ -495,6 +513,7 @@ export async function resolveMatrixAuth(params?: {
495513
ensureMatrixSdkLoggingConfigured();
496514
const loginClient = new MatrixClient(homeserver, "", undefined, undefined, {
497515
ssrfPolicy: resolved.ssrfPolicy,
516+
dispatcherPolicy: resolved.dispatcherPolicy,
498517
});
499518
const login = (await loginClient.doRequest("POST", "/_matrix/client/v3/login", undefined, {
500519
type: "m.login.password",
@@ -523,7 +542,12 @@ export async function resolveMatrixAuth(params?: {
523542
deviceName: resolved.deviceName,
524543
initialSyncLimit: resolved.initialSyncLimit,
525544
encryption: resolved.encryption,
526-
...buildMatrixNetworkFields(resolved.allowPrivateNetwork),
545+
...buildMatrixNetworkFields(
546+
resolved.allowPrivateNetwork,
547+
resolved.dispatcherPolicy?.mode === "explicit-proxy"
548+
? resolved.dispatcherPolicy.proxyUrl
549+
: undefined,
550+
),
527551
};
528552

529553
const { saveMatrixCredentials } = await loadCredentialsWriter();

extensions/matrix/src/matrix/client/create-client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import fs from "node:fs";
2+
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
23
import type { SsrFPolicy } from "../../runtime-api.js";
34
import { MatrixClient } from "../sdk.js";
45
import { resolveValidatedMatrixHomeserverUrl } from "./config.js";
@@ -22,6 +23,7 @@ export async function createMatrixClient(params: {
2223
autoBootstrapCrypto?: boolean;
2324
allowPrivateNetwork?: boolean;
2425
ssrfPolicy?: SsrFPolicy;
26+
dispatcherPolicy?: PinnedDispatcherPolicy;
2527
}): Promise<MatrixClient> {
2628
ensureMatrixSdkLoggingConfigured();
2729
const env = process.env;
@@ -68,5 +70,6 @@ export async function createMatrixClient(params: {
6870
cryptoDatabasePrefix,
6971
autoBootstrapCrypto: params.autoBootstrapCrypto,
7072
ssrfPolicy: params.ssrfPolicy,
73+
dispatcherPolicy: params.dispatcherPolicy,
7174
});
7275
}

extensions/matrix/src/matrix/client/shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ async function createSharedMatrixClient(params: {
4545
accountId: params.auth.accountId,
4646
allowPrivateNetwork: params.auth.allowPrivateNetwork,
4747
ssrfPolicy: params.auth.ssrfPolicy,
48+
dispatcherPolicy: params.auth.dispatcherPolicy,
4849
});
4950
return {
5051
client,

extensions/matrix/src/matrix/client/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
12
import type { SsrFPolicy } from "../../runtime-api.js";
23

34
export type MatrixResolvedConfig = {
@@ -11,6 +12,7 @@ export type MatrixResolvedConfig = {
1112
encryption?: boolean;
1213
allowPrivateNetwork?: boolean;
1314
ssrfPolicy?: SsrFPolicy;
15+
dispatcherPolicy?: PinnedDispatcherPolicy;
1416
};
1517

1618
/**
@@ -33,6 +35,7 @@ export type MatrixAuth = {
3335
encryption?: boolean;
3436
allowPrivateNetwork?: boolean;
3537
ssrfPolicy?: SsrFPolicy;
38+
dispatcherPolicy?: PinnedDispatcherPolicy;
3639
};
3740

3841
export type MatrixStoragePaths = {

extensions/matrix/src/matrix/sdk.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from "matrix-js-sdk";
1212
import { VerificationMethod } from "matrix-js-sdk/lib/types.js";
1313
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/core";
14+
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
1415
import type { SsrFPolicy } from "../runtime-api.js";
1516
import { resolveMatrixRoomKeyBackupReadinessError } from "./backup-health.js";
1617
import { FileBackedMatrixSyncStore } from "./client/file-sync-store.js";
@@ -221,9 +222,15 @@ export class MatrixClient {
221222
cryptoDatabasePrefix?: string;
222223
autoBootstrapCrypto?: boolean;
223224
ssrfPolicy?: SsrFPolicy;
225+
dispatcherPolicy?: PinnedDispatcherPolicy;
224226
} = {},
225227
) {
226-
this.httpClient = new MatrixAuthedHttpClient(homeserver, accessToken, opts.ssrfPolicy);
228+
this.httpClient = new MatrixAuthedHttpClient(
229+
homeserver,
230+
accessToken,
231+
opts.ssrfPolicy,
232+
opts.dispatcherPolicy,
233+
);
227234
this.localTimeoutMs = Math.max(1, opts.localTimeoutMs ?? 60_000);
228235
this.initialSyncLimit = opts.initialSyncLimit;
229236
this.encryptionEnabled = opts.encryption === true;
@@ -244,7 +251,10 @@ export class MatrixClient {
244251
deviceId: opts.deviceId,
245252
logger: createMatrixJsSdkClientLogger("MatrixClient"),
246253
localTimeoutMs: this.localTimeoutMs,
247-
fetchFn: createMatrixGuardedFetch({ ssrfPolicy: opts.ssrfPolicy }),
254+
fetchFn: createMatrixGuardedFetch({
255+
ssrfPolicy: opts.ssrfPolicy,
256+
dispatcherPolicy: opts.dispatcherPolicy,
257+
}),
248258
store: this.syncStore,
249259
cryptoCallbacks: cryptoCallbacks as never,
250260
verificationMethods: [

extensions/matrix/src/matrix/sdk/http-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
12
import type { SsrFPolicy } from "../../runtime-api.js";
23
import { buildHttpError } from "./event-helpers.js";
34
import { type HttpMethod, type QueryParams, performMatrixRequest } from "./transport.js";
@@ -7,6 +8,7 @@ export class MatrixAuthedHttpClient {
78
private readonly homeserver: string,
89
private readonly accessToken: string,
910
private readonly ssrfPolicy?: SsrFPolicy,
11+
private readonly dispatcherPolicy?: PinnedDispatcherPolicy,
1012
) {}
1113

1214
async requestJson(params: {
@@ -26,6 +28,7 @@ export class MatrixAuthedHttpClient {
2628
body: params.body,
2729
timeoutMs: params.timeoutMs,
2830
ssrfPolicy: this.ssrfPolicy,
31+
dispatcherPolicy: this.dispatcherPolicy,
2932
allowAbsoluteEndpoint: params.allowAbsoluteEndpoint,
3033
});
3134
if (!response.ok) {
@@ -61,6 +64,7 @@ export class MatrixAuthedHttpClient {
6164
maxBytes: params.maxBytes,
6265
readIdleTimeoutMs: params.readIdleTimeoutMs,
6366
ssrfPolicy: this.ssrfPolicy,
67+
dispatcherPolicy: this.dispatcherPolicy,
6468
allowAbsoluteEndpoint: params.allowAbsoluteEndpoint,
6569
});
6670
if (!response.ok) {

extensions/matrix/src/matrix/sdk/transport.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
12
import {
23
buildTimeoutAbortSignal,
34
closeDispatcher,
@@ -88,6 +89,7 @@ async function fetchWithMatrixGuardedRedirects(params: {
8889
signal?: AbortSignal;
8990
timeoutMs?: number;
9091
ssrfPolicy?: SsrFPolicy;
92+
dispatcherPolicy?: PinnedDispatcherPolicy;
9193
}): Promise<{ response: Response; release: () => Promise<void>; finalUrl: string }> {
9294
let currentUrl = new URL(params.url);
9395
let method = (params.init?.method ?? "GET").toUpperCase();
@@ -106,7 +108,7 @@ async function fetchWithMatrixGuardedRedirects(params: {
106108
const pinned = await resolvePinnedHostnameWithPolicy(currentUrl.hostname, {
107109
policy: params.ssrfPolicy,
108110
});
109-
dispatcher = createPinnedDispatcher(pinned, undefined, params.ssrfPolicy);
111+
dispatcher = createPinnedDispatcher(pinned, params.dispatcherPolicy, params.ssrfPolicy);
110112
const response = await fetch(currentUrl.toString(), {
111113
...params.init,
112114
method,
@@ -184,7 +186,10 @@ async function fetchWithMatrixGuardedRedirects(params: {
184186
throw new Error(`Too many redirects while requesting ${params.url}`);
185187
}
186188

187-
export function createMatrixGuardedFetch(params: { ssrfPolicy?: SsrFPolicy }): typeof fetch {
189+
export function createMatrixGuardedFetch(params: {
190+
ssrfPolicy?: SsrFPolicy;
191+
dispatcherPolicy?: PinnedDispatcherPolicy;
192+
}): typeof fetch {
188193
return (async (resource: RequestInfo | URL, init?: RequestInit) => {
189194
const url = toFetchUrl(resource);
190195
const { signal, ...requestInit } = init ?? {};
@@ -193,6 +198,7 @@ export function createMatrixGuardedFetch(params: { ssrfPolicy?: SsrFPolicy }): t
193198
init: requestInit,
194199
signal: signal ?? undefined,
195200
ssrfPolicy: params.ssrfPolicy,
201+
dispatcherPolicy: params.dispatcherPolicy,
196202
});
197203

198204
try {
@@ -220,6 +226,7 @@ export async function performMatrixRequest(params: {
220226
maxBytes?: number;
221227
readIdleTimeoutMs?: number;
222228
ssrfPolicy?: SsrFPolicy;
229+
dispatcherPolicy?: PinnedDispatcherPolicy;
223230
allowAbsoluteEndpoint?: boolean;
224231
}): Promise<{ response: Response; text: string; buffer: Buffer }> {
225232
const isAbsoluteEndpoint =
@@ -264,6 +271,7 @@ export async function performMatrixRequest(params: {
264271
},
265272
timeoutMs: params.timeoutMs,
266273
ssrfPolicy: params.ssrfPolicy,
274+
dispatcherPolicy: params.dispatcherPolicy,
267275
});
268276

269277
try {

extensions/matrix/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ export type MatrixConfig = {
7070
homeserver?: string;
7171
/** Allow Matrix homeserver traffic to private/internal hosts. */
7272
allowPrivateNetwork?: boolean;
73+
/** Optional HTTP(S) proxy URL for Matrix connections (e.g. http://127.0.0.1:7890). */
74+
proxy?: string;
7375
/** Matrix user id (@user:server). */
7476
userId?: string;
7577
/** Matrix access token. */

0 commit comments

Comments
 (0)