@rstreamlabs/tunnels 1.0.5 → 1.0.7

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
@@ -48,6 +48,7 @@ __export(index_exports, {
48
48
  authTokenScopesSchema: () => authTokenScopesSchema,
49
49
  authTokenTunnelGrantSchema: () => authTokenTunnelGrantSchema,
50
50
  authTokenTunnelsScopesSchema: () => authTokenTunnelsScopesSchema,
51
+ clientFilterSchema: () => clientFilterSchema,
51
52
  clientSchema: () => clientSchema,
52
53
  createAPITURNCredentials: () => createAPITURNCredentials,
53
54
  createAPPTURNCredentials: () => createAPPTURNCredentials,
@@ -56,7 +57,10 @@ __export(index_exports, {
56
57
  createAuthTokenResponseSchema: () => createAuthTokenResponseSchema,
57
58
  createPATTURNCredentials: () => createPATTURNCredentials,
58
59
  createTURNCredentials: () => createTURNCredentials,
60
+ engineOpenApiDocument: () => engineOpenApiDocument,
61
+ engineOpenApiJson: () => engineOpenApiJson,
59
62
  eventSchema: () => eventSchema,
63
+ formatTunnelHost: () => formatTunnelHost,
60
64
  listClientsParamsSchema: () => listClientsParamsSchema,
61
65
  listClientsResponseSchema: () => listClientsResponseSchema,
62
66
  listTunnelsParamsSchema: () => listTunnelsParamsSchema,
@@ -70,6 +74,7 @@ __export(index_exports, {
70
74
  streamSummarySchema: () => streamSummarySchema,
71
75
  streamTLSInfoSchema: () => streamTLSInfoSchema,
72
76
  tokenSchema: () => tokenSchema,
77
+ tunnelFilterSchema: () => tunnelFilterSchema,
73
78
  tunnelSchema: () => tunnelSchema,
74
79
  turnCredentialModeSchema: () => turnCredentialModeSchema,
75
80
  webhookEventsSchema: () => webhookEventsSchema,
@@ -126,6 +131,7 @@ function select(base) {
126
131
 
127
132
  // src/tunnel.ts
128
133
  var z2 = __toESM(require("zod"));
134
+ var DEFAULT_PUBLISHED_PORT = 443;
129
135
  var tunnelSchema = z2.object({
130
136
  // common properties
131
137
  client_id: z2.string(),
@@ -148,6 +154,8 @@ var tunnelSchema = z2.object({
148
154
  geo_ip: z2.array(z2.string()).optional(),
149
155
  trusted_ips: z2.array(z2.string()).optional(),
150
156
  host: z2.string().optional(),
157
+ hostname: z2.string().optional(),
158
+ port: z2.number().int().min(1).max(65535).optional(),
151
159
  tls_mode: z2.enum(["passthrough", "terminated"]).optional(),
152
160
  tls_alpns: z2.array(z2.string()).optional(),
153
161
  tls_min_version: z2.string().optional(),
@@ -156,21 +164,36 @@ var tunnelSchema = z2.object({
156
164
  mtls_ca_cert_pem: z2.string().optional(),
157
165
  http_version: z2.enum(["http/1.1", "h2", "h2c", "h3"]).optional(),
158
166
  http_use_tls: z2.boolean().optional(),
167
+ upstream_tls: z2.boolean().optional(),
159
168
  token_auth: z2.boolean().optional(),
160
169
  rstream_auth: z2.boolean().optional(),
161
170
  challenge_mode: z2.boolean().optional()
162
171
  });
172
+ var tunnelFilterSchema = tunnelSchema.pick({
173
+ id: true,
174
+ name: true,
175
+ type: true,
176
+ status: true,
177
+ client_id: true,
178
+ user_id: true,
179
+ publish: true,
180
+ protocol: true,
181
+ hostname: true,
182
+ http_version: true,
183
+ labels: true
184
+ }).partial();
163
185
  var listTunnelsParamsSchema = z2.object({
164
- limit: z2.number().optional(),
165
- filters: tunnelSchema.pick({
166
- status: true,
167
- client_id: true,
168
- protocol: true,
169
- publish: true,
170
- labels: true
171
- }).partial().optional()
186
+ limit: z2.number().int().min(1).optional(),
187
+ filters: tunnelFilterSchema.optional()
172
188
  });
173
189
  var listTunnelsResponseSchema = z2.array(tunnelSchema);
190
+ function formatTunnelHost(tunnel) {
191
+ if (!tunnel.hostname) return tunnel.host;
192
+ if (!tunnel.port || tunnel.port === DEFAULT_PUBLISHED_PORT) {
193
+ return tunnel.hostname;
194
+ }
195
+ return `${tunnel.hostname}:${tunnel.port}`;
196
+ }
174
197
 
175
198
  // src/auth.ts
176
199
  var z3 = __toESM(require("zod"));
@@ -358,11 +381,21 @@ var clientSchema = z4.object({
358
381
  protocol_version: z4.string().optional(),
359
382
  labels: z4.record(z4.string(), z4.string()).optional()
360
383
  });
384
+ var clientFilterSchema = clientSchema.pick({
385
+ id: true,
386
+ status: true,
387
+ user_id: true,
388
+ agent: true,
389
+ channel: true,
390
+ version: true,
391
+ os: true,
392
+ arch: true,
393
+ protocol_version: true,
394
+ labels: true
395
+ }).partial();
361
396
  var listClientsParamsSchema = z4.object({
362
- limit: z4.number().optional(),
363
- filters: clientSchema.pick({
364
- labels: true
365
- }).partial().optional()
397
+ limit: z4.number().int().min(1).optional(),
398
+ filters: clientFilterSchema.optional()
366
399
  });
367
400
  var listClientsResponseSchema = z4.array(clientSchema);
368
401
 
@@ -521,12 +554,14 @@ var firewallSchema = z5.discriminatedUnion("decision", [
521
554
  var routedTunnelSchema = z5.object({
522
555
  tunnelId: nonEmptyStringSchema2.optional(),
523
556
  tunnelName: z5.string().optional(),
557
+ hostname: z5.string().optional(),
524
558
  protocol: z5.string().optional(),
525
559
  published: z5.boolean().optional(),
526
560
  type: z5.string().optional(),
527
561
  tlsMode: z5.string().optional(),
528
562
  httpVersion: z5.string().optional(),
529
563
  httpUseTls: z5.boolean().optional(),
564
+ upstreamTls: z5.boolean().optional(),
530
565
  tokenAuth: z5.boolean().optional(),
531
566
  mtls: z5.boolean().optional()
532
567
  });
@@ -649,6 +684,358 @@ var webhookEventsSchema = z6.union([
649
684
  ]);
650
685
  var eventSchema = z6.union([wsEventsSchema, webhookEventsSchema]);
651
686
 
687
+ // src/openapi.ts
688
+ var z7 = __toESM(require("zod"));
689
+ var jsonContentType = "application/json";
690
+ var openApiContentType = "application/vnd.oai.openapi+json;version=3.1";
691
+ function isRecord(value) {
692
+ return typeof value === "object" && value !== null && !Array.isArray(value);
693
+ }
694
+ function dropSchemaKeyword(value) {
695
+ if (!isRecord(value)) return value;
696
+ return Object.fromEntries(
697
+ Object.entries(value).filter(([key]) => key !== "$schema")
698
+ );
699
+ }
700
+ function jsonSchemaFor(schema) {
701
+ return dropSchemaKeyword(
702
+ z7.toJSONSchema(schema, {
703
+ unrepresentable: "any",
704
+ override: (ctx) => {
705
+ if (ctx.zodSchema instanceof z7.ZodDate) {
706
+ ctx.jsonSchema.type = "string";
707
+ ctx.jsonSchema.format = "date-time";
708
+ }
709
+ }
710
+ })
711
+ );
712
+ }
713
+ function jsonArrayResponse(description, itemSchemaRef) {
714
+ return {
715
+ description,
716
+ content: {
717
+ [jsonContentType]: {
718
+ schema: {
719
+ type: "array",
720
+ items: {
721
+ $ref: itemSchemaRef
722
+ }
723
+ }
724
+ }
725
+ }
726
+ };
727
+ }
728
+ function streamResponse(description) {
729
+ return {
730
+ description,
731
+ content: {
732
+ "text/event-stream": {
733
+ schema: {
734
+ type: "string"
735
+ }
736
+ }
737
+ }
738
+ };
739
+ }
740
+ function errorResponse(description) {
741
+ return {
742
+ description
743
+ };
744
+ }
745
+ function queryJsonParameter(name, description, schemaRef, examples) {
746
+ return {
747
+ name,
748
+ in: "query",
749
+ required: false,
750
+ description,
751
+ content: {
752
+ [jsonContentType]: {
753
+ schema: {
754
+ $ref: schemaRef
755
+ },
756
+ ...examples ? { examples } : {}
757
+ }
758
+ }
759
+ };
760
+ }
761
+ var readEngineStateRule = {
762
+ resource: ["engine.tunnels", "engine.streams"],
763
+ action: "read"
764
+ };
765
+ var readEngineStateRules = [readEngineStateRule];
766
+ var readEngineStreamsRules = [
767
+ {
768
+ resource: "engine.streams",
769
+ action: "read"
770
+ }
771
+ ];
772
+ var watchParamsSchema = z7.object({
773
+ clients: clientFilterSchema.optional(),
774
+ tunnels: tunnelFilterSchema.optional()
775
+ });
776
+ var enginePermissions = {
777
+ "tunnels.resources.read-only": {
778
+ name: "Discovery: Read-Only",
779
+ description: "Read active clients, active tunnels, and live state events.",
780
+ rules: [readEngineStateRule]
781
+ },
782
+ "tunnels.tunnels.create-delete": {
783
+ name: "Tunnels: Create and Delete",
784
+ description: "Create and close tunnels through the CLI, SDK, or engine control channel.",
785
+ rules: [
786
+ {
787
+ resource: "engine.tunnels",
788
+ action: ["create", "delete"]
789
+ }
790
+ ]
791
+ },
792
+ "tunnels.streams.create-delete": {
793
+ name: "Connections: Create and Delete",
794
+ description: "Open and close private tunnel streams and WebTTY connections.",
795
+ rules: [
796
+ {
797
+ resource: "engine.streams",
798
+ action: ["create", "delete"]
799
+ }
800
+ ]
801
+ }
802
+ };
803
+ var engineOpenApiDocument = {
804
+ openapi: "3.1.0",
805
+ info: {
806
+ title: "rstream Engine API",
807
+ version: "1.0.0",
808
+ description: "Operational data-plane API exposed by rstream-engine. This surface is scoped to the authenticated engine or project endpoint and covers live clients, live tunnels, and state-change streams."
809
+ },
810
+ servers: [
811
+ {
812
+ url: "https://{engine}",
813
+ description: "Project-scoped rstream engine endpoint.",
814
+ variables: {
815
+ engine: {
816
+ default: "project-endpoint.cluster-host:443",
817
+ description: "Use the engine value returned by project discovery or configured in RSTREAM_ENGINE."
818
+ }
819
+ }
820
+ }
821
+ ],
822
+ security: [
823
+ {
824
+ bearerAuth: []
825
+ }
826
+ ],
827
+ components: {
828
+ securitySchemes: {
829
+ bearerAuth: {
830
+ type: "http",
831
+ scheme: "bearer",
832
+ bearerFormat: "rstream token",
833
+ description: "Preferred authentication mechanism. Send an OAuth-issued rstream access token or compatible rstream credential in Authorization: Bearer <token>."
834
+ },
835
+ queryToken: {
836
+ type: "apiKey",
837
+ in: "query",
838
+ name: "rstream.token",
839
+ description: "Fallback authentication mechanism for transports where setting Authorization is impractical. Prefer bearerAuth whenever possible."
840
+ }
841
+ },
842
+ parameters: {
843
+ ListClientsParams: queryJsonParameter(
844
+ "params",
845
+ "JSON-encoded ListClientsParams object.",
846
+ "#/components/schemas/ListClientsParams",
847
+ {
848
+ filterByAgent: {
849
+ value: {
850
+ limit: 10,
851
+ filters: {
852
+ agent: "rstream",
853
+ channel: "stable"
854
+ }
855
+ }
856
+ }
857
+ }
858
+ ),
859
+ ListTunnelsParams: queryJsonParameter(
860
+ "params",
861
+ "JSON-encoded ListTunnelsParams object.",
862
+ "#/components/schemas/ListTunnelsParams",
863
+ {
864
+ filterByLabels: {
865
+ value: {
866
+ filters: {
867
+ labels: {
868
+ service: "ssh",
869
+ env: "prod"
870
+ }
871
+ }
872
+ }
873
+ }
874
+ }
875
+ ),
876
+ WatchParams: queryJsonParameter(
877
+ "params",
878
+ "JSON-encoded WatchParams object. The same shape is accepted by SSE and WebSocket transports.",
879
+ "#/components/schemas/WatchParams",
880
+ {
881
+ watchTaggedTunnels: {
882
+ value: {
883
+ clients: {
884
+ agent: "rstream"
885
+ },
886
+ tunnels: {
887
+ labels: {
888
+ service: "ssh"
889
+ }
890
+ }
891
+ }
892
+ }
893
+ }
894
+ )
895
+ },
896
+ responses: {
897
+ BadRequest: errorResponse("Request validation failed."),
898
+ Unauthorized: errorResponse("Authentication is required."),
899
+ Forbidden: errorResponse("The token is not authorized.")
900
+ },
901
+ schemas: {
902
+ Client: jsonSchemaFor(clientSchema),
903
+ Event: jsonSchemaFor(wsEventsSchema),
904
+ ListClientsParams: jsonSchemaFor(listClientsParamsSchema),
905
+ ListTunnelsParams: jsonSchemaFor(listTunnelsParamsSchema),
906
+ TunnelListItem: jsonSchemaFor(tunnelSchema),
907
+ WatchParams: jsonSchemaFor(watchParamsSchema)
908
+ }
909
+ },
910
+ "x-rstream-agent-authentication": {
911
+ oauth_authorization_server_metadata: "https://rstream.io/.well-known/oauth-authorization-server",
912
+ oauth_protected_resource_metadata: "https://rstream.io/.well-known/oauth-protected-resource",
913
+ scope_selection: "Use x-rstream-required-rules to understand backend authorization checks. Choose OAuth scopes from x-rstream-permissions or use the task bundles in x-rstream-use-case-permissions."
914
+ },
915
+ "x-rstream-permissions": enginePermissions,
916
+ "x-rstream-use-case-permissions": {
917
+ "inspect-engine-state": ["tunnels.resources.read-only"],
918
+ "expose-service-from-cli": [
919
+ "tunnels.resources.read-only",
920
+ "tunnels.tunnels.create-delete"
921
+ ],
922
+ "connect-private-tunnel": [
923
+ "tunnels.resources.read-only",
924
+ "tunnels.streams.create-delete"
925
+ ],
926
+ "operate-webtty": [
927
+ "tunnels.resources.read-only",
928
+ "tunnels.tunnels.create-delete",
929
+ "tunnels.streams.create-delete"
930
+ ]
931
+ },
932
+ paths: {
933
+ "/api/openapi.json": {
934
+ get: {
935
+ tags: ["Discovery"],
936
+ summary: "Return this OpenAPI description.",
937
+ operationId: "get_engine_openapi",
938
+ security: [],
939
+ responses: {
940
+ "200": {
941
+ description: "OpenAPI document.",
942
+ content: {
943
+ [openApiContentType]: {
944
+ schema: {
945
+ type: "object"
946
+ }
947
+ }
948
+ }
949
+ }
950
+ }
951
+ }
952
+ },
953
+ "/api/clients": {
954
+ get: {
955
+ tags: ["Runtime inventory"],
956
+ summary: "List visible engine clients.",
957
+ operationId: "list_engine_clients",
958
+ parameters: [{ $ref: "#/components/parameters/ListClientsParams" }],
959
+ security: [{ bearerAuth: [] }, { queryToken: [] }],
960
+ responses: {
961
+ "200": jsonArrayResponse(
962
+ "Visible clients.",
963
+ "#/components/schemas/Client"
964
+ ),
965
+ "400": { $ref: "#/components/responses/BadRequest" },
966
+ "401": { $ref: "#/components/responses/Unauthorized" },
967
+ "403": { $ref: "#/components/responses/Forbidden" }
968
+ },
969
+ "x-rstream-required-rules": readEngineStreamsRules
970
+ }
971
+ },
972
+ "/api/tunnels": {
973
+ get: {
974
+ tags: ["Runtime inventory"],
975
+ summary: "List visible engine tunnels.",
976
+ operationId: "list_engine_tunnels",
977
+ parameters: [{ $ref: "#/components/parameters/ListTunnelsParams" }],
978
+ security: [{ bearerAuth: [] }, { queryToken: [] }],
979
+ responses: {
980
+ "200": jsonArrayResponse(
981
+ "Visible tunnels. Fine-grained list scopes may filter objects and project fields.",
982
+ "#/components/schemas/TunnelListItem"
983
+ ),
984
+ "400": { $ref: "#/components/responses/BadRequest" },
985
+ "401": { $ref: "#/components/responses/Unauthorized" },
986
+ "403": { $ref: "#/components/responses/Forbidden" }
987
+ },
988
+ "x-rstream-required-rules": readEngineStateRules,
989
+ "x-rstream-token-metadata-scope": "metadata.scopes.tunnels.list"
990
+ }
991
+ },
992
+ "/api/sse": {
993
+ get: {
994
+ tags: ["Runtime watch"],
995
+ summary: "Subscribe to visible engine state through Server-Sent Events.",
996
+ operationId: "watch_engine_events_sse",
997
+ parameters: [{ $ref: "#/components/parameters/WatchParams" }],
998
+ security: [{ bearerAuth: [] }, { queryToken: [] }],
999
+ responses: {
1000
+ "200": streamResponse(
1001
+ "SSE stream. Each event is emitted as a data line containing a JSON Event object."
1002
+ ),
1003
+ "400": { $ref: "#/components/responses/BadRequest" },
1004
+ "401": { $ref: "#/components/responses/Unauthorized" },
1005
+ "403": { $ref: "#/components/responses/Forbidden" }
1006
+ },
1007
+ "x-rstream-event-schema": "#/components/schemas/Event",
1008
+ "x-rstream-required-rules": readEngineStateRules
1009
+ }
1010
+ },
1011
+ "/api/websocket": {
1012
+ get: {
1013
+ tags: ["Runtime watch"],
1014
+ summary: "Subscribe to visible engine state through a WebSocket upgrade.",
1015
+ operationId: "watch_engine_events_websocket",
1016
+ parameters: [{ $ref: "#/components/parameters/WatchParams" }],
1017
+ security: [{ bearerAuth: [] }, { queryToken: [] }],
1018
+ responses: {
1019
+ "101": {
1020
+ description: "WebSocket connection accepted. Frames contain JSON Event objects."
1021
+ },
1022
+ "400": { $ref: "#/components/responses/BadRequest" },
1023
+ "401": { $ref: "#/components/responses/Unauthorized" },
1024
+ "403": { $ref: "#/components/responses/Forbidden" }
1025
+ },
1026
+ "x-rstream-event-schema": "#/components/schemas/Event",
1027
+ "x-rstream-required-rules": readEngineStateRules
1028
+ }
1029
+ }
1030
+ }
1031
+ };
1032
+ var engineOpenApiJson = `${JSON.stringify(
1033
+ engineOpenApiDocument,
1034
+ null,
1035
+ 2
1036
+ )}
1037
+ `;
1038
+
652
1039
  // src/resolution.ts
653
1040
  var import_rstream3 = require("@rstreamlabs/rstream");
654
1041
  var import_rstream4 = require("@rstreamlabs/rstream");
@@ -774,16 +1161,16 @@ var import_rstream8 = require("@rstreamlabs/rstream");
774
1161
  var import_rstream9 = require("@rstreamlabs/rstream");
775
1162
  var import_rstream10 = require("@rstreamlabs/rstream");
776
1163
  var import_rstream11 = require("@rstreamlabs/rstream");
777
- var z7 = __toESM(require("zod"));
1164
+ var z8 = __toESM(require("zod"));
778
1165
  var import_crypto = __toESM(require("crypto"));
779
1166
  var defaultTURNPort = 3478;
780
1167
  var defaultTURNSPort = 5349;
781
1168
  var defaultTURNCredentialTTLSeconds = 24 * 60 * 60;
782
- var turnTokenClaimsSchema = z7.object({
783
- token_endpoint: z7.string().optional(),
784
- type: z7.string()
1169
+ var turnTokenClaimsSchema = z8.object({
1170
+ token_endpoint: z8.string().optional(),
1171
+ type: z8.string()
785
1172
  });
786
- var turnCredentialModeSchema = z7.enum(["api", "app", "pat"]);
1173
+ var turnCredentialModeSchema = z8.enum(["api", "app", "pat"]);
787
1174
  function normalizeOptionalString(value) {
788
1175
  const normalized = value?.trim();
789
1176
  return normalized ? normalized : void 0;
@@ -1309,7 +1696,7 @@ var Watch = class {
1309
1696
  };
1310
1697
 
1311
1698
  // src/webtty.ts
1312
- var z8 = __toESM(require("zod"));
1699
+ var z9 = __toESM(require("zod"));
1313
1700
  var osFamilies = [
1314
1701
  "linux",
1315
1702
  "macos",
@@ -1318,25 +1705,25 @@ var osFamilies = [
1318
1705
  "openbsd",
1319
1706
  "freebsd"
1320
1707
  ];
1321
- var webttyServerSchema = z8.object({
1322
- tunnel_id: z8.string(),
1323
- host: z8.string(),
1324
- token_auth: z8.boolean(),
1325
- os_family: z8.enum(osFamilies).optional(),
1326
- arch: z8.string().optional(),
1327
- os_id: z8.string().optional(),
1708
+ var webttyServerSchema = z9.object({
1709
+ tunnel_id: z9.string(),
1710
+ host: z9.string(),
1711
+ token_auth: z9.boolean(),
1712
+ os_family: z9.enum(osFamilies).optional(),
1713
+ arch: z9.string().optional(),
1714
+ os_id: z9.string().optional(),
1328
1715
  // /etc/os-release::ID (ubuntu, debian, rocky, etc.)
1329
- os_version_id: z8.string().optional(),
1716
+ os_version_id: z9.string().optional(),
1330
1717
  // /etc/os-release::VERSION_ID (24.04, 22.04, 11, 10.0.19045, ...)
1331
- os_version_codename: z8.string().optional(),
1718
+ os_version_codename: z9.string().optional(),
1332
1719
  // /etc/os-release::VERSION_CODENAME (jammy, noble...)
1333
- os_pretty_name: z8.string().optional(),
1720
+ os_pretty_name: z9.string().optional(),
1334
1721
  // /etc/os-release::PRETTY_NAME
1335
- kernel_release: z8.string().optional(),
1722
+ kernel_release: z9.string().optional(),
1336
1723
  // uname -r
1337
- hostname: z8.string().optional(),
1724
+ hostname: z9.string().optional(),
1338
1725
  // uname -n
1339
- labels: z8.record(z8.string(), z8.string().optional()).optional()
1726
+ labels: z9.record(z9.string(), z9.string().optional()).optional()
1340
1727
  });
1341
1728
  function parser(tunnel) {
1342
1729
  if (tunnel.status !== "online") return null;
@@ -1355,7 +1742,7 @@ function parser(tunnel) {
1355
1742
  }
1356
1743
  const candidate = {
1357
1744
  tunnel_id: tunnel.id,
1358
- host: tunnel.host,
1745
+ host: formatTunnelHost(tunnel),
1359
1746
  token_auth: tunnel.token_auth === true,
1360
1747
  os_family: tunnelLabels["rstream.webtty.os_family"],
1361
1748
  arch: tunnelLabels["rstream.webtty.arch"],
@@ -1394,6 +1781,7 @@ function parseWebTTYServers(tunnels) {
1394
1781
  authTokenScopesSchema,
1395
1782
  authTokenTunnelGrantSchema,
1396
1783
  authTokenTunnelsScopesSchema,
1784
+ clientFilterSchema,
1397
1785
  clientSchema,
1398
1786
  createAPITURNCredentials,
1399
1787
  createAPPTURNCredentials,
@@ -1402,7 +1790,10 @@ function parseWebTTYServers(tunnels) {
1402
1790
  createAuthTokenResponseSchema,
1403
1791
  createPATTURNCredentials,
1404
1792
  createTURNCredentials,
1793
+ engineOpenApiDocument,
1794
+ engineOpenApiJson,
1405
1795
  eventSchema,
1796
+ formatTunnelHost,
1406
1797
  listClientsParamsSchema,
1407
1798
  listClientsResponseSchema,
1408
1799
  listTunnelsParamsSchema,
@@ -1416,6 +1807,7 @@ function parseWebTTYServers(tunnels) {
1416
1807
  streamSummarySchema,
1417
1808
  streamTLSInfoSchema,
1418
1809
  tokenSchema,
1810
+ tunnelFilterSchema,
1419
1811
  tunnelSchema,
1420
1812
  turnCredentialModeSchema,
1421
1813
  webhookEventsSchema,