@rstreamlabs/tunnels 3.2.0 → 3.4.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.
package/dist/index.mjs CHANGED
@@ -1,23 +1,54 @@
1
1
  import {
2
+ RstreamWebTTYResource,
3
+ attachWebTTYParticipantParamsSchema,
2
4
  clientFilterSchema,
3
5
  clientSchema,
6
+ createWebTTYControlRequestParamsSchema,
7
+ detachWebTTYParticipantParamsSchema,
4
8
  engineOpenApiDocument,
5
9
  engineOpenApiJson,
6
10
  eventSchema,
11
+ formatStreamAccessPath,
7
12
  formatTunnelHost,
8
13
  listClientsParamsSchema,
9
14
  listClientsResponseSchema,
10
15
  listTunnelsParamsSchema,
11
16
  listTunnelsResponseSchema,
17
+ listWebTTYControlRequestsParamsSchema,
18
+ listWebTTYKeyGrantDecryptMaterialParamsSchema,
19
+ listWebTTYSessionGroupsParamsSchema,
20
+ listWebTTYSessionsParamsSchema,
12
21
  projectLogEventsSchema,
22
+ readWebTTYSessionEventsParamsSchema,
23
+ resolveWebTTYControlRequestParamsSchema,
13
24
  streamEndpointSchema,
25
+ streamEntrySchema,
14
26
  streamSummaryEventSchema,
15
27
  streamSummarySchema,
16
28
  streamTLSInfoSchema,
29
+ streamWebTTYMetadataSchema,
17
30
  tunnelFilterSchema,
31
+ webTTYCapabilitiesSchema,
32
+ webTTYContextMetadataSchema,
33
+ webTTYControlRequestSchema,
34
+ webTTYCryptoMetadataSchema,
35
+ webTTYEncryptionModeSchema,
36
+ webTTYKeyGrantDecryptMaterialSchema,
37
+ webTTYKeyGrantSchema,
38
+ webTTYOriginSchema,
39
+ webTTYParticipantLiveSchema,
40
+ webTTYParticipantRoleSchema,
41
+ webTTYParticipantSchema,
42
+ webTTYRecordingModeSchema,
43
+ webTTYSessionEventSchema,
44
+ webTTYSessionGroupSchema,
45
+ webTTYSessionLiveSchema,
46
+ webTTYSessionModeSchema,
47
+ webTTYSessionSchema,
48
+ webTTYTransportSchema,
18
49
  webhookEventsSchema,
19
50
  wsEventsSchema
20
- } from "./chunk-VUAM3LRU.mjs";
51
+ } from "./chunk-6OUCIT5G.mjs";
21
52
 
22
53
  // src/auth-resource.ts
23
54
  import { createAuthTokenParamsSchema } from "@rstreamlabs/rstream/auth-token";
@@ -730,9 +761,18 @@ var RstreamTURNResource = class {
730
761
 
731
762
  // src/webhooks-resource.ts
732
763
  import crypto2 from "crypto";
764
+ var WEBHOOK_SIGNATURE_HEADER = "rstream-signature";
765
+ var WEBHOOK_EVENT_ID_HEADER = "rstream-event-id";
766
+ var WEBHOOK_EVENT_TYPE_HEADER = "rstream-event-type";
767
+ var WEBHOOK_ID_HEADER = "rstream-webhook-id";
768
+ var WEBHOOK_DELIVERY_ID_HEADER = "rstream-delivery-id";
733
769
  var SIGNATURE_HEX_BYTES = 32;
770
+ var webhookSecretBytes = 32;
734
771
  var signatureHexSchema = /^[0-9a-fA-F]{64}$/;
735
772
  var timestampSchema = /^\d+$/;
773
+ function payloadBuffer(payload) {
774
+ return Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
775
+ }
736
776
  function parseSignatureTimestamp(timestamp) {
737
777
  if (!timestampSchema.test(timestamp)) {
738
778
  throw new Error("Invalid signature timestamp");
@@ -749,6 +789,19 @@ function normalizeTolerance(tolerance) {
749
789
  }
750
790
  return tolerance;
751
791
  }
792
+ function normalizeTimestamp(timestamp) {
793
+ if (timestamp === void 0) {
794
+ return Math.floor(Date.now() / 1e3);
795
+ }
796
+ const seconds = timestamp instanceof Date ? timestamp.getTime() / 1e3 : timestamp;
797
+ if (!Number.isFinite(seconds) || seconds < 0) {
798
+ throw new Error("Invalid webhook signature timestamp");
799
+ }
800
+ return Math.floor(seconds);
801
+ }
802
+ function signedPayload(timestamp, payload) {
803
+ return Buffer.concat([Buffer.from(`${timestamp}.`, "utf8"), payload]);
804
+ }
752
805
  function readSignature(signature) {
753
806
  if (!signatureHexSchema.test(signature)) {
754
807
  return void 0;
@@ -765,9 +818,42 @@ function assertSecretConfigured(secret) {
765
818
  throw new Error("Webhook signing secret is required");
766
819
  }
767
820
  }
821
+ function assertNonEmpty(value, label) {
822
+ const normalized = value.trim();
823
+ if (normalized.length === 0) {
824
+ throw new Error(`${label} is required`);
825
+ }
826
+ return normalized;
827
+ }
828
+ function generateWebhookSigningSecret() {
829
+ return `whsec_${crypto2.randomBytes(webhookSecretBytes).toString("base64url")}`;
830
+ }
831
+ function signWebhookPayload(payload, secret, timestamp) {
832
+ assertSecretConfigured(secret);
833
+ const timestampSeconds = normalizeTimestamp(timestamp).toString();
834
+ const signature = crypto2.createHmac("sha256", secret).update(signedPayload(timestampSeconds, payloadBuffer(payload))).digest("hex");
835
+ return `t=${timestampSeconds},v1=${signature}`;
836
+ }
837
+ function buildWebhookHeaders(payload, event, secret, options) {
838
+ const eventId = assertNonEmpty(event.id ?? "", "Webhook event id");
839
+ const eventType = assertNonEmpty(event.type, "Webhook event type");
840
+ const webhookId = assertNonEmpty(options.webhookId, "Webhook id");
841
+ const deliveryId = assertNonEmpty(options.deliveryId, "Webhook delivery id");
842
+ return {
843
+ [WEBHOOK_SIGNATURE_HEADER]: signWebhookPayload(
844
+ payload,
845
+ secret,
846
+ options.timestamp
847
+ ),
848
+ [WEBHOOK_EVENT_ID_HEADER]: eventId,
849
+ [WEBHOOK_EVENT_TYPE_HEADER]: eventType,
850
+ [WEBHOOK_ID_HEADER]: webhookId,
851
+ [WEBHOOK_DELIVERY_ID_HEADER]: deliveryId
852
+ };
853
+ }
768
854
  var RstreamWebhookResource = class {
769
855
  async event(payload, header, secret, tolerance = 300, receivedAt = Date.now()) {
770
- const payloadBuffer = Buffer.isBuffer(payload) ? payload : Buffer.from(payload);
856
+ const rawPayload = payloadBuffer(payload);
771
857
  const signatureHeader = Array.isArray(header) ? header[0] : header.toString();
772
858
  if (!signatureHeader) {
773
859
  throw new Error("No signature header");
@@ -787,8 +873,7 @@ var RstreamWebhookResource = class {
787
873
  `Webhook signature timestamp outside tolerance: ${now}, ${timestampNum}`
788
874
  );
789
875
  }
790
- const signedPayload = `${timestamp}.${payloadBuffer.toString("utf8")}`;
791
- const expectedSignature = crypto2.createHmac("sha256", secret).update(signedPayload).digest();
876
+ const expectedSignature = crypto2.createHmac("sha256", secret).update(signedPayload(timestamp, rawPayload)).digest();
792
877
  const signatureMatched = signatures.some(
793
878
  (signature) => signatureMatches(signature, expectedSignature)
794
879
  );
@@ -796,9 +881,7 @@ var RstreamWebhookResource = class {
796
881
  throw new Error("Signature verification failed");
797
882
  }
798
883
  try {
799
- return webhookEventsSchema.parse(
800
- JSON.parse(payloadBuffer.toString("utf8"))
801
- );
884
+ return webhookEventsSchema.parse(JSON.parse(rawPayload.toString("utf8")));
802
885
  } catch (error) {
803
886
  throw new Error(
804
887
  `Failed to parse webhook payload: ${error instanceof Error ? error.message : String(error)}`,
@@ -806,6 +889,15 @@ var RstreamWebhookResource = class {
806
889
  );
807
890
  }
808
891
  }
892
+ generateSigningSecret() {
893
+ return generateWebhookSigningSecret();
894
+ }
895
+ sign(payload, secret, timestamp) {
896
+ return signWebhookPayload(payload, secret, timestamp);
897
+ }
898
+ headers(payload, event, secret, options) {
899
+ return buildWebhookHeaders(payload, event, secret, options);
900
+ }
809
901
  };
810
902
 
811
903
  // src/tunnels.ts
@@ -924,6 +1016,9 @@ var RstreamTunnelsClient = class {
924
1016
  get webhooks() {
925
1017
  return new RstreamWebhookResource();
926
1018
  }
1019
+ get webtty() {
1020
+ return new RstreamWebTTYResource(this);
1021
+ }
927
1022
  get turn() {
928
1023
  return new RstreamTURNResource(this);
929
1024
  }
@@ -1149,10 +1244,23 @@ var osFamilySchema = z2.enum([
1149
1244
  ]);
1150
1245
  var webttyCapabilitySchema = z2.enum(["exec", "fs"]);
1151
1246
  var webttyFSModeSchema = z2.enum(["read-write", "read-only"]);
1247
+ var webttyEncryptionPolicySchema = z2.enum([
1248
+ "disabled",
1249
+ "explicit_key",
1250
+ "workspace_managed"
1251
+ ]);
1152
1252
  var webttyServerSchema = z2.object({
1153
1253
  tunnel_id: z2.string(),
1254
+ tunnel_protocol: z2.enum(["http", "webtty"]),
1255
+ managed: z2.boolean(),
1154
1256
  host: z2.string(),
1155
1257
  token_auth: z2.boolean(),
1258
+ server_id: z2.string().optional(),
1259
+ server_name: z2.string().optional(),
1260
+ host_key_id: z2.string().optional(),
1261
+ e2e: z2.enum(["disabled", "required"]).optional(),
1262
+ client_proof: z2.enum(["none", "required"]).optional(),
1263
+ encryption_policy: webttyEncryptionPolicySchema.optional(),
1156
1264
  capabilities: z2.array(webttyCapabilitySchema).optional(),
1157
1265
  exec_path: z2.string().optional(),
1158
1266
  fs_path: z2.string().optional(),
@@ -1186,9 +1294,10 @@ function webTTYHasCapability(capabilities, capability) {
1186
1294
  function parser(tunnel) {
1187
1295
  if (tunnel.status !== "online") return null;
1188
1296
  if (tunnel.publish !== true) return null;
1189
- if (tunnel.protocol !== "http") return null;
1297
+ const managedProtocol = tunnel.protocol === "webtty";
1298
+ if (tunnel.protocol !== "http" && !managedProtocol) return null;
1190
1299
  const tunnelLabels = tunnel.labels ?? {};
1191
- if (tunnelLabels["application-protocol"] !== "rstream.webtty") {
1300
+ if (!managedProtocol && tunnelLabels["application-protocol"] !== "rstream.webtty") {
1192
1301
  return null;
1193
1302
  }
1194
1303
  const capabilities = webTTYCapabilitiesForLabels(tunnelLabels);
@@ -1201,8 +1310,16 @@ function parser(tunnel) {
1201
1310
  }
1202
1311
  const candidate = {
1203
1312
  tunnel_id: tunnel.id,
1313
+ tunnel_protocol: tunnel.protocol,
1314
+ managed: managedProtocol,
1204
1315
  host: formatTunnelHost(tunnel),
1205
1316
  token_auth: tunnel.token_auth === true,
1317
+ server_id: tunnelLabels["rstream.webtty.server_id"],
1318
+ server_name: tunnelLabels["rstream.webtty.server_name"],
1319
+ host_key_id: tunnelLabels["rstream.webtty.host_key_id"],
1320
+ e2e: tunnelLabels["rstream.webtty.e2e"],
1321
+ client_proof: tunnelLabels["rstream.webtty.client_proof"],
1322
+ encryption_policy: tunnelLabels["rstream.webtty.encryption_policy"],
1206
1323
  capabilities,
1207
1324
  exec_path: webTTYHasCapability(capabilities, "exec") ? tunnelLabels["rstream.webtty.exec.path"] ?? "/" : void 0,
1208
1325
  fs_path: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.path"] ?? "/fs" : void 0,
@@ -1234,10 +1351,18 @@ export {
1234
1351
  RstreamTunnelsClient,
1235
1352
  RstreamTunnelsResource,
1236
1353
  RstreamTunnelsResource as RstreamTunnelsRessource,
1354
+ RstreamWebTTYResource,
1237
1355
  RstreamWebhookResource,
1238
1356
  RstreamWebhookResource as RstreamWebhookRessource,
1239
1357
  RstreamTunnelsClient as Tunnels,
1358
+ WEBHOOK_DELIVERY_ID_HEADER,
1359
+ WEBHOOK_EVENT_ID_HEADER,
1360
+ WEBHOOK_EVENT_TYPE_HEADER,
1361
+ WEBHOOK_ID_HEADER,
1362
+ WEBHOOK_SIGNATURE_HEADER,
1240
1363
  Watch,
1364
+ attachWebTTYParticipantParamsSchema,
1365
+ buildWebhookHeaders,
1241
1366
  clientFilterSchema,
1242
1367
  clientSchema,
1243
1368
  createAPITURNCredentials,
@@ -1245,28 +1370,59 @@ export {
1245
1370
  createAuthTokenFromClientCredentials,
1246
1371
  createPATTURNCredentials,
1247
1372
  createTURNCredentials,
1373
+ createWebTTYControlRequestParamsSchema,
1374
+ detachWebTTYParticipantParamsSchema,
1248
1375
  engineOpenApiDocument,
1249
1376
  engineOpenApiJson,
1250
1377
  eventSchema,
1378
+ formatStreamAccessPath,
1251
1379
  formatTunnelHost,
1380
+ generateWebhookSigningSecret,
1252
1381
  listClientsParamsSchema,
1253
1382
  listClientsResponseSchema,
1254
1383
  listTunnelsParamsSchema,
1255
1384
  listTunnelsResponseSchema,
1385
+ listWebTTYControlRequestsParamsSchema,
1386
+ listWebTTYKeyGrantDecryptMaterialParamsSchema,
1387
+ listWebTTYSessionGroupsParamsSchema,
1388
+ listWebTTYSessionsParamsSchema,
1256
1389
  normalizeEngineAddress,
1257
1390
  parseWebTTYServers,
1258
1391
  projectLogEventsSchema,
1392
+ readWebTTYSessionEventsParamsSchema,
1259
1393
  resolveControlPlaneCredentials,
1260
1394
  resolveManagedTunnelsProject,
1261
1395
  resolveTunnelsAPIURL,
1262
1396
  resolveTunnelsCredentials,
1263
1397
  resolveTunnelsEngine,
1398
+ resolveWebTTYControlRequestParamsSchema,
1399
+ signWebhookPayload,
1264
1400
  streamEndpointSchema,
1401
+ streamEntrySchema,
1265
1402
  streamSummaryEventSchema,
1266
1403
  streamSummarySchema,
1267
1404
  streamTLSInfoSchema,
1405
+ streamWebTTYMetadataSchema,
1268
1406
  tunnelFilterSchema,
1269
1407
  turnCredentialModeSchema,
1408
+ webTTYCapabilitiesSchema,
1409
+ webTTYContextMetadataSchema,
1410
+ webTTYControlRequestSchema,
1411
+ webTTYCryptoMetadataSchema,
1412
+ webTTYEncryptionModeSchema,
1413
+ webTTYKeyGrantDecryptMaterialSchema,
1414
+ webTTYKeyGrantSchema,
1415
+ webTTYOriginSchema,
1416
+ webTTYParticipantLiveSchema,
1417
+ webTTYParticipantRoleSchema,
1418
+ webTTYParticipantSchema,
1419
+ webTTYRecordingModeSchema,
1420
+ webTTYSessionEventSchema,
1421
+ webTTYSessionGroupSchema,
1422
+ webTTYSessionLiveSchema,
1423
+ webTTYSessionModeSchema,
1424
+ webTTYSessionSchema,
1425
+ webTTYTransportSchema,
1270
1426
  webhookEventsSchema,
1271
1427
  webttyServerSchema,
1272
1428
  wsEventsSchema
@@ -116,7 +116,10 @@ declare const engineOpenApiDocument: {
116
116
  "inspect-engine-state": string[];
117
117
  "expose-service-from-cli": string[];
118
118
  "connect-private-tunnel": string[];
119
- "operate-webtty": string[];
119
+ "run-tunnel-webtty": string[];
120
+ "join-managed-webtty": string[];
121
+ "control-managed-webtty": string[];
122
+ "read-managed-webtty-recordings": string[];
120
123
  };
121
124
  paths: {
122
125
  "/api/openapi.json": {
package/dist/openapi.d.ts CHANGED
@@ -116,7 +116,10 @@ declare const engineOpenApiDocument: {
116
116
  "inspect-engine-state": string[];
117
117
  "expose-service-from-cli": string[];
118
118
  "connect-private-tunnel": string[];
119
- "operate-webtty": string[];
119
+ "run-tunnel-webtty": string[];
120
+ "join-managed-webtty": string[];
121
+ "control-managed-webtty": string[];
122
+ "read-managed-webtty-recordings": string[];
120
123
  };
121
124
  paths: {
122
125
  "/api/openapi.json": {