@rstreamlabs/tunnels 3.8.1 → 3.8.2

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/dist/index.js CHANGED
@@ -1766,9 +1766,9 @@ var RstreamTunnelsResource = class {
1766
1766
  };
1767
1767
 
1768
1768
  // src/tunnels.ts
1769
- var import_rstream11 = require("@rstreamlabs/rstream");
1770
1769
  var import_rstream12 = require("@rstreamlabs/rstream");
1771
1770
  var import_rstream13 = require("@rstreamlabs/rstream");
1771
+ var import_rstream14 = require("@rstreamlabs/rstream");
1772
1772
 
1773
1773
  // src/turn.ts
1774
1774
  var import_rstream8 = require("@rstreamlabs/rstream");
@@ -1831,14 +1831,14 @@ function normalizeTURNPort(port) {
1831
1831
  function normalizeTURNSPort(port) {
1832
1832
  return normalizePort(port, defaultTURNSPort, "TURNS port");
1833
1833
  }
1834
- function createTURNURLs(clusterDomain, turnPort, turnsPort) {
1834
+ function createTURNURLs(turnDomain, turnPort, turnsPort) {
1835
1835
  const normalizedTURNPort = normalizeTURNPort(turnPort);
1836
1836
  const normalizedTURNSPort = normalizeTURNSPort(turnsPort);
1837
1837
  return [
1838
- `turn:${clusterDomain}:${normalizedTURNPort}?transport=udp`,
1839
- `turn:${clusterDomain}:${normalizedTURNPort}?transport=tcp`,
1840
- `turns:${clusterDomain}:${normalizedTURNSPort}?transport=udp`,
1841
- `turns:${clusterDomain}:${normalizedTURNSPort}?transport=tcp`
1838
+ `turn:${turnDomain}:${normalizedTURNPort}?transport=udp`,
1839
+ `turn:${turnDomain}:${normalizedTURNPort}?transport=tcp`,
1840
+ `turns:${turnDomain}:${normalizedTURNSPort}?transport=udp`,
1841
+ `turns:${turnDomain}:${normalizedTURNSPort}?transport=tcp`
1842
1842
  ];
1843
1843
  }
1844
1844
  function parseTURNTokenClaims(token) {
@@ -2030,9 +2030,14 @@ async function createAPITURNCredentials(options) {
2030
2030
  );
2031
2031
  }
2032
2032
  function createPATTURNCredentials(options) {
2033
- const clusterDomain = normalizeClusterDomain(options.clusterDomain);
2034
- if (!clusterDomain) {
2035
- throw new Error("Cluster domain is required for TURN PAT mode.");
2033
+ const legacyDomain = normalizeClusterDomain(options.clusterDomain);
2034
+ const turnDomain = normalizeClusterDomain(options.turnDomain) ?? legacyDomain;
2035
+ const turnRealm = normalizeClusterDomain(options.turnRealm) ?? legacyDomain;
2036
+ if (!turnDomain) {
2037
+ throw new Error("TURN domain is required for TURN PAT mode.");
2038
+ }
2039
+ if (!turnRealm) {
2040
+ throw new Error("TURN realm is required for TURN PAT mode.");
2036
2041
  }
2037
2042
  const projectEndpoint = normalizeOptionalString2(options.projectEndpoint);
2038
2043
  if (!projectEndpoint) {
@@ -2046,12 +2051,19 @@ function createPATTURNCredentials(options) {
2046
2051
  if (claims.type !== "pat") {
2047
2052
  throw new Error("TURN PAT mode requires a PAT token.");
2048
2053
  }
2049
- const tokenEndpoint = normalizeOptionalString2(options.tokenEndpoint);
2054
+ const claimsTokenEndpoint = normalizeOptionalString2(
2055
+ claims.token_endpoint ?? claims.tokendpoint
2056
+ );
2057
+ const configuredTokenEndpoint = normalizeOptionalString2(
2058
+ options.tokenEndpoint
2059
+ );
2060
+ const tokenEndpoint = configuredTokenEndpoint ?? claimsTokenEndpoint;
2050
2061
  if (!tokenEndpoint) {
2051
- throw new Error("Token endpoint is required for TURN PAT mode.");
2062
+ throw new Error(
2063
+ "TURN PAT mode requires a PAT token carrying a token endpoint."
2064
+ );
2052
2065
  }
2053
- const claimsTokenEndpoint = claims.token_endpoint ?? claims.tokendpoint;
2054
- if (claimsTokenEndpoint !== void 0 && claimsTokenEndpoint !== tokenEndpoint) {
2066
+ if (configuredTokenEndpoint !== void 0 && claimsTokenEndpoint !== void 0 && claimsTokenEndpoint !== configuredTokenEndpoint) {
2055
2067
  throw new Error("Token endpoint does not match the PAT token.");
2056
2068
  }
2057
2069
  const now = normalizeNow(options.now);
@@ -2064,7 +2076,7 @@ function createPATTURNCredentials(options) {
2064
2076
  import_crypto.default.hkdfSync(
2065
2077
  "sha256",
2066
2078
  tokenHash,
2067
- Buffer.from(clusterDomain, "utf8"),
2079
+ Buffer.from(turnRealm, "utf8"),
2068
2080
  Buffer.from("turn-pat-v1", "utf8"),
2069
2081
  32
2070
2082
  )
@@ -2073,14 +2085,19 @@ function createPATTURNCredentials(options) {
2073
2085
  return import_turn.turnCredentialsSchema.parse({
2074
2086
  credential,
2075
2087
  ttl,
2076
- urls: createTURNURLs(clusterDomain, options.turnPort, options.turnsPort),
2088
+ urls: createTURNURLs(turnDomain, options.turnPort, options.turnsPort),
2077
2089
  username
2078
2090
  });
2079
2091
  }
2080
2092
  async function createAPPTURNCredentials(options) {
2081
- const clusterDomain = normalizeClusterDomain(options.clusterDomain);
2082
- if (!clusterDomain) {
2083
- throw new Error("Cluster domain is required for TURN APP mode.");
2093
+ const legacyDomain = normalizeClusterDomain(options.clusterDomain);
2094
+ const turnDomain = normalizeClusterDomain(options.turnDomain) ?? legacyDomain;
2095
+ const turnRealm = normalizeClusterDomain(options.turnRealm) ?? legacyDomain;
2096
+ if (!turnDomain) {
2097
+ throw new Error("TURN domain is required for TURN APP mode.");
2098
+ }
2099
+ if (!turnRealm) {
2100
+ throw new Error("TURN realm is required for TURN APP mode.");
2084
2101
  }
2085
2102
  const projectEndpoint = normalizeOptionalString2(options.projectEndpoint);
2086
2103
  if (!projectEndpoint) {
@@ -2098,7 +2115,7 @@ async function createAPPTURNCredentials(options) {
2098
2115
  const exp = normalizeNow(options.now) + ttlSeconds;
2099
2116
  const username = `v1:${exp}:app:${projectEndpoint}:${clientId}`;
2100
2117
  const clientPrivateKey = parseAPPPrivateKey(clientSecret);
2101
- const serverPublicKeyHex = normalizeOptionalString2(options.serverPublicKeyHex) ?? await loadTURNServerPublicKeyHex(clusterDomain, options);
2118
+ const serverPublicKeyHex = normalizeOptionalString2(options.serverPublicKeyHex) ?? await loadTURNServerPublicKeyHex(turnRealm, options);
2102
2119
  const serverPublicKey = parseTURNServerPublicKeyHex(serverPublicKeyHex);
2103
2120
  requireMatchingCurve(clientPrivateKey, serverPublicKey);
2104
2121
  const sharedSecret = import_crypto.default.diffieHellman({
@@ -2109,7 +2126,7 @@ async function createAPPTURNCredentials(options) {
2109
2126
  import_crypto.default.hkdfSync(
2110
2127
  "sha256",
2111
2128
  sharedSecret,
2112
- Buffer.from(clusterDomain, "utf8"),
2129
+ Buffer.from(turnRealm, "utf8"),
2113
2130
  Buffer.from("turn-app-v1", "utf8"),
2114
2131
  32
2115
2132
  )
@@ -2118,7 +2135,7 @@ async function createAPPTURNCredentials(options) {
2118
2135
  return import_turn.turnCredentialsSchema.parse({
2119
2136
  credential,
2120
2137
  ttl: ttlSeconds,
2121
- urls: createTURNURLs(clusterDomain, options.turnPort, options.turnsPort),
2138
+ urls: createTURNURLs(turnDomain, options.turnPort, options.turnsPort),
2122
2139
  username
2123
2140
  });
2124
2141
  }
@@ -2141,28 +2158,31 @@ async function createTURNCredentials(options) {
2141
2158
  ttlSeconds: options.ttlSeconds
2142
2159
  });
2143
2160
  }
2144
- const clusterDomain = normalizeClusterDomain(options.clusterDomain) ?? (projectEndpoint && options.engine ? deriveClusterDomainFromEngine(projectEndpoint, options.engine) : void 0);
2161
+ const legacyDomain = normalizeClusterDomain(options.clusterDomain);
2162
+ const turnDomain = normalizeClusterDomain(options.turnDomain) ?? legacyDomain ?? (projectEndpoint && options.engine ? deriveClusterDomainFromEngine(projectEndpoint, options.engine) : void 0);
2163
+ const turnRealm = normalizeClusterDomain(options.turnRealm) ?? legacyDomain;
2145
2164
  if (mode === "pat") {
2146
2165
  if (!(0, import_rstream9.isTokenCredentials)(credentials)) {
2147
2166
  throw new Error("A PAT token is required for TURN PAT mode.");
2148
2167
  }
2149
2168
  return createPATTURNCredentials({
2150
- clusterDomain: requireOption(
2151
- clusterDomain,
2152
- "Cluster domain is required for TURN PAT mode."
2153
- ),
2154
2169
  now: options.now,
2155
2170
  projectEndpoint: requireOption(
2156
2171
  projectEndpoint,
2157
2172
  "Project endpoint is required for TURN PAT mode."
2158
2173
  ),
2159
2174
  token: credentials.token,
2160
- tokenEndpoint: requireOption(
2161
- normalizeOptionalString2(options.tokenEndpoint),
2162
- "Token endpoint is required for TURN PAT mode."
2163
- ),
2175
+ tokenEndpoint: options.tokenEndpoint,
2164
2176
  ttlSeconds: options.ttlSeconds,
2177
+ turnDomain: requireOption(
2178
+ turnDomain,
2179
+ "TURN domain is required for TURN PAT mode."
2180
+ ),
2165
2181
  turnPort: options.turnPort,
2182
+ turnRealm: requireOption(
2183
+ turnRealm,
2184
+ "TURN realm is required for TURN PAT mode."
2185
+ ),
2166
2186
  turnsPort: options.turnsPort
2167
2187
  });
2168
2188
  }
@@ -2173,10 +2193,6 @@ async function createTURNCredentials(options) {
2173
2193
  apiUrl: options.apiUrl,
2174
2194
  clientId: credentials.clientId,
2175
2195
  clientSecret: credentials.clientSecret,
2176
- clusterDomain: requireOption(
2177
- clusterDomain,
2178
- "Cluster domain is required for TURN APP mode."
2179
- ),
2180
2196
  keyringBaseUrl: options.keyringBaseUrl,
2181
2197
  now: options.now,
2182
2198
  projectEndpoint: requireOption(
@@ -2185,24 +2201,36 @@ async function createTURNCredentials(options) {
2185
2201
  ),
2186
2202
  serverPublicKeyHex: options.serverPublicKeyHex,
2187
2203
  ttlSeconds: options.ttlSeconds,
2204
+ turnDomain: requireOption(
2205
+ turnDomain,
2206
+ "TURN domain is required for TURN APP mode."
2207
+ ),
2188
2208
  turnPort: options.turnPort,
2209
+ turnRealm: requireOption(
2210
+ turnRealm,
2211
+ "TURN realm is required for TURN APP mode."
2212
+ ),
2189
2213
  turnsPort: options.turnsPort
2190
2214
  });
2191
2215
  }
2192
2216
 
2193
2217
  // src/turn-resource.ts
2218
+ var import_rstream11 = require("@rstreamlabs/rstream");
2194
2219
  var RstreamTURNResource = class {
2195
2220
  client;
2196
2221
  constructor(client) {
2197
2222
  this.client = client;
2198
2223
  }
2199
2224
  async createCredentials(options = {}) {
2200
- const engine = options.engine ?? (options.clusterDomain === void 0 ? await this.client.getEngine().catch(() => void 0) : void 0);
2225
+ const credentials = options.credentials ?? this.client.credentials;
2226
+ const localMode = options.mode === "app" || options.mode === "pat" || options.mode === void 0 && (0, import_rstream11.isClientCredentials)(credentials);
2227
+ const target = localMode && (options.turnDomain === void 0 || options.turnRealm === void 0) && options.clusterDomain === void 0 ? await this.client.getTURNTarget() : void 0;
2228
+ const engine = options.engine ?? (options.clusterDomain === void 0 && options.turnDomain === void 0 && target === void 0 ? await this.client.getEngine().catch(() => void 0) : void 0);
2201
2229
  return await createTURNCredentials({
2202
2230
  apiUrl: options.apiUrl ?? this.client.apiUrl,
2203
2231
  clusterDomain: options.clusterDomain,
2204
2232
  controlPlaneCredentials: options.controlPlaneCredentials ?? this.client.controlPlaneCredentials,
2205
- credentials: options.credentials ?? this.client.credentials,
2233
+ credentials,
2206
2234
  engine,
2207
2235
  keyringBaseUrl: options.keyringBaseUrl,
2208
2236
  mode: options.mode,
@@ -2212,8 +2240,10 @@ var RstreamTURNResource = class {
2212
2240
  serverPublicKeyHex: options.serverPublicKeyHex,
2213
2241
  tokenEndpoint: options.tokenEndpoint,
2214
2242
  ttlSeconds: options.ttlSeconds,
2215
- turnPort: options.turnPort,
2216
- turnsPort: options.turnsPort
2243
+ turnDomain: options.turnDomain ?? target?.turnDomain,
2244
+ turnPort: options.turnPort ?? target?.turnPort,
2245
+ turnRealm: options.turnRealm ?? target?.turnRealm,
2246
+ turnsPort: options.turnsPort ?? target?.turnsPort
2217
2247
  });
2218
2248
  }
2219
2249
  };
@@ -2394,7 +2424,7 @@ var RstreamTunnelsClient = class {
2394
2424
  this.config?.projectEndpoint
2395
2425
  );
2396
2426
  if (region !== void 0) {
2397
- if (normalizeOptionalString3(this.config?.engine) !== void 0 || (0, import_rstream13.readEnvironment)().engine !== void 0) {
2427
+ if (normalizeOptionalString3(this.config?.engine) !== void 0 || (0, import_rstream14.readEnvironment)().engine !== void 0) {
2398
2428
  throw new Error(
2399
2429
  "Region selection cannot be combined with an explicit engine override."
2400
2430
  );
@@ -2406,17 +2436,17 @@ var RstreamTunnelsClient = class {
2406
2436
  }
2407
2437
  const project = await this.getManagedProject();
2408
2438
  if (project !== void 0) {
2409
- const engine = (0, import_rstream11.getTunnelsProjectEngine)(project, region);
2439
+ const engine = (0, import_rstream12.getTunnelsProjectEngine)(project, region);
2410
2440
  if (engine) return normalizeEngineAddress(engine);
2411
2441
  }
2412
2442
  throw new Error(
2413
2443
  "Failed to resolve the selected regional engine from the managed tunnels project."
2414
2444
  );
2415
2445
  }
2416
- if (projectEndpoint !== void 0 && normalizeOptionalString3(this.config?.engine) === void 0 && (0, import_rstream13.readEnvironment)().engine === void 0) {
2446
+ if (projectEndpoint !== void 0 && normalizeOptionalString3(this.config?.engine) === void 0 && (0, import_rstream14.readEnvironment)().engine === void 0) {
2417
2447
  const project = await this.getManagedProject();
2418
2448
  if (project !== void 0) {
2419
- const engine = (0, import_rstream11.getTunnelsProjectEngine)(project);
2449
+ const engine = (0, import_rstream12.getTunnelsProjectEngine)(project);
2420
2450
  if (engine) {
2421
2451
  return normalizeEngineAddress(engine);
2422
2452
  }
@@ -2435,6 +2465,27 @@ var RstreamTunnelsClient = class {
2435
2465
  region: this.config?.region
2436
2466
  });
2437
2467
  }
2468
+ async getTURNTarget() {
2469
+ const project = await this.getManagedProject();
2470
+ if (project === void 0) {
2471
+ throw new Error(
2472
+ "Managed project endpoint is required to resolve the TURN target."
2473
+ );
2474
+ }
2475
+ const turnDomain = project.turnDomain?.trim() || project.domain.trim();
2476
+ const turnRealm = project.turnRealm?.trim();
2477
+ if (!turnRealm) {
2478
+ throw new Error(
2479
+ "Managed project metadata does not expose the TURN realm; use API credential mode or provide turnRealm explicitly."
2480
+ );
2481
+ }
2482
+ return {
2483
+ turnDomain,
2484
+ turnPort: project.turnPort,
2485
+ turnRealm,
2486
+ turnsPort: project.turnsPort
2487
+ };
2488
+ }
2438
2489
  async getManagedProject() {
2439
2490
  const projectEndpoint = normalizeOptionalString3(
2440
2491
  this.config?.projectEndpoint
@@ -2511,7 +2562,7 @@ var RstreamTunnelsClient = class {
2511
2562
  if (!credentials) {
2512
2563
  return void 0;
2513
2564
  }
2514
- if (!(0, import_rstream12.isClientCredentials)(credentials)) {
2565
+ if (!(0, import_rstream13.isClientCredentials)(credentials)) {
2515
2566
  return credentials.token;
2516
2567
  }
2517
2568
  const tokenParams = await this.getEngineAuthTokenParams();
package/dist/index.mjs CHANGED
@@ -404,7 +404,7 @@ var RstreamTunnelsResource = class {
404
404
 
405
405
  // src/tunnels.ts
406
406
  import { getTunnelsProjectEngine as getTunnelsProjectEngine2 } from "@rstreamlabs/rstream";
407
- import { isClientCredentials as isClientCredentials3 } from "@rstreamlabs/rstream";
407
+ import { isClientCredentials as isClientCredentials4 } from "@rstreamlabs/rstream";
408
408
  import { readEnvironment as readEnvironment2 } from "@rstreamlabs/rstream";
409
409
 
410
410
  // src/turn.ts
@@ -468,14 +468,14 @@ function normalizeTURNPort(port) {
468
468
  function normalizeTURNSPort(port) {
469
469
  return normalizePort(port, defaultTURNSPort, "TURNS port");
470
470
  }
471
- function createTURNURLs(clusterDomain, turnPort, turnsPort) {
471
+ function createTURNURLs(turnDomain, turnPort, turnsPort) {
472
472
  const normalizedTURNPort = normalizeTURNPort(turnPort);
473
473
  const normalizedTURNSPort = normalizeTURNSPort(turnsPort);
474
474
  return [
475
- `turn:${clusterDomain}:${normalizedTURNPort}?transport=udp`,
476
- `turn:${clusterDomain}:${normalizedTURNPort}?transport=tcp`,
477
- `turns:${clusterDomain}:${normalizedTURNSPort}?transport=udp`,
478
- `turns:${clusterDomain}:${normalizedTURNSPort}?transport=tcp`
475
+ `turn:${turnDomain}:${normalizedTURNPort}?transport=udp`,
476
+ `turn:${turnDomain}:${normalizedTURNPort}?transport=tcp`,
477
+ `turns:${turnDomain}:${normalizedTURNSPort}?transport=udp`,
478
+ `turns:${turnDomain}:${normalizedTURNSPort}?transport=tcp`
479
479
  ];
480
480
  }
481
481
  function parseTURNTokenClaims(token) {
@@ -667,9 +667,14 @@ async function createAPITURNCredentials(options) {
667
667
  );
668
668
  }
669
669
  function createPATTURNCredentials(options) {
670
- const clusterDomain = normalizeClusterDomain(options.clusterDomain);
671
- if (!clusterDomain) {
672
- throw new Error("Cluster domain is required for TURN PAT mode.");
670
+ const legacyDomain = normalizeClusterDomain(options.clusterDomain);
671
+ const turnDomain = normalizeClusterDomain(options.turnDomain) ?? legacyDomain;
672
+ const turnRealm = normalizeClusterDomain(options.turnRealm) ?? legacyDomain;
673
+ if (!turnDomain) {
674
+ throw new Error("TURN domain is required for TURN PAT mode.");
675
+ }
676
+ if (!turnRealm) {
677
+ throw new Error("TURN realm is required for TURN PAT mode.");
673
678
  }
674
679
  const projectEndpoint = normalizeOptionalString2(options.projectEndpoint);
675
680
  if (!projectEndpoint) {
@@ -683,12 +688,19 @@ function createPATTURNCredentials(options) {
683
688
  if (claims.type !== "pat") {
684
689
  throw new Error("TURN PAT mode requires a PAT token.");
685
690
  }
686
- const tokenEndpoint = normalizeOptionalString2(options.tokenEndpoint);
691
+ const claimsTokenEndpoint = normalizeOptionalString2(
692
+ claims.token_endpoint ?? claims.tokendpoint
693
+ );
694
+ const configuredTokenEndpoint = normalizeOptionalString2(
695
+ options.tokenEndpoint
696
+ );
697
+ const tokenEndpoint = configuredTokenEndpoint ?? claimsTokenEndpoint;
687
698
  if (!tokenEndpoint) {
688
- throw new Error("Token endpoint is required for TURN PAT mode.");
699
+ throw new Error(
700
+ "TURN PAT mode requires a PAT token carrying a token endpoint."
701
+ );
689
702
  }
690
- const claimsTokenEndpoint = claims.token_endpoint ?? claims.tokendpoint;
691
- if (claimsTokenEndpoint !== void 0 && claimsTokenEndpoint !== tokenEndpoint) {
703
+ if (configuredTokenEndpoint !== void 0 && claimsTokenEndpoint !== void 0 && claimsTokenEndpoint !== configuredTokenEndpoint) {
692
704
  throw new Error("Token endpoint does not match the PAT token.");
693
705
  }
694
706
  const now = normalizeNow(options.now);
@@ -701,7 +713,7 @@ function createPATTURNCredentials(options) {
701
713
  crypto.hkdfSync(
702
714
  "sha256",
703
715
  tokenHash,
704
- Buffer.from(clusterDomain, "utf8"),
716
+ Buffer.from(turnRealm, "utf8"),
705
717
  Buffer.from("turn-pat-v1", "utf8"),
706
718
  32
707
719
  )
@@ -710,14 +722,19 @@ function createPATTURNCredentials(options) {
710
722
  return turnCredentialsSchema.parse({
711
723
  credential,
712
724
  ttl,
713
- urls: createTURNURLs(clusterDomain, options.turnPort, options.turnsPort),
725
+ urls: createTURNURLs(turnDomain, options.turnPort, options.turnsPort),
714
726
  username
715
727
  });
716
728
  }
717
729
  async function createAPPTURNCredentials(options) {
718
- const clusterDomain = normalizeClusterDomain(options.clusterDomain);
719
- if (!clusterDomain) {
720
- throw new Error("Cluster domain is required for TURN APP mode.");
730
+ const legacyDomain = normalizeClusterDomain(options.clusterDomain);
731
+ const turnDomain = normalizeClusterDomain(options.turnDomain) ?? legacyDomain;
732
+ const turnRealm = normalizeClusterDomain(options.turnRealm) ?? legacyDomain;
733
+ if (!turnDomain) {
734
+ throw new Error("TURN domain is required for TURN APP mode.");
735
+ }
736
+ if (!turnRealm) {
737
+ throw new Error("TURN realm is required for TURN APP mode.");
721
738
  }
722
739
  const projectEndpoint = normalizeOptionalString2(options.projectEndpoint);
723
740
  if (!projectEndpoint) {
@@ -735,7 +752,7 @@ async function createAPPTURNCredentials(options) {
735
752
  const exp = normalizeNow(options.now) + ttlSeconds;
736
753
  const username = `v1:${exp}:app:${projectEndpoint}:${clientId}`;
737
754
  const clientPrivateKey = parseAPPPrivateKey(clientSecret);
738
- const serverPublicKeyHex = normalizeOptionalString2(options.serverPublicKeyHex) ?? await loadTURNServerPublicKeyHex(clusterDomain, options);
755
+ const serverPublicKeyHex = normalizeOptionalString2(options.serverPublicKeyHex) ?? await loadTURNServerPublicKeyHex(turnRealm, options);
739
756
  const serverPublicKey = parseTURNServerPublicKeyHex(serverPublicKeyHex);
740
757
  requireMatchingCurve(clientPrivateKey, serverPublicKey);
741
758
  const sharedSecret = crypto.diffieHellman({
@@ -746,7 +763,7 @@ async function createAPPTURNCredentials(options) {
746
763
  crypto.hkdfSync(
747
764
  "sha256",
748
765
  sharedSecret,
749
- Buffer.from(clusterDomain, "utf8"),
766
+ Buffer.from(turnRealm, "utf8"),
750
767
  Buffer.from("turn-app-v1", "utf8"),
751
768
  32
752
769
  )
@@ -755,7 +772,7 @@ async function createAPPTURNCredentials(options) {
755
772
  return turnCredentialsSchema.parse({
756
773
  credential,
757
774
  ttl: ttlSeconds,
758
- urls: createTURNURLs(clusterDomain, options.turnPort, options.turnsPort),
775
+ urls: createTURNURLs(turnDomain, options.turnPort, options.turnsPort),
759
776
  username
760
777
  });
761
778
  }
@@ -778,28 +795,31 @@ async function createTURNCredentials(options) {
778
795
  ttlSeconds: options.ttlSeconds
779
796
  });
780
797
  }
781
- const clusterDomain = normalizeClusterDomain(options.clusterDomain) ?? (projectEndpoint && options.engine ? deriveClusterDomainFromEngine(projectEndpoint, options.engine) : void 0);
798
+ const legacyDomain = normalizeClusterDomain(options.clusterDomain);
799
+ const turnDomain = normalizeClusterDomain(options.turnDomain) ?? legacyDomain ?? (projectEndpoint && options.engine ? deriveClusterDomainFromEngine(projectEndpoint, options.engine) : void 0);
800
+ const turnRealm = normalizeClusterDomain(options.turnRealm) ?? legacyDomain;
782
801
  if (mode === "pat") {
783
802
  if (!isTokenCredentials2(credentials)) {
784
803
  throw new Error("A PAT token is required for TURN PAT mode.");
785
804
  }
786
805
  return createPATTURNCredentials({
787
- clusterDomain: requireOption(
788
- clusterDomain,
789
- "Cluster domain is required for TURN PAT mode."
790
- ),
791
806
  now: options.now,
792
807
  projectEndpoint: requireOption(
793
808
  projectEndpoint,
794
809
  "Project endpoint is required for TURN PAT mode."
795
810
  ),
796
811
  token: credentials.token,
797
- tokenEndpoint: requireOption(
798
- normalizeOptionalString2(options.tokenEndpoint),
799
- "Token endpoint is required for TURN PAT mode."
800
- ),
812
+ tokenEndpoint: options.tokenEndpoint,
801
813
  ttlSeconds: options.ttlSeconds,
814
+ turnDomain: requireOption(
815
+ turnDomain,
816
+ "TURN domain is required for TURN PAT mode."
817
+ ),
802
818
  turnPort: options.turnPort,
819
+ turnRealm: requireOption(
820
+ turnRealm,
821
+ "TURN realm is required for TURN PAT mode."
822
+ ),
803
823
  turnsPort: options.turnsPort
804
824
  });
805
825
  }
@@ -810,10 +830,6 @@ async function createTURNCredentials(options) {
810
830
  apiUrl: options.apiUrl,
811
831
  clientId: credentials.clientId,
812
832
  clientSecret: credentials.clientSecret,
813
- clusterDomain: requireOption(
814
- clusterDomain,
815
- "Cluster domain is required for TURN APP mode."
816
- ),
817
833
  keyringBaseUrl: options.keyringBaseUrl,
818
834
  now: options.now,
819
835
  projectEndpoint: requireOption(
@@ -822,24 +838,36 @@ async function createTURNCredentials(options) {
822
838
  ),
823
839
  serverPublicKeyHex: options.serverPublicKeyHex,
824
840
  ttlSeconds: options.ttlSeconds,
841
+ turnDomain: requireOption(
842
+ turnDomain,
843
+ "TURN domain is required for TURN APP mode."
844
+ ),
825
845
  turnPort: options.turnPort,
846
+ turnRealm: requireOption(
847
+ turnRealm,
848
+ "TURN realm is required for TURN APP mode."
849
+ ),
826
850
  turnsPort: options.turnsPort
827
851
  });
828
852
  }
829
853
 
830
854
  // src/turn-resource.ts
855
+ import { isClientCredentials as isClientCredentials3 } from "@rstreamlabs/rstream";
831
856
  var RstreamTURNResource = class {
832
857
  client;
833
858
  constructor(client) {
834
859
  this.client = client;
835
860
  }
836
861
  async createCredentials(options = {}) {
837
- const engine = options.engine ?? (options.clusterDomain === void 0 ? await this.client.getEngine().catch(() => void 0) : void 0);
862
+ const credentials = options.credentials ?? this.client.credentials;
863
+ const localMode = options.mode === "app" || options.mode === "pat" || options.mode === void 0 && isClientCredentials3(credentials);
864
+ const target = localMode && (options.turnDomain === void 0 || options.turnRealm === void 0) && options.clusterDomain === void 0 ? await this.client.getTURNTarget() : void 0;
865
+ const engine = options.engine ?? (options.clusterDomain === void 0 && options.turnDomain === void 0 && target === void 0 ? await this.client.getEngine().catch(() => void 0) : void 0);
838
866
  return await createTURNCredentials({
839
867
  apiUrl: options.apiUrl ?? this.client.apiUrl,
840
868
  clusterDomain: options.clusterDomain,
841
869
  controlPlaneCredentials: options.controlPlaneCredentials ?? this.client.controlPlaneCredentials,
842
- credentials: options.credentials ?? this.client.credentials,
870
+ credentials,
843
871
  engine,
844
872
  keyringBaseUrl: options.keyringBaseUrl,
845
873
  mode: options.mode,
@@ -849,8 +877,10 @@ var RstreamTURNResource = class {
849
877
  serverPublicKeyHex: options.serverPublicKeyHex,
850
878
  tokenEndpoint: options.tokenEndpoint,
851
879
  ttlSeconds: options.ttlSeconds,
852
- turnPort: options.turnPort,
853
- turnsPort: options.turnsPort
880
+ turnDomain: options.turnDomain ?? target?.turnDomain,
881
+ turnPort: options.turnPort ?? target?.turnPort,
882
+ turnRealm: options.turnRealm ?? target?.turnRealm,
883
+ turnsPort: options.turnsPort ?? target?.turnsPort
854
884
  });
855
885
  }
856
886
  };
@@ -1072,6 +1102,27 @@ var RstreamTunnelsClient = class {
1072
1102
  region: this.config?.region
1073
1103
  });
1074
1104
  }
1105
+ async getTURNTarget() {
1106
+ const project = await this.getManagedProject();
1107
+ if (project === void 0) {
1108
+ throw new Error(
1109
+ "Managed project endpoint is required to resolve the TURN target."
1110
+ );
1111
+ }
1112
+ const turnDomain = project.turnDomain?.trim() || project.domain.trim();
1113
+ const turnRealm = project.turnRealm?.trim();
1114
+ if (!turnRealm) {
1115
+ throw new Error(
1116
+ "Managed project metadata does not expose the TURN realm; use API credential mode or provide turnRealm explicitly."
1117
+ );
1118
+ }
1119
+ return {
1120
+ turnDomain,
1121
+ turnPort: project.turnPort,
1122
+ turnRealm,
1123
+ turnsPort: project.turnsPort
1124
+ };
1125
+ }
1075
1126
  async getManagedProject() {
1076
1127
  const projectEndpoint = normalizeOptionalString3(
1077
1128
  this.config?.projectEndpoint
@@ -1148,7 +1199,7 @@ var RstreamTunnelsClient = class {
1148
1199
  if (!credentials) {
1149
1200
  return void 0;
1150
1201
  }
1151
- if (!isClientCredentials3(credentials)) {
1202
+ if (!isClientCredentials4(credentials)) {
1152
1203
  return credentials.token;
1153
1204
  }
1154
1205
  const tokenParams = await this.getEngineAuthTokenParams();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rstreamlabs/tunnels",
3
- "version": "3.8.1",
3
+ "version": "3.8.2",
4
4
  "description": "JS/TS SDK for the rstream Engine API and tunnel runtime APIs.",
5
5
  "author": "@uartnet <hello@rstream.io>",
6
6
  "repository": {
@@ -47,7 +47,7 @@
47
47
  "typescript-config": "*"
48
48
  },
49
49
  "dependencies": {
50
- "@rstreamlabs/rstream": "3.8.0",
50
+ "@rstreamlabs/rstream": "3.8.1",
51
51
  "@types/jsonwebtoken": "^9.0.10",
52
52
  "jsonwebtoken": "^9.0.3",
53
53
  "zod": "^4.4.3"